它是一个内核用于通知用户空间程序文件系统变化的机制。
inotify是文件系统变化通知机制,在监听到文件系统变化后,会向相应的应用程序发送事件。典型的应用场景是文件管理器,理想情况下是用户修改了文件内容后立刻显示出文件最新的内容,如果没有inotify机制,一般会采用轮询的方式实现这种功能,这不能再第一时间反应文件系统的变化,而且浪费CPU时间。
与 inotify 有关的系统调用主要有三个:inotify_init
,inotify_add_watch
,inotify_rm_watch
,具体的系统调用如下所示:
int fd = inotify_init ();
inotify_init
创建一个inotify
实例,该函数会返回文件描述符用来指代inotify
实例,同时之后需要通过对该文件描述符进行read
操作
获取文件变更事件。
int wd = inotify_add_watch (fd, path, mask);
inotify_init
系统调用返回的 notify
实例该系统调用返回值(wd)是监控描述符,指代一条监控项。
int ret = inotify_rm_watch (fd, wd);
inotify_init
系统调用返回的 notify
实例mask 标志 | 描述 |
---|---|
IN_ACCESS | 文件被访问(执行了 read 操作) |
IN_ATTRIB | 文件元数据发生变更 |
IN_CLOSE_WRITE | 关闭为了写入而打开的文件 |
IN_CLOSE_NOWRITE | 关闭以只读方式打开的文件 |
IN_CREATE | 在受控目录内创建了文件或者目录 |
IN_DELETE | 在受控目录内删除了文件或者目录 |
IN_DELETE_SELF | 删除受控文件或者目录本身 |
IN_MOVED_FROM | 文件移出受控目录之外 |
IN_MOVED_TO | 文件移入受控目录 |
IN_OPEN | 文件被打开 |
IN_MOVE | IN_MOVED_FROM|IN_MOVED_TO 事件的统称 |
IN_CLOSE | IN_CLOSE_WRITE|IN_CLOSE_NOWRITE 统称 |
IN_ATTRIB
监控的元数据变更包括,权限,所有权,链接数,用户 ID 等IN_DELETE_SELF
事件,而如果受控目标是一个目录,那么受控目标下的文件发生重命名时会触发两个事件IN_MOVED_FROM
和IN_MOVED_TO
在我们的日常开发工作中,上述事件已经基本涵盖了文件变更的所有情况。我们可以按照各自的场景,针对上述不同的事件类型做出相应的处理流程。
除了上述文件的常规事件外,inotify
还提供了以下几个 mask 来控制事件监听的过程
mask 标志 | 描述 |
---|---|
IN_DONT_FOLLOW | 不对符号链接引用 |
IN_MASK_ADD | 将事件追加到 pathname 的当前监控掩码 |
IN_ONESHOT | 只监控 pathname 的一个事件 |
IN_ONLYDIR | pathname 不为目录时会失败 |
将上述 mask 标志添加到 inotify_add_watch
中时可以控制监听过程,这么说有点笼统,举个例子来说。
inotify_add_watch(fd, pathname, IN_OPEN | IN_CLOSE | IN_ONESHOT);
上面这段代码,除了监听文件的 IN_OPEN
和IN_CLOSE
事件外,还添加了 IN_ONESHOT
mask,那么这就意味着,当监听到 pathname 所指代的文件一次事件后 inotify
就不会在监听 pathname 所指代的文件发出的事件了。
上述 mask 是在添加某个文件监控项的时候作为inotify_add_watch
系统调用的参数传入的。除此之外还有以下几个事件,这些事件不需要用户显示调用inotify_add_watch
添加,仅当出现一些其他异常情况时发出。
mask 标志 | 描述 |
---|---|
IN_IGNORED | 监控项为内核或者应用程序移除 |
IN_ISDIR | 被监听的是一个目录的路径 |
IN_Q_OVERFLOW | 事件队列溢出 |
IN_UNMOUNT | 包含对象的文件系统遭到卸载 |
IN_ISDIR
事件表明被监听的 pathname 指代的是一个目录,举例来说 mkdir /tmp/xxx 这个系统命令会产生 IN_CREATE|IS_DIR
事件。上文描述了inotify
支持的事件类型,可以看出来支持的事件类型非常丰富,基本满足了我们对于文件监听的各种诉求。除了上述的事件类型外,在这一小节我们会简单描述一下inotify
的event
结构,通过事件的数据结构可以看出,从事件中我们可以获取到哪些信息。事件的具体数据结构如下:
struct inotify_event {
int wd; //监控描述符号,唯一指代一个监控项目
uint32_t mask; //监控的事件类型
uint32_t cookie; //只有重命名才会使用到该字段
uint32_t len; //下面 name 数组的尺寸
char name[]; //当受控目录下的文件有变更时,该字符串会记录发生变更的文件的文件名
};
#include
#include
#include
#include
int main() {
char* filePath = "/dev/input";
for (;;) {
int mINotifyFd = inotify_init();
printf("mINotifyFd=%d\n", mINotifyFd);
int result = inotify_add_watch(mINotifyFd, filePath, IN_ALL_EVENTS);
printf("inotify_add_watch result=%d\n", result);
fd_set fds;
FD_ZERO(&fds);
FD_SET(mINotifyFd, &fds);
unsigned char buffer[1024] = { 0 };
if (select(mINotifyFd + 1, &fds, NULL, NULL, NULL) > 0) {
int length = read(mINotifyFd, buffer, sizeof(buffer));
int offset = 0;
while (offset <= length) {
struct inotify_event* event = (struct inotify_event*)(buffer + offset);
printf("event->name:%s\n", event->name);
if (event->mask & IN_ACCESS) {
printf("IN_ACCESS\n");
}
if (event->mask & IN_OPEN) {
printf("IN_OPEN\n");
}
if (event->mask & IN_CLOSE) {
printf("IN_CLOSE\n");
}
offset += sizeof(struct inotify_event) + event->len;
}
}
}
return 0;
}