inotify是一种使用简单而功能强大的文件系统事件监控机制。在用户态下通过一组简单的系统调用即可使用inotify机制监控文件的变化,而在内核态中也可以通过一组API来inotify机制。
1.创建并初始化inotify实例
为了在内核中使用inotify机制,必须首先创建并初始化一个inotify实例,每个inotify实例其实对应的是一个数据结构inotify_handle。
struct inotify_handle { struct idr idr; /* idr mapping wd -> watch */ struct mutex mutex; /* protects this bad boy */ struct list_head watches; /* list of watches */ atomic_t count; /* reference count */ u32 last_wd; /* the last wd allocated */ const struct inotify_operations *in_ops; /* inotify caller operations */ };
创建并初始化一个inotify实例的过程其实就是分配并初始化一个inotify_handle结构,这个过程通过inotify_init()完成。此外,每个inotify实例都与一个inotify_operations结构相关联,该结构中有两个钩子函数,分别定义了事件处理和销毁watch两个函数的接口,这两个钩子函数应该根据具体的应用场景来实现。
struct inotify_operations { void (*handle_event)(struct inotify_watch *, u32, u32, u32, const char *, struct inode *); void (*destroy_watch)(struct inotify_watch *); };
一个inotify_init()通用的使用方法如下:
struct inotify_operations *ops; ops->handle_event = my_handle_event; ops->destroy_watch = my_destroy_watch; struct inotify_handle *ih = inotify_init(ops);
也就是说,通过向inotify_init()中传入一个inotify_operations结构的变量来实现钩子函数和inotify实例之间的关联。
2.添加watch
从inotify实例所对应的数据结构inotify_handle中可以看出,每个inotify实例都拥有一个由watch组成的链表。该双链表上的每个watch即代表该inotify实例所监控的对象,这个对象可能是文件,也可能是目录。每个watch在内核中的表示如下:
struct inotify_watch { struct list_head h_list; /* entry in inotify_handle's list */ struct list_head i_list; /* entry in inode's list */ atomic_t count; /* reference count */ struct inotify_handle *ih; /* associated inotify handle */ struct inode *inode; /* associated inode */ __s32 wd; /* watch descriptor */ __u32 mask; /* event mask for this watch */ };
该结构中每个字段的含义如下:
h_list:一个inotify实例中所有watch组成一个双链表,h_list表示当前watch在该双链表中所处的结点。该双链表的表头即为inotify_handle结构中的watches字段。
i_list:一个文件可能被多个inotify实例监控,而被监控一次就产生一个watch,该文件对应的所有watch组成一个双链表,i_list表示当前watch在该双链表中所处的结点。该双链表的表头即为inode结构中的inotify_watches字段。
count:表示该watch被引用的次数。
ih:每个watch必然属于某个inotify实例,该字段指向当前watch所属的inotify_handle结构。
inode:该字段指向当前watch所关联的文件的inode。
wd:表示当前watch的文件描述符。
mask:表示当前监控对象所对应的事件掩码。
当初始化完一个inotify实例后,通过inotify_add_watch()即可向该实例中添加watch。不过在添加前还需通过inotify_init_watch()对每个watch进行初始化。
对watch初始化完成的主要工作即初始化两个链表结点、该watch的引用计数等。向inotify实例添加一个watch主要完成的工作即为向两个watch链表中添加当前的watch结点,并且更新watch的引用计数等。
3.删除一个watch
从指定的inotify实例中删除一个watch所完成的工作其实和添加watch的过程相反,通过inotify_rm_watch()即可完成。
4.对监控事件的处理
handle_event()通常并不对具体的监控对象做处理,而是作为所有监控事件的入口点;接着根据每个事件的掩码做出“分流”动作,即对具体的监控事件做以处理。