Linux INotif机制详解及实例代码
<p><strong>Linux INotif机制</strong></p>
<p>
<strong>一、 前言:</strong></p>
<p>
众所周知,Linux 桌面系统与 MAC 或 Windows 相比有许多不如人意的地方,为了改善这种状况,开源社区提出用户态需要内核提供一些机制,以便用户态能够及时地得知内核或底层硬件设备发生了什么,从而能够更好地管理设备,给用户提供更好的服务,如 hotplug、udev 和 inotify 就是这种需求催生的。Hotplug 是一种内核向用户态应用通报关于热插拔设备一些事件发生的机制,桌面系统能够利用它对设备进行有效的管理,udev 动态地维护 /dev 下的设备文件,inotify 是一种文件系统的变化通知机制,如文件增加、删除等事件可以立刻让用户态得知,该机制是著名的桌面搜索引擎项目 beagle 引入的,并在 Gamin 等项目中被应用。</p>
<p>
事实上,在 inotify 之前已经存在一种类似的机制叫 dnotify,但是它存在许多缺陷:</p>
<p>
1. 对于想监视的每一个目录,用户都需要打开一个文件描述符,因此如果需要监视的目录较多,将导致打开许多文件描述符,特别是,如果被监视目录在移动介质上(如光盘和 USB 盘),将导致无法 umount 这些文件系统,因为使用 dnotify 的应用打开的文件描述符在使用该文件系统。</p>
<p>
2. dnotify 是基于目录的,它只能得到目录变化事件,当然在目录内的文件的变化会影响到其所在目录从而引发目录变化事件,但是要想通过目录事件来得知哪个文件变化,需要缓存许多 stat 结构的数据。</p>
<p>
3. Dnotify 的接口非常不友好,它使用 signal。</p>
<p>
Inotify 是为替代 dnotify 而设计的,它克服了 dnotify 的缺陷,提供了更好用的,简洁而强大的文件变化通知机制:</p>
<p>
1. Inotify 不需要对被监视的目标打开文件描述符,而且如果被监视目标在可移动介质上,那么在 umount 该介质上的文件系统后,被监视目标对应的 watch 将被自动删除,并且会产生一个 umount 事件。</p>
<p>
2. Inotify 既可以监视文件,也可以监视目录。</p>
<p>
3. Inotify 使用系统调用而非 SIGIO 来通知文件系统事件。</p>
<p>
4. Inotify 使用文件描述符作为接口,因而可以使用通常的文件 I/O 操作select 和 poll 来监视文件系统的变化。</p>
<p>
Inotify 可以监视的文件系统事件包括:</p>
<p>
IN_ACCESS,即文件被访问<br>
IN_MODIFY,文件被 write<br>
IN_ATTRIB,文件属性被修改,如 chmod、chown、touch 等<br>
IN_CLOSE_WRITE,可写文件被 close<br>
IN_CLOSE_NOWRITE,不可写文件被 close<br>
IN_OPEN,文件被 open<br>
IN_MOVED_FROM,文件被移走,如 mv<br>
IN_MOVED_TO,文件被移来,如 mv、cp<br>
IN_CREATE,创建新文件<br>
IN_DELETE,文件被删除,如 rm<br>
IN_DELETE_SELF,自删除,即一个可执行文件在执行时删除自己<br>
IN_MOVE_SELF,自移动,即一个可执行文件在执行时移动自己<br>
IN_UNMOUNT,宿主文件系统被 umount<br>
IN_CLOSE,文件被关闭,等同于(IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)<br>
IN_MOVE,文件被移动,等同于(IN_MOVED_FROM | IN_MOVED_TO)</p>
<p>
注:上面所说的文件也包括目录。</p>
<p>
<strong>二、用户接口</strong></p>
<p>
在用户态,inotify 通过三个系统调用和在返回的文件描述符上的文件 I/ 操作来使用,使用 inotify 的第一步是创建 inotify 实例:</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighteras3" id="highlighter_99542">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="as3 keyword">int</code> <code class="as3 plain">fd = inotify_init ();</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
每一个 inotify 实例对应一个独立的排序的队列。</p>
<p>
文件系统的变化事件被称做 watches 的一个对象管理,每一个 watch 是一个二元组(目标,事件掩码),目标可以是文件或目录,事件掩码表示应用希望关注的 inotify 事件,每一个位对应一个 inotify 事件。Watch 对象通过 watch描述符引用,watches 通过文件或目录的路径名来添加。目录 watches 将返回在该目录下的所有文件上面发生的事件。</p>
<p>
下面函数用于添加一个 watch:</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_619017">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">int wd = inotify_add_watch (fd, path, mask); </code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
fd 是 inotify_init() 返回的文件描述符,path 是被监视的目标的路径名(即文件名或目录名),mask 是事件掩码, 在头文件 linux/inotify.h 中定义了每一位代表的事件。可以使用同样的方式来修改事件掩码,即改变希望被通知的inotify 事件。Wd 是 watch 描述符。<br>
下面的函数用于删除一个 watch:</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_861499">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">int ret = inotify_rm_watch (fd, wd);</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
fd 是 inotify_init() 返回的文件描述符,wd 是 inotify_add_watch() 返回的 watch 描述符。Ret 是函数的返回值。<br>
文件事件用一个 inotify_event 结构表示,它通过由 inotify_init() 返回的文件描述符使用通常文件读取函数 read 来获得</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_176422">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
<div class="line number4 index3 alt1">
4</div>
<div class="line number5 index4 alt2">
5</div>
<div class="line number6 index5 alt1">
6</div>
<div class="line number7 index6 alt2">
7</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">struct inotify_event { </code>
</div>
<div class="line number2 index1 alt1">
<code class="bash spaces"> </code><code class="bash plain">__s32 wd; /* </code><code class="bash functions">watch</code> <code class="bash plain">descriptor */ </code>
</div>
<div class="line number3 index2 alt2">
<code class="bash spaces"> </code><code class="bash plain">__u32 mask; /* </code><code class="bash functions">watch</code> <code class="bash plain">mask */ </code>
</div>
<div class="line number4 index3 alt1">
<code class="bash spaces"> </code><code class="bash plain">__u32 cookie; /* cookie to synchronize two events */ </code>
</div>
<div class="line number5 index4 alt2">
<code class="bash spaces"> </code><code class="bash plain">__u32 len; /* length (including nulls) of name */ </code>
</div>
<div class="line number6 index5 alt1">
<code class="bash spaces"> </code><code class="bash plain">char name; /* stub </code><code class="bash keyword">for</code> <code class="bash plain">possible name */ </code>
</div>
<div class="line number7 index6 alt2">
<code class="bash plain">}; </code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
结构中的 wd 为被监视目标的 watch 描述符,mask 为事件掩码,len 为 name字符串的长度,name 为被监视目标的路径名,该结构的 name 字段为一个桩,它只是为了用户方面引用文件名,文件名是变长的,它实际紧跟在该结构的后面,文件名将被 0 填充以使下一个事件结构能够 4 字节对齐。注意,len 也把填充字节数统计在内。<br>
通过 read 调用可以一次获得多个事件,只要提供的 buf 足够大。</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_928072">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">size_t len = </code><code class="bash functions">read</code> <code class="bash plain">(fd, buf, BUF_LEN); </code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
buf 是一个 inotify_event 结构的数组指针,BUF_LEN 指定要读取的总长度,buf 大小至少要不小于 BUF_LEN,该调用返回的事件数取决于 BUF_LEN 以及事件中文件名的长度。Len 为实际读去的字节数,即获得的事件的总长度。<br>
可以在函数 inotify_init() 返回的文件描述符 fd 上使用 select() 或poll(), 也可以在 fd 上使用 ioctl 命令 FIONREAD 来得到当前队列的长度。close(fd)将删除所有添加到 fd 中的 watch 并做必要的清理。</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_7839">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash spaces"> </code><code class="bash plain">int inotify_init (void); </code>
</div>
<div class="line number2 index1 alt1">
<code class="bash plain">int inotify_add_watch (int fd, const char *path, __u32 mask); </code>
</div>
<div class="line number3 index2 alt2">
<code class="bash plain">int inotify_rm_watch (int fd, __u32 mask);</code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
<strong>三、内核实现原理</strong></p>
<p>
在内核中,每一个 inotify 实例对应一个 inotify_device 结构:</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_387311">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
<div class="line number4 index3 alt1">
4</div>
<div class="line number5 index4 alt2">
5</div>
<div class="line number6 index5 alt1">
6</div>
<div class="line number7 index6 alt2">
7</div>
<div class="line number8 index7 alt1">
8</div>
<div class="line number9 index8 alt2">
9</div>
<div class="line number10 index9 alt1">
10</div>
<div class="line number11 index10 alt2">
11</div>
<div class="line number12 index11 alt1">
12</div>
<div class="line number13 index12 alt2">
13</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">struct inotify_device { </code>
</div>
<div class="line number2 index1 alt1">
<code class="bash spaces"> </code><code class="bash plain">wait_queue_head_t wq; /* wait queue </code><code class="bash keyword">for</code> <code class="bash plain">i</code><code class="bash plain">/o</code> <code class="bash plain">*/ </code>
</div>
<div class="line number3 index2 alt2">
<code class="bash spaces"> </code><code class="bash plain">struct idr idr; /* idr mapping wd -> </code><code class="bash functions">watch</code> <code class="bash plain">*/ </code>
</div>
<div class="line number4 index3 alt1">
<code class="bash spaces"> </code><code class="bash plain">struct semaphore sem; /* protects this bad boy */ </code>
</div>
<div class="line number5 index4 alt2">
<code class="bash spaces"> </code><code class="bash plain">struct list_head events; /* list of queued events */ </code>
</div>
<div class="line number6 index5 alt1">
<code class="bash spaces"> </code><code class="bash plain">struct list_head watches; /* list of watches */ </code>
</div>
<div class="line number7 index6 alt2">
<code class="bash spaces"> </code><code class="bash plain">atomic_t count; /* reference count */ </code>
</div>
<div class="line number8 index7 alt1">
<code class="bash spaces"> </code><code class="bash plain">struct user_struct *user; /* user </code><code class="bash functions">who</code> <code class="bash plain">opened this dev */ </code>
</div>
<div class="line number9 index8 alt2">
<code class="bash spaces"> </code><code class="bash plain">unsigned int queue_size; /* size of the queue (bytes) */ </code>
</div>
<div class="line number10 index9 alt1">
<code class="bash spaces"> </code><code class="bash plain">unsigned int event_count; /* number of pending events */ </code>
</div>
<div class="line number11 index10 alt2">
<code class="bash spaces"> </code><code class="bash plain">unsigned int max_events; /* maximum number of events */ </code>
</div>
<div class="line number12 index11 alt1">
<code class="bash spaces"> </code><code class="bash plain">u32 last_wd; /* the last wd allocated */ </code>
</div>
<div class="line number13 index12 alt2">
<code class="bash plain">}; </code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
wq 是等待队列,被 read 调用阻塞的进程将挂在该等待队列上,idr 用于把 watch 描述符映射到对应的 inotify_watch,sem 用于同步对该结构的访问,events 为该 inotify 实例上发生的事件的列表,被该 inotify 实例监视的所有事件在发生后都将插入到这个列表,watches 是给 inotify 实例监视的 watch 列表,inotify_add_watch 将把新添加的 watch 插入到该列表,count 是引用计数,user 用于描述创建该 inotify 实例的用户,queue_size 表示该 inotify 实例的事件队列的字节数,event_count 是 events 列表的事件数,max_events 为最大允许的事件数,last_wd 是上次分配的 watch 描述符。</p>
<p>
每一个 watch 对应一个 inotify_watch 结构:</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_136769">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
<div class="line number4 index3 alt1">
4</div>
<div class="line number5 index4 alt2">
5</div>
<div class="line number6 index5 alt1">
6</div>
<div class="line number7 index6 alt2">
7</div>
<div class="line number8 index7 alt1">
8</div>
<div class="line number9 index8 alt2">
9</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">struct inotify_watch { </code>
</div>
<div class="line number2 index1 alt1">
<code class="bash spaces"> </code><code class="bash plain">struct list_head d_list; /* entry </code><code class="bash keyword">in</code> <code class="bash plain">inotify_device's list */ </code>
</div>
<div class="line number3 index2 alt2">
<code class="bash spaces"> </code><code class="bash plain">struct list_head i_list; /* entry </code><code class="bash keyword">in</code> <code class="bash plain">inode's list */ </code>
</div>
<div class="line number4 index3 alt1">
<code class="bash spaces"> </code><code class="bash plain">atomic_t count; /* reference count */ </code>
</div>
<div class="line number5 index4 alt2">
<code class="bash spaces"> </code><code class="bash plain">struct inotify_device *dev; /* associated device */ </code>
</div>
<div class="line number6 index5 alt1">
<code class="bash spaces"> </code><code class="bash plain">struct inode *inode; /* associated inode */ </code>
</div>
<div class="line number7 index6 alt2">
<code class="bash spaces"> </code><code class="bash plain">s32 wd; /* </code><code class="bash functions">watch</code> <code class="bash plain">descriptor */ </code>
</div>
<div class="line number8 index7 alt1">
<code class="bash spaces"> </code><code class="bash plain">u32 mask; /* event mask </code><code class="bash keyword">for</code> <code class="bash plain">this </code><code class="bash functions">watch</code> <code class="bash plain">*/ </code>
</div>
<div class="line number9 index8 alt2">
<code class="bash plain">}; </code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
d_list 指向所有 inotify_device 组成的列表的,i_list 指向所有被监视 inode 组成的列表,count 是引用计数,dev 指向该 watch 所在的 inotify 实例对应的 inotify_device 结构,inode 指向该 watch 要监视的 inode,wd 是分配给该 watch 的描述符,mask 是该 watch 的事件掩码,表示它对哪些文件系统事件感兴趣。</p>
<p>
结构 inotify_device 在用户态调用 inotify_init() 时创建,当关闭 inotify_init()返回的文件描述符时将被释放。结构 inotify_watch 在用户态调用 inotify_add_watch()时创建,在用户态调用 inotify_rm_watch() 或 close(fd) 时被释放。<br>
无论是目录还是文件,在内核中都对应一个 inode 结构,inotify 系统在 inode 结构中增加了两个字段:</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_704221">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
<div class="line number4 index3 alt1">
4</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash comments">#ifdef CONFIG_INOTIFY </code>
</div>
<div class="line number2 index1 alt1">
<code class="bash spaces"> </code><code class="bash plain">struct list_head inotify_watches; /* watches on this inode */ </code>
</div>
<div class="line number3 index2 alt2">
<code class="bash spaces"> </code><code class="bash plain">struct semaphore inotify_sem; /* protects the watches list */ </code>
</div>
<div class="line number4 index3 alt1">
<code class="bash comments">#endif </code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
inotify_watches 是在被监视目标上的 watch 列表,每当用户调用 inotify_add_watch()时,内核就为添加的 watch 创建一个 inotify_watch 结构,并把它插入到被监视目标对应的 inode 的 inotify_watches 列表。inotify_sem 用于同步对 inotify_watches 列表的访问。当文件系统发生第一部分提到的事件之一时,相应的文件系统代码将显示调用fsnotify_* 来把相应的事件报告给</p>
<p>
inotify 系统,其中*号就是相应的事件名,目前实现包括:</p>
<p>
fsnotify_move,文件从一个目录移动到另一个目录</p>
<p>
fsnotify_nameremove,文件从目录中删除<br>
fsnotify_inoderemove,自删除<br>
fsnotify_create,创建新文件<br>
fsnotify_mkdir,创建新目录<br>
fsnotify_access,文件被读<br>
fsnotify_modify,文件被写<br>
fsnotify_open,文件被打开<br>
fsnotify_close,文件被关闭<br>
fsnotify_xattr,文件的扩展属性被修改<br>
fsnotify_change,文件被修改或原数据被修改</p>
<p>
有一个例外情况,就是 inotify_unmount_inodes,它会在文件系统被 umount 时调用来通知 umount 事件给 inotify 系统。<br>
以上提到的通知函数最后都调用 inotify_inode_queue_event(inotify_unmount_inodes直接调用 inotify_dev_queue_event ),该函数首先判断对应的inode是否被监视,这通过查看 inotify_watches 列表是否为空来实现,如果发现 inode 没有被监视,什么也不做,立刻返回,反之,遍历 inotify_watches 列表,看是否当前的文件操作事件被某个 watch 监视,如果是,调用 inotify_dev_queue_event,否则,返回。函数inotify_dev_queue_event 首先判断该事件是否是上一个事件的重复,如果是就丢弃该事件并返回,否则,它判断是否 inotify 实例即 inotify_device 的事件队列是否溢出,如果溢出,产生一个溢出事件,否则产生一个当前的文件操作事件,这些事件通过kernel_event 构建,kernel_event 将创建一个 inotify_kernel_event 结构,然后把该结构插入到对应的 inotify_device 的 events 事件列表,然后唤醒等待在inotify_device 结构中的 wq 指向的等待队列。想监视文件系统事件的用户态进程在inotify 实例(即 inotify_init() 返回的文件描述符)上调用 read 时但没有事件时就挂在等待队列 wq 上。</p>
<p>
<strong>四、使用示例</strong></p>
<p>
下面是一个使用 inotify 来监视文件系统事件的例子:</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_180269">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
<div class="line number4 index3 alt1">
4</div>
<div class="line number5 index4 alt2">
5</div>
<div class="line number6 index5 alt1">
6</div>
<div class="line number7 index6 alt2">
7</div>
<div class="line number8 index7 alt1">
8</div>
<div class="line number9 index8 alt2">
9</div>
<div class="line number10 index9 alt1">
10</div>
<div class="line number11 index10 alt2">
11</div>
<div class="line number12 index11 alt1">
12</div>
<div class="line number13 index12 alt2">
13</div>
<div class="line number14 index13 alt1">
14</div>
<div class="line number15 index14 alt2">
15</div>
<div class="line number16 index15 alt1">
16</div>
<div class="line number17 index16 alt2">
17</div>
<div class="line number18 index17 alt1">
18</div>
<div class="line number19 index18 alt2">
19</div>
<div class="line number20 index19 alt1">
20</div>
<div class="line number21 index20 alt2">
21</div>
<div class="line number22 index21 alt1">
22</div>
<div class="line number23 index22 alt2">
23</div>
<div class="line number24 index23 alt1">
24</div>
<div class="line number25 index24 alt2">
25</div>
<div class="line number26 index25 alt1">
26</div>
<div class="line number27 index26 alt2">
27</div>
<div class="line number28 index27 alt1">
28</div>
<div class="line number29 index28 alt2">
29</div>
<div class="line number30 index29 alt1">
30</div>
<div class="line number31 index30 alt2">
31</div>
<div class="line number32 index31 alt1">
32</div>
<div class="line number33 index32 alt2">
33</div>
<div class="line number34 index33 alt1">
34</div>
<div class="line number35 index34 alt2">
35</div>
<div class="line number36 index35 alt1">
36</div>
<div class="line number37 index36 alt2">
37</div>
<div class="line number38 index37 alt1">
38</div>
<div class="line number39 index38 alt2">
39</div>
<div class="line number40 index39 alt1">
40</div>
<div class="line number41 index40 alt2">
41</div>
<div class="line number42 index41 alt1">
42</div>
<div class="line number43 index42 alt2">
43</div>
<div class="line number44 index43 alt1">
44</div>
<div class="line number45 index44 alt2">
45</div>
<div class="line number46 index45 alt1">
46</div>
<div class="line number47 index46 alt2">
47</div>
<div class="line number48 index47 alt1">
48</div>
<div class="line number49 index48 alt2">
49</div>
<div class="line number50 index49 alt1">
50</div>
<div class="line number51 index50 alt2">
51</div>
<div class="line number52 index51 alt1">
52</div>
<div class="line number53 index52 alt2">
53</div>
<div class="line number54 index53 alt1">
54</div>
<div class="line number55 index54 alt2">
55</div>
<div class="line number56 index55 alt1">
56</div>
<div class="line number57 index56 alt2">
57</div>
<div class="line number58 index57 alt1">
58</div>
<div class="line number59 index58 alt2">
59</div>
<div class="line number60 index59 alt1">
60</div>
<div class="line number61 index60 alt2">
61</div>
<div class="line number62 index61 alt1">
62</div>
<div class="line number63 index62 alt2">
63</div>
<div class="line number64 index63 alt1">
64</div>
<div class="line number65 index64 alt2">
65</div>
<div class="line number66 index65 alt1">
66</div>
<div class="line number67 index66 alt2">
67</div>
<div class="line number68 index67 alt1">
68</div>
<div class="line number69 index68 alt2">
69</div>
<div class="line number70 index69 alt1">
70</div>
<div class="line number71 index70 alt2">
71</div>
<div class="line number72 index71 alt1">
72</div>
<div class="line number73 index72 alt2">
73</div>
<div class="line number74 index73 alt1">
74</div>
<div class="line number75 index74 alt2">
75</div>
<div class="line number76 index75 alt1">
76</div>
<div class="line number77 index76 alt2">
77</div>
<div class="line number78 index77 alt1">
78</div>
<div class="line number79 index78 alt2">
79</div>
<div class="line number80 index79 alt1">
80</div>
<div class="line number81 index80 alt2">
81</div>
<div class="line number82 index81 alt1">
82</div>
<div class="line number83 index82 alt2">
83</div>
<div class="line number84 index83 alt1">
84</div>
<div class="line number85 index84 alt2">
85</div>
<div class="line number86 index85 alt1">
86</div>
<div class="line number87 index86 alt2">
87</div>
<div class="line number88 index87 alt1">
88</div>
<div class="line number89 index88 alt2">
89</div>
<div class="line number90 index89 alt1">
90</div>
<div class="line number91 index90 alt2">
91</div>
<div class="line number92 index91 alt1">
92</div>
<div class="line number93 index92 alt2">
93</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash comments">#include <linux/unistd.h> </code>
</div>
<div class="line number2 index1 alt1">
<code class="bash comments">#include <linux/inotify.h> </code>
</div>
<div class="line number3 index2 alt2">
<code class="bash comments">#include <errno.h> </code>
</div>
<div class="line number4 index3 alt1">
<code class="bash plain">_syscall0(int, inotify_init) </code>
</div>
<div class="line number5 index4 alt2">
<code class="bash plain">_syscall3(int, inotify_add_watch, int, fd, const char *, path, __u32, mask) </code>
</div>
<div class="line number6 index5 alt1">
<code class="bash plain">_syscall2(int, inotify_rm_watch, int, fd, __u32, mask) </code>
</div>
<div class="line number7 index6 alt2">
<code class="bash plain">char * monitored_files[] = { </code>
</div>
<div class="line number8 index7 alt1">
<code class="bash spaces"> </code><code class="bash string">"./tmp_file"</code><code class="bash plain">, </code>
</div>
<div class="line number9 index8 alt2">
<code class="bash spaces"> </code><code class="bash string">"./tmp_dir"</code><code class="bash plain">, </code>
</div>
<div class="line number10 index9 alt1">
<code class="bash spaces"> </code><code class="bash string">"/mnt/sda3/windows_file"</code>
</div>
<div class="line number11 index10 alt2">
<code class="bash plain">}; </code>
</div>
<div class="line number12 index11 alt1">
<code class="bash plain">struct wd_name { </code>
</div>
<div class="line number13 index12 alt2">
<code class="bash spaces"> </code><code class="bash plain">int wd; </code>
</div>
<div class="line number14 index13 alt1">
<code class="bash spaces"> </code><code class="bash plain">char * name; </code>
</div>
<div class="line number15 index14 alt2">
<code class="bash plain">}; </code>
</div>
<div class="line number16 index15 alt1">
<code class="bash comments">#define WD_NUM 3 </code>
</div>
<div class="line number17 index16 alt2">
<code class="bash plain">struct wd_name wd_array; </code>
</div>
<div class="line number18 index17 alt1">
<code class="bash plain">char * event_array[] = { </code>
</div>
<div class="line number19 index18 alt2">
<code class="bash spaces"> </code><code class="bash string">"File was accessed"</code><code class="bash plain">, </code>
</div>
<div class="line number20 index19 alt1">
<code class="bash spaces"> </code><code class="bash string">"File was modified"</code><code class="bash plain">, </code>
</div>
<div class="line number21 index20 alt2">
<code class="bash spaces"> </code><code class="bash string">"File attributes were changed"</code><code class="bash plain">, </code>
</div>
<div class="line number22 index21 alt1">
<code class="bash spaces"> </code><code class="bash string">"writtable file closed"</code><code class="bash plain">, </code>
</div>
<div class="line number23 index22 alt2">
<code class="bash spaces"> </code><code class="bash string">"Unwrittable file closed"</code><code class="bash plain">, </code>
</div>
<div class="line number24 index23 alt1">
<code class="bash spaces"> </code><code class="bash string">"File was opened"</code><code class="bash plain">, </code>
</div>
<div class="line number25 index24 alt2">
<code class="bash spaces"> </code><code class="bash string">"File was moved from X"</code><code class="bash plain">, </code>
</div>
<div class="line number26 index25 alt1">
<code class="bash spaces"> </code><code class="bash string">"File was moved to Y"</code><code class="bash plain">, </code>
</div>
<div class="line number27 index26 alt2">
<code class="bash spaces"> </code><code class="bash string">"Subfile was created"</code><code class="bash plain">, </code>
</div>
<div class="line number28 index27 alt1">
<code class="bash spaces"> </code><code class="bash string">"Subfile was deleted"</code><code class="bash plain">, </code>
</div>
<div class="line number29 index28 alt2">
<code class="bash spaces"> </code><code class="bash string">"Self was deleted"</code><code class="bash plain">, </code>
</div>
<div class="line number30 index29 alt1">
<code class="bash spaces"> </code><code class="bash string">"Self was moved"</code><code class="bash plain">, </code>
</div>
<div class="line number31 index30 alt2">
<code class="bash spaces"> </code><code class="bash string">""</code><code class="bash plain">, </code>
</div>
<div class="line number32 index31 alt1">
<code class="bash spaces"> </code><code class="bash string">"Backing fs was unmounted"</code><code class="bash plain">, </code>
</div>
<div class="line number33 index32 alt2">
<code class="bash spaces"> </code><code class="bash string">"Event queued overflowed"</code><code class="bash plain">, </code>
</div>
<div class="line number34 index33 alt1">
<code class="bash spaces"> </code><code class="bash string">"File was ignored"</code>
</div>
<div class="line number35 index34 alt2">
<code class="bash plain">}; </code>
</div>
<div class="line number36 index35 alt1">
<code class="bash comments">#define EVENT_NUM 16 </code>
</div>
<div class="line number37 index36 alt2">
<code class="bash comments">#define MAX_BUF_SIZE 1024 </code>
</div>
<div class="line number38 index37 alt1">
<code class="bash spaces"> </code>
</div>
<div class="line number39 index38 alt2">
<code class="bash plain">int main(void) </code>
</div>
<div class="line number40 index39 alt1">
<code class="bash plain">{ </code>
</div>
<div class="line number41 index40 alt2">
<code class="bash spaces"> </code><code class="bash plain">int fd; </code>
</div>
<div class="line number42 index41 alt1">
<code class="bash spaces"> </code><code class="bash plain">int wd; </code>
</div>
<div class="line number43 index42 alt2">
<code class="bash spaces"> </code><code class="bash plain">char buffer; </code>
</div>
<div class="line number44 index43 alt1">
<code class="bash spaces"> </code><code class="bash plain">char * offset = NULL; </code>
</div>
<div class="line number45 index44 alt2">
<code class="bash spaces"> </code><code class="bash plain">struct inotify_event * event; </code>
</div>
<div class="line number46 index45 alt1">
<code class="bash spaces"> </code><code class="bash plain">int len, tmp_len; </code>
</div>
<div class="line number47 index46 alt2">
<code class="bash spaces"> </code><code class="bash plain">char strbuf; </code>
</div>
<div class="line number48 index47 alt1">
<code class="bash spaces"> </code><code class="bash plain">int i = 0; </code>
</div>
<div class="line number49 index48 alt2">
<code class="bash spaces"> </code>
</div>
<div class="line number50 index49 alt1">
<code class="bash spaces"> </code><code class="bash plain">fd = inotify_init(); </code>
</div>
<div class="line number51 index50 alt2">
<code class="bash spaces"> </code><code class="bash keyword">if</code> <code class="bash plain">(fd < 0) { </code>
</div>
<div class="line number52 index51 alt1">
<code class="bash spaces"> </code><code class="bash functions">printf</code><code class="bash plain">(</code><code class="bash string">"Fail to initialize inotify.\n"</code><code class="bash plain">); </code>
</div>
<div class="line number53 index52 alt2">
<code class="bash spaces"> </code><code class="bash functions">exit</code><code class="bash plain">(-1); </code>
</div>
<div class="line number54 index53 alt1">
<code class="bash spaces"> </code><code class="bash plain">} </code>
</div>
<div class="line number55 index54 alt2">
<code class="bash spaces"> </code><code class="bash keyword">for</code> <code class="bash plain">(i=0; i<WD_NUM; i++) { </code>
</div>
<div class="line number56 index55 alt1">
<code class="bash spaces"> </code><code class="bash plain">wd_array.name = monitored_files; </code>
</div>
<div class="line number57 index56 alt2">
<code class="bash spaces"> </code><code class="bash plain">wd = inotify_add_watch(fd, wd_array.name, IN_ALL_EVENTS); </code>
</div>
<div class="line number58 index57 alt1">
<code class="bash spaces"> </code><code class="bash keyword">if</code> <code class="bash plain">(wd < 0) { </code>
</div>
<div class="line number59 index58 alt2">
<code class="bash spaces"> </code><code class="bash functions">printf</code><code class="bash plain">(</code><code class="bash string">"Can't add watch for %s.\n"</code><code class="bash plain">, wd_array.name); </code>
</div>
<div class="line number60 index59 alt1">
<code class="bash spaces"> </code><code class="bash functions">exit</code><code class="bash plain">(-1); </code>
</div>
<div class="line number61 index60 alt2">
<code class="bash spaces"> </code><code class="bash plain">} </code>
</div>
<div class="line number62 index61 alt1">
<code class="bash spaces"> </code><code class="bash plain">wd_array.wd = wd; </code>
</div>
<div class="line number63 index62 alt2">
<code class="bash spaces"> </code><code class="bash plain">} </code>
</div>
<div class="line number64 index63 alt1">
<code class="bash spaces"> </code><code class="bash keyword">while</code><code class="bash plain">(len = </code><code class="bash functions">read</code><code class="bash plain">(fd, buffer, MAX_BUF_SIZE)) { </code>
</div>
<div class="line number65 index64 alt2">
<code class="bash spaces"> </code><code class="bash plain">offset = buffer; </code>
</div>
<div class="line number66 index65 alt1">
<code class="bash spaces"> </code><code class="bash functions">printf</code><code class="bash plain">(</code><code class="bash string">"Some event happens, len = %d.\n"</code><code class="bash plain">, len); </code>
</div>
<div class="line number67 index66 alt2">
<code class="bash spaces"> </code><code class="bash plain">event = (struct inotify_event *)buffer; </code>
</div>
<div class="line number68 index67 alt1">
<code class="bash spaces"> </code><code class="bash keyword">while</code> <code class="bash plain">(((char *)event - buffer) < len) { </code>
</div>
<div class="line number69 index68 alt2">
<code class="bash spaces"> </code><code class="bash keyword">if</code> <code class="bash plain">(event->mask & IN_ISDIR) { </code>
</div>
<div class="line number70 index69 alt1">
<code class="bash spaces"> </code><code class="bash plain">memcpy(strbuf, </code><code class="bash string">"Direcotory"</code><code class="bash plain">, 11); </code>
</div>
<div class="line number71 index70 alt2">
<code class="bash spaces"> </code><code class="bash plain">} </code>
</div>
<div class="line number72 index71 alt1">
<code class="bash spaces"> </code><code class="bash keyword">else</code> <code class="bash plain">{ </code>
</div>
<div class="line number73 index72 alt2">
<code class="bash spaces"> </code><code class="bash plain">memcpy(strbuf, </code><code class="bash string">"File"</code><code class="bash plain">, 5); </code>
</div>
<div class="line number74 index73 alt1">
<code class="bash spaces"> </code><code class="bash plain">} </code>
</div>
<div class="line number75 index74 alt2">
<code class="bash spaces"> </code><code class="bash functions">printf</code><code class="bash plain">(</code><code class="bash string">"Object type: %s\n"</code><code class="bash plain">, strbuf); </code>
</div>
<div class="line number76 index75 alt1">
<code class="bash spaces"> </code><code class="bash keyword">for</code> <code class="bash plain">(i=0; i<WD_NUM; i++) { </code>
</div>
<div class="line number77 index76 alt2">
<code class="bash spaces"> </code><code class="bash keyword">if</code> <code class="bash plain">(event->wd != wd_array.wd) </code><code class="bash keyword">continue</code><code class="bash plain">; </code>
</div>
<div class="line number78 index77 alt1">
<code class="bash spaces"> </code><code class="bash functions">printf</code><code class="bash plain">(</code><code class="bash string">"Object name: %s\n"</code><code class="bash plain">, wd_array.name); </code>
</div>
<div class="line number79 index78 alt2">
<code class="bash spaces"> </code><code class="bash keyword">break</code><code class="bash plain">; </code>
</div>
<div class="line number80 index79 alt1">
<code class="bash spaces"> </code><code class="bash plain">} </code>
</div>
<div class="line number81 index80 alt2">
<code class="bash spaces"> </code><code class="bash functions">printf</code><code class="bash plain">(</code><code class="bash string">"Event mask: %08X\n"</code><code class="bash plain">, event->mask); </code>
</div>
<div class="line number82 index81 alt1">
<code class="bash spaces"> </code><code class="bash keyword">for</code> <code class="bash plain">(i=0; i<EVENT_NUM; i++) { </code>
</div>
<div class="line number83 index82 alt2">
<code class="bash spaces"> </code><code class="bash keyword">if</code> <code class="bash plain">(event_array == </code><code class="bash string">'\0'</code><code class="bash plain">) </code><code class="bash keyword">continue</code><code class="bash plain">; </code>
</div>
<div class="line number84 index83 alt1">
<code class="bash spaces"> </code><code class="bash keyword">if</code> <code class="bash plain">(event->mask & (1<<i)) { </code>
</div>
<div class="line number85 index84 alt2">
<code class="bash spaces"> </code><code class="bash functions">printf</code><code class="bash plain">(</code><code class="bash string">"Event: %s\n"</code><code class="bash plain">, event_array); </code>
</div>
<div class="line number86 index85 alt1">
<code class="bash spaces"> </code><code class="bash plain">} </code>
</div>
<div class="line number87 index86 alt2">
<code class="bash spaces"> </code><code class="bash plain">} </code>
</div>
<div class="line number88 index87 alt1">
<code class="bash spaces"> </code><code class="bash plain">tmp_len = sizeof(struct inotify_event) + event->len; </code>
</div>
<div class="line number89 index88 alt2">
<code class="bash spaces"> </code><code class="bash plain">event = (struct inotify_event *)(offset + tmp_len); </code>
</div>
<div class="line number90 index89 alt1">
<code class="bash spaces"> </code><code class="bash plain">offset += tmp_len; </code>
</div>
<div class="line number91 index90 alt2">
<code class="bash spaces"> </code><code class="bash plain">} </code>
</div>
<div class="line number92 index91 alt1">
<code class="bash spaces"> </code><code class="bash plain">} </code>
</div>
<div class="line number93 index92 alt2">
<code class="bash plain">} </code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
该程序将监视发生在当前目录下的文件 tmp_file 与当前目录下的目录 tmp_dir 上的所有文件系统事件, 同时它也将监视发生在文件 /mnt/sda3/windows_file 上的文件系统事件,注意,/mnt/sda3 是 SATA 硬盘分区 3 的挂接点。</p>
<p>
细心的读者可能注意到,该程序首部使用 _syscallN 来声明 inotify 系统调用,原因是这些系统调用是在最新的稳定内核 2.6.13 中引入的,glibc 并没有实现这些系统调用的库函数版本,因此,为了能在程序中使用这些系统调用,必须通过 _syscallN 来声明这些新的系统,其中的 N 为要声明的系统调用实际的参数数。还有需要注意的地方是系统的头文件必须与被启动的内核匹配,为了让上面的程序能够成功编译,必须让 2.6.13 的内核头文件(包括 include/linux/*, include/asm/* 和 include/asm-generic/*)在头文件搜索路径内,并且是第一优先搜索的头文件路径,因为 _syscallN 需要用到这些头文件中的 linux/unistd.h 和 asm/unistd.h,它们包含了 inotify 的三个系统调用的系统调用号 __NR_inotify_init、__NR_inotify_add_watch 和 __NR_inotify_rm_watch。</p>
<p>
因此,要想成功编译此程序,只要把用户编译好的内核的头文件拷贝到该程序所在的路径,并使用如下命令编译即可:</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_236272">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">$gcc -o inotify_example -I. inotify_example.c </code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
注意:当前目录下应当包含 linux、asm 和 asm-generic 三个已编译好的 2.6.13 内核的有文件目录,asm 是一个链接,因此拷贝 asm 头文件的时候需要拷贝 asm 与 asm-ARCH(对于 x86 平台应当是 asm-i386)。 然后,为了运行该程序,需要在当前目录下创建文件 tmp_file 和目录 tmp_dir,对于/mnt/sda3/windows_file 文件,用户需要依自己的实际情况而定,可能是/mnt/dosc/windows_file,即 /mnt/dosc 是一个 FAT32 的 windows 硬盘,因此用户在编译该程序时需要根据自己的实际情况来修改 /mnt/sda3。Windows_file 是在被 mount 硬盘上创建的一个文件,为了运行该程序,它必须被创建。<br>
以下是作者在 redhat 9.0 上运行此程序得到的一些结果:</p>
<p>
当运行此程序的时候在另一个虚拟终端执行 cat ./tmp_file,此程序的输出为:</p>
<div class="jb51code">
<div>
<div class="syntaxhighlighterbash" id="highlighter_953399">
<div class="toolbar">
<span>?</span>
</div>
<table border="0" cellpadding="0" cellspacing="0"><tbody><tr>
<td class="gutter">
<div class="line number1 index0 alt2">
1</div>
<div class="line number2 index1 alt1">
2</div>
<div class="line number3 index2 alt2">
3</div>
<div class="line number4 index3 alt1">
4</div>
<div class="line number5 index4 alt2">
5</div>
<div class="line number6 index5 alt1">
6</div>
<div class="line number7 index6 alt2">
7</div>
<div class="line number8 index7 alt1">
8</div>
<div class="line number9 index8 alt2">
9</div>
<div class="line number10 index9 alt1">
10</div>
<div class="line number11 index10 alt2">
11</div>
<div class="line number12 index11 alt1">
12</div>
<div class="line number13 index12 alt2">
13</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2">
<code class="bash plain">Some event happens, len = 48. </code>
</div>
<div class="line number2 index1 alt1">
<code class="bash plain">Object </code><code class="bash functions">type</code><code class="bash plain">: File </code>
</div>
<div class="line number3 index2 alt2">
<code class="bash plain">Object name: .</code><code class="bash plain">/tmp_file</code>
</div>
<div class="line number4 index3 alt1">
<code class="bash plain">Event mask: 00000020 </code>
</div>
<div class="line number5 index4 alt2">
<code class="bash plain">Event: File was opened </code>
</div>
<div class="line number6 index5 alt1">
<code class="bash plain">Object </code><code class="bash functions">type</code><code class="bash plain">: File </code>
</div>
<div class="line number7 index6 alt2">
<code class="bash plain">Object name: .</code><code class="bash plain">/tmp_file</code>
</div>
<div class="line number8 index7 alt1">
<code class="bash plain">Event mask: 00000001 </code>
</div>
<div class="line number9 index8 alt2">
<code class="bash plain">Event: File was accessed </code>
</div>
<div class="line number10 index9 alt1">
<code class="bash plain">Object </code><code class="bash functions">type</code><code class="bash plain">: File </code>
</div>
<div class="line number11 index10 alt2">
<code class="bash plain">Object name: .</code><code class="bash plain">/tmp_file</code>
</div>
<div class="line number12 index11 alt1">
<code class="bash plain">Event mask: 00000010 </code>
</div>
<div class="line number13 index12 alt2">
<code class="bash plain">Event: Unwrittable </code><code class="bash functions">file</code> <code class="bash plain">closed </code>
</div>
</div>
</td>
</tr></tbody></table>
</div>
</div>
<div class="codetool" id="codetool">
<div class="code_n">
<textarea></textarea>
</div>
</div>
</div>
<p>
以上事件清楚地说明了 cat 指令执行了文件 open 和 close 操作,当然 open 和 close操作都属于 access 操作,任何对文件的操作都是 access 操作。</p>
<p>
此外,运行 vi ./tmp_file,发现 vi实际在编辑文件时复制了一个副本,在未保存之前是对副本进行操作。 运行 vi ./tmp_file, 修改并保存退出时,发现 vi 实际在保存修改时删除了最初的文件并把那个副本文件名更改为最初的文件的名称。注意,事件"File was ignored"表示系统把该文件对应的 watch 从 inotify 实例的 watch 列表中删除,因为文件已经被删除。 读者可以自己分别执行命令:echo "abc" > ./tmp_file 、rm -f tmp_file、 ls tmp_dir、 cd tmp_dir;touch c.txt、 rm c.txt 、 umount /mnt/sda3(实际用户需要使用自己当时的 mount 点路径名),然后分析一下结果。Umount 触发两个事件,一个表示文件已经被删除或不在存在,另一个表示该文件的 watch被从 watch 列表中删除。</p>
<p>
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!</p>
頁:
[1]