转:http://www.ibm.com/developerwork … otifynew/index.html
一、 引言
众所周知,Linux 桌面系统与 MAC 或 Windows 相比有许多不如人意的地方,为了改善这种状况,开源社区提出用户态需要内核提供一些机制,以便用户态能够及时地得知内核或底层硬件设备发生了什么,从而能够更好地管理设备,给用户提供更好的服务,如 hotplug、udev 和 inotify 就是这种需求催生的。Hotplug 是一种内核向用户态应用通报关于热插拔设备一些事件发生的机制,桌面系统能够利用它对设备进行有效的管理,udev 动态地维护 /dev 下的设备文件,inotify 是一种文件系统的变化通知机制,如文件增加、删除等事件可以立刻让用户态得知,该机制是著名的桌面搜索引擎项目 beagle 引入的,并在 Gamin 等项目中被应用。
Inotify 可以监视的文件系统事件包括:
[list]
[]IN_ACCESS,即文件被访问
[]IN_MODIFY,文件被 write
[]IN_ATTRIB,文件属性被修改,如 chmod、chown、touch 等
[]IN_CLOSE_WRITE,可写文件被 close
[]IN_CLOSE_NOWRITE,不可写文件被 close
[]IN_OPEN,文件被 open
[]IN_MOVED_FROM,文件被移走,如 mv
[]IN_MOVED_TO,文件被移来,如 mv、cp
[]IN_CREATE,创建新文件
[]IN_DELETE,文件被删除,如 rm
[]IN_DELETE_SELF,自删除,即一个可执行文件在执行时删除自己
[]IN_MOVE_SELF,自移动,即一个可执行文件在执行时移动自己
[]IN_UNMOUNT,宿主文件系统被 umount
[]IN_CLOSE,文件被关闭,等同于(IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)
[*]IN_MOVE,文件被移动,等同于(IN_MOVED_FROM | IN_MOVED_TO)
[/list] 注:上面所说的文件也包括目录。
二、用户接口
在用户态,inotify 通过三个系统调用和在返回的文件描述符上的文件 I/ 操作来使用,使用 inotify 的第一步是创建 inotify 实例:
[table=98%]
[tr][td] int fd = inotify_init (); [/td][/tr]
[/table]
每一个 inotify 实例对应一个独立的排序的队列。
文件系统的变化事件被称做 watches 的一个对象管理,每一个 watch 是一个二元组(目标,事件掩码),目标可以是文件或目录,事件掩码表示应用希望关注的 inotify 事件,每一个位对应一个 inotify 事件。Watch 对象通过 watch描述符引用,watches 通过文件或目录的路径名来添加。目录 watches 将返回在该目录下的所有文件上面发生的事件。
下面函数用于添加一个 watch:
[table=98%]
[tr][td] int wd = inotify_add_watch (fd, path, mask); [/td][/tr]
[/table]
fd 是 inotify_init() 返回的文件描述符,path 是被监视的目标的路径名(即文件名或目录名),mask 是事件掩码, 在头文件 linux/inotify.h 中定义了每一位代表的事件。可以使用同样的方式来修改事件掩码,即改变希望被通知的inotify 事件。Wd 是 watch 描述符。
下面的函数用于删除一个 watch:
[table=98%]
[tr][td] int ret = inotify_rm_watch (fd, wd); [/td][/tr]
[/table]
fd 是 inotify_init() 返回的文件描述符,wd 是 inotify_add_watch() 返回的 watch 描述符。Ret 是函数的返回值。
文件事件用一个 inotify_event 结构表示,它通过由 inotify_init() 返回的文件描述符使用通常文件读取函数 read 来获得:
struct inotify_event {
__s32 wd; /* watch descriptor */
__u32 mask; /* watch mask */
__u32 cookie; /* cookie to synchronize two events */
__u32 len; /* length (including nulls) of name */
char name[0]; /* stub for possible name */};
结构中的 wd 为被监视目标的 watch 描述符,mask 为事件掩码,len 为 name字符串的长度,name 为被监视目标的路径名,该结构的 name 字段为一个桩,它只是为了用户方面引用文件名,文件名是变长的,它实际紧跟在该结构的后面,文件名将被 0 填充以使下一个事件结构能够 4 字节对齐。注意,len 也把填充字节数统计在内。
通过 read 调用可以一次获得多个事件,只要提供的 buf 足够大。
[table=98%]
[tr][td] size_t len = read (fd, buf, BUF_LEN); [/td][/tr]
[/table]
buf 是一个 inotify_event 结构的数组指针,BUF_LEN 指定要读取的总长度,buf 大小至少要不小于 BUF_LEN,该调用返回的事件数取决于 BUF_LEN 以及事件中文件名的长度。Len 为实际读去的字节数,即获得的事件的总长度。
可以在函数 inotify_init() 返回的文件描述符 fd 上使用 select() 或poll(),也可以在 fd 上使用 ioctl 命令 FIONREAD 来得到当前队列的长度。close(fd)将删除所有添加到 fd 中的 watch 并做必要的清理。
int inotify_init (void);
int inotify_add_watch (int fd, const char *path, __u32 mask);
int inotify_rm_watch (int fd, __u32 mask);
补充: 1.inotify_add_watch返回并不是一个fd,而只是一个标识
2.对于同一个PATH,inotify_add_watch将返回相同的标识。不需要调用close关闭,但需要调用inotify_rm_watch来删除。
在对文件进行读、写、关闭监控时需要注意这个特性。但inotify_init返回的是一个真正的fd,因此需要调用close关闭它。
但是如果相同PATH,但是是add和rm交替进行的,则不会重复,而且是从1递增。
在未rm上一个之前对同一个PATH进行add_watch,实际只是进行修改,返回值是不会变的。