1.inotify是什么?
inotify机制用于监控文件系统,通过它可以监控一个或多个文件,如果该文件发生了指定事件,比如打开,读或写等,该机制会异步的响应用程序发出通知(或称为警告),应用程序根据文件系统发生的事件类型做出相应的反应。
2.inotify可以监控的事件
inotify使用一组宏来表示文件可以被监控的事件,这些宏在稍候介绍的inotify_add_watch()中使用。在没有特别说明的情况下,下面解释中的文件均指被监控的文件,并且即可以是普通文件又可以是目录文件。
IN_ACCESS:文件被访问,如果是目录文件,则指目录中的文件名被访问。
IN_MODIFY:文件被修改,如果是目录文件,则指目录中的文件名被修改。
IN_ATTRIB:文件属性被修改,比如使用chmod命令。
IN_CLOSE_WRITE:可写的文件被关闭。
IN_CLOSE_NOWRITE:不可写文件被关闭。
IN_CLOSE:文件被关闭,它等同于(IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)的效果。
IN_OPEN:文件被打开。
IN_MOVED_FROM:文件被移出监控区,比如使用mv命令将一个文件移出监控目录。
IN_MOVED_TO:文件(这个文件既可以是受监控的又可以是未受监控的)被移入监控区,比如使用mv和cp命令。
IN_MOVE:文件被移动,它等同于(IN_MOVED_FROM | IN_MOVED_TO)的作用效果。
IN_CREATE:在目录中创建一个新文件,比如touch或mkdir命令。
IN_DELETE:文件被删除,比如使用rm命令。
IN_DELETE_SELF:自删除,即一个可执行文件在执行时删除自己。
IN_MOVE_SELF:自移动,即一个可执行文件在执行时移动自己。
IN_UNMOUNT:宿主文件系统被 umount。
另外,IN_ISDIR宏用来判断被监控的文件是否为目录文件,该宏可以在应用程序对监控文件作监控处理时应用。
3.inotify用户态使用概述
inotify机制属于Linux在2.6.13之后增加的一个新特性,它属于dnotify机制的升级版。要使用inotify机制监控文件系统,那么必须先创建一个inotify的实例。由于Linux中的一切皆为文件,可以将inotify实例理解为一个“inotify类型的文件”,因此该实例会对应一个文件描述符,这也属于inotify优于dnotify的一大特性。
inotify机制的另一大特性即为监控程序对文件的监控不必轮询去查看,一旦监控的文件有指定的事件发生,它会异步通知监控程序,监控程序收到警告后会立马做出相应的响应。而在没有发生监控事件的时候,监控程序则一直处于阻塞状态。
这里的阻塞通过read()即可完成。当没有监控时间发生时,inotify实例中没有数据则read()阻塞;当有监控事件发生时,监控事件将被写入inotify实例中,此时read函数被唤醒读取该事件,监控程序根据读取的数据做出相应处理。这里的事件其实是通过字节流发送到inotify实例中的,因此可以通过read()函数来读取。为此,专门有一个数据结构来存储监控事件,即为struct inotify_event:
struct inotify_event
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 */
};
该结构的定义位于用户态文件目录include/linux/inotify.h中,每个字段代表的含义如下:
wd:一个监视器(watch)的描述符,所谓监视器就是一个二元组(监视文件,事件掩码),其中事件掩码包含该文件被监视的所有事件。wd是通过inotify_add_watch()返回的,wd在此结构中与一个监视事件关联,即说明wd监视器上发生了当前inotify_event这个事件。
mask:该事件的类型即为当前结构中的mask,它是wd中所指定mask的一个子集。
len:表示当前结构中name的长度,但有时候name为了字节对齐会填充若干个0,因此len会大于等于name的长度。
name:表示监控文件的路径,这里通过GNU C中的0长度数组来表示变长的文件路径。
对inotify机制的典型使用方法如下:
1.创建并初始化一个inotify的实例,通过inotify_init()即可实现,该函数返回一个文件描述符。
2.添加一个或多个监控文件,即监视器,通过inotify_add_watch()即可实现,该函数就返回一个监视器的文件描述符。
3.循环等待监控事件的发生,通过循环read()inotify实例的fd即可实现。
4.如果有监控事件发生,则将fd中的字节流读取到inotify_event结构中,监控程序随之作适当处理,处理完毕后返回继续等待。
5.当不需要继续监控或收到某个代表监控结束的信号时,关闭inotify实例的文件描述符。
关于基本的使用流程还可以参考下图:
4.inotify用户态API
inotify的API都使用文件描述符,这样可以将监控粒度控制到单个文件,而dnotify机制的控制粒度则为单个目录。使用文件描述符更大的优势在于对inotify的操作也可以使用read()、close()、select()等这些传统的文件操作函数。
1.int inotify_init (void)
创建并初始化一个inotify实例,该函数返回一个文件描述符。可以认为这个函数是打开一个inotify类型的文件并返回该类型文件的描述符。
2.int inotify_add_watch (int __fd, const char *__name, uint32_t __mask)
增加监视文件(监视器),fd用于指明该文件被添加于哪个inotify实例,name用于指名该文件的路径,mask则指明了该文件所有的监控事件。该函数调用成功后返回一个监视器的描述符。
3.int inotify_rm_watch (int __fd, int __wd)
从fd中删除一个监视器,wd指名具体的监视器。
关于上述函数的详细的使用方法以及错误返回值等内容可以参考man手册。
参考:
IBM Developer Works:http://www.ibm.com/developerworks/cn/linux/l-ubuntu-inotify/index.html