日常工作中,经常会需要知道某些文件上的变化,一般是通过轮询机制检查文件的变化,不过这样非常低效。
Inotify 是一种文件变化通知机制,Linux 内核从 2.6.13 开始引入,而 BSD 和 Mac OS 系统中比较有名的是 kqueue,可以高效地实时跟踪文件系统的变化。
这里简单介绍其使用方法。
简介
可以通过如下方式查看是否支持 Inotify 机制。
$ grep INOTIFY_USER /boot/config-$(uname -r)
CONFIG_INOTIFY_USER=y
如果输出 CONFIG_INOTIFY_USER=y
那么就表示系统支持 inotify 机制。
源码实现
在用户态主要通过三个系统调用,首先是要创建 inotify 实例。
#include <sys/inotify.h>
int inotify_init(void);
int inotify_init1(int flags);
每个 inotify 实例对应一个独立的排序的队列,文件系统的变化事件被称做 watches 的一个对象管理,每一个 watch 是一个二元组:
- 目标,可以是文件或目录;
- 事件掩码,应用希望关注的 inotify 事件,每位对应一个 inotify 事件。
Watch 对象通过 watch 描述符引用,其中,目录 watches 将返回在该目录下的所有文件上面发生的事件。
下面函数用于添加一个 watch 。
#include <sys/inotify.h>
int inotify_add_watch(int fd, const char *pathname, uint32_t mask);
参数:
fd 如上的 inotify_init() 返回的文件描述符;
path 被监视的目标的路径名,可以是文件名或目录名;
mask 事件掩码,在头文件 `linux/inotify.h` 中定义了每一位代表的事件。
可以使用同样的方式来修改事件掩码,即改变希望被通知的 inotify 事件。
下面的函数用于删除一个 watch 。
#include <sys/inotify.h>
int inotify_rm_watch(int fd, int wd);
参数:
fd 同样是 inotify_init() 返回的文件描述符;
wd 通过 inotify_add_watch() 返回的 watch 描述符。
获取事件
文件事件通过 struct 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 */
};
该结构的 name 字段为一个桩,它只是为了用户方面引用文件名,文件名是变长的,它实际紧跟在该结构的后面,文件名将被 0 填充以使下一个事件结构能够 4 字节对齐。
通过 read 调用可以一次获得多个事件,只要提供的 buf 足够大。
可以在函数 inotify_init()
返回的文件描述符 fd 上使用 select()
或 poll()
,也可以在 fd 上使用 ioctl
命令 FIONREAD
来得到当前队列的长度。
close(fd)
将删除所有添加到 fd 中的 watch 并做必要的清理。
内核实现
在内核中,每一个 inotify 实例对应一个 struct inotify_device
结构。
struct inotify_device {
wait_queue_head_t wq; /* wait queue for i/o */
struct idr idr; /* idr mapping wd -> watch */
struct semaphore sem; /* protects this bad boy */
struct list_head events; /* list of queued events */
struct list_head watches; /* list of watches */
atomic_t count; /* reference count */
struct user_struct *user; /* user who opened this dev */
unsigned int queue_size; /* size of the queue (bytes) */
unsigned int event_count; /* number of pending events */
unsigned int max_events; /* maximum number of events */
u32 last_wd; /* the last wd allocated */
};
struct inotify_device
在调用 inotify_init()
时创建,当关闭对应的文件描述符时将被释放,而结构体 struct inotify_watch
在用户调用 inotify_add_watch()
时创建,在调用 inotify_rm_watch()
或 close(fd)
时被释放。
参考
inotail 通过 inotify 机制实现的 tail 功能,不过实际上 tail 最新版本也已经支持了 inotify 机制。