> 文章列表 > linux inotify机制

linux inotify机制

linux inotify机制

1、inotify主要功能

它是一个内核用于通知用户空间程序文件系统变化的机制。

inotify是文件系统变化通知机制,在监听到文件系统变化后,会向相应的应用程序发送事件。典型的应用场景是文件管理器,理想情况下是用户修改了文件内容后立刻显示出文件最新的内容,如果没有inotify机制,一般会采用轮询的方式实现这种功能,这不能再第一时间反应文件系统的变化,而且浪费CPU时间。

2. 系统调用

与 inotify 有关的系统调用主要有三个:inotify_initinotify_add_watchinotify_rm_watch,具体的系统调用如下所示:

int fd = inotify_init ();

inotify_init创建一个inotify实例,该函数会返回文件描述符用来指代inotify实例,同时之后需要通过对该文件描述符进行read 操作获取文件变更事件。

int wd = inotify_add_watch (fd, path, mask);
  • fd 指代 inotify_init 系统调用返回的 notify 实例
  • pathname 指代需要被监听事件的文件或者目录路径
  • mask 事件掩码,表明需要监听的事件类型。

该系统调用返回值(wd)是监控描述符,指代一条监控项。

 int ret = inotify_rm_watch (fd, wd);
  • fd 指代 inotify_init 系统调用返回的 notify 实例
  • wd 指代监控项描述符

3、事件类型

1.常规事件类型

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_FROMIN_MOVED_TO

在我们的日常开发工作中,上述事件已经基本涵盖了文件变更的所有情况。我们可以按照各自的场景,针对上述不同的事件类型做出相应的处理流程。

2. 其他事件

除了上述文件的常规事件外,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_OPENIN_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 事件。

4、事件结构

上文描述了inotify支持的事件类型,可以看出来支持的事件类型非常丰富,基本满足了我们对于文件监听的各种诉求。除了上述的事件类型外,在这一小节我们会简单描述一下inotifyevent结构,通过事件的数据结构可以看出,从事件中我们可以获取到哪些信息。事件的具体数据结构如下:

struct inotify_event {int		    wd;  	//监控描述符号,唯一指代一个监控项目uint32_t	mask;	//监控的事件类型uint32_t	cookie;	//只有重命名才会使用到该字段uint32_t	len;	//下面 name 数组的尺寸char		name[];	//当受控目录下的文件有变更时,该字符串会记录发生变更的文件的文件名
};

5、示例代码

#include <stdio.h>
#include <sys/inotify.h>
#include <sys/select.h>
#include <unistd.h>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;
}