inotify -- Linux 2.6 内核中的文件系统变化通知机制

转: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,实际只是进行修改,返回值是不会变的。

转:http://blog.yikuyiku.com/?tag=fanotify

fanotify与inotify的区别

fanotify在2.6.36版本(2010-10-21)并入Linux内核(同时增加了CIFS本地缓存),它与原有的inotify的区别在于以下:

1、inotify最麻烦的一点就是不能监控子目录,要自己去弄n多个监控。fanotify也不能自动监控子目录,但它有一个Global模式,可以监控整个文件系统的所有事件。这样就可以监控所有事件,然后在程序里判断,这样也算是个比之前好一些的解决方案;

2、inotify只能接受事件。fanotify不仅可以接受事件,还可以阻塞事件,甚至进一步阻止事件执行成功。并且可以持续地给这个文件设置上acl,且不用每次触发都判断一次(省去麻烦,增加性能);

3、fanotify允许多个程序同时监控同一个文件系统对象,并且可以设置优先级(消息到达前后)。这个机制可以处理通过fanotify机制本身做出的动态文件;

4、fanotify可以指定不监控某些pid对文件的操作,这是inotify做不到的。使用场景例子:可以让监控程序自己不会触发事件,避免了让程序员自己去处理死循环的麻烦;

5、fanotify相对于inotify的致命缺陷:fanotify可以触发的事件比inotify少,尤其是缺乏MOVE、ATTRIB、CREATE、DELETE事件(有ACCESS、MODIFY、CLOSE)。