inotify(7) - Linux 手册页

名称

inotify - 监控文件系统事件

描述

inotify API 提供了一种监控文件系统事件的机制。Inotify 可用于监控单个文件或目录。当监控某个目录时,inotify 将返回针对该目录本身的事件,以及该目录内部文件的事件。

此 API 使用以下系统调用:inotify_init(2)(或 inotify_init1(2))、inotify_add_watch(2)、inotify_rm_watch(2)、read(2) 和 close(2)。

inotify_init(2) 创建一个 inotify 实例并返回一个指向该实例的文件描述符。较新的 inotify_init1(2) 与 inotify_init(2) 类似,但提供了额外的功能。

inotify_add_watch(2) 用于操作与 inotify 实例关联的“监控列表”(watch list)。列表中的每一项(“监控项”)都指定了一个文件或目录的路径名,以及内核应为该路径名指向的文件监控的一组事件。inotify_add_watch(2) 可以创建新的监控项或修改现有的监控项。每个监控项都有一个唯一的“监控描述符”(watch descriptor),这是由 inotify_add_watch(2) 在创建监控项时返回的一个整数。

inotify_rm_watch(2) 从 inotify 监控列表中移除一项。

当所有指向某个 inotify 实例的文件描述符都被关闭后,底层的对象及其资源将由内核释放以供复用;所有相关的监控项也会被自动释放。

要确定发生了什么事件,应用程序需对 inotify 文件描述符执行 read(2) 操作。如果目前尚未发生任何事件,且假设该文件描述符是阻塞模式,read(2) 将阻塞直到至少发生一个事件(除非被信号中断,在这种情况下调用会失败并返回 EINTR 错误;请参阅 signal(7))。

每次成功的 read(2) 调用都会返回一个缓冲区,其中包含以下一个或多个结构体:

struct inotify_event {
    int      wd;       /* Watch descriptor */
    uint32_t mask;     /* Mask of events */
    uint32_t cookie;   /* Unique cookie associating related
                          events (for rename(2)) */
    uint32_t len;      /* Size of name field */
    char     name[];   /* Optional null-terminated name */
};
wd 用于标识发生此事件的监控项。它是之前调用 inotify_add_watch(2) 所返回的监控描述符之一。

mask 包含描述所发生事件的位掩码(见下文)。

cookie 是一个唯一整数,用于关联相关事件。目前它仅用于重命名事件,允许应用程序将生成的 IN_MOVED_FROMIN_MOVED_TO 事件对关联起来。对于所有其他事件类型,cookie 设置为 0。

name 字段仅在返回被监控目录内的文件事件时存在;它标识了相对于被监控目录的文件路径名。此路径名以空字符结尾,并可能包含额外的空字节,以将后续读取对齐到合适的地址边界。

len 字段计算 name 中的所有字节数,包括空字节;因此,每个 inotify_event 结构体的长度为 sizeof(struct inotify_event)+len

当提供给 read(2) 的缓冲区太小而无法返回下一个事件的信息时,其行为取决于内核版本:在 2.6.21 之前的内核中,read(2) 返回 0;自内核 2.6.21 起,read(2) 会失败并返回 EINVAL 错误。指定一个大小为

sizeof(struct inotify_event) + NAME_MAX + 1

的缓冲区足以读取至少一个事件。

inotify 事件

inotify_add_watch(2) 的 mask 参数和调用 read(2) 读取 inotify 文件描述符时返回的 inotify_event 结构体的 mask 字段,都是标识 inotify 事件的位掩码。调用 inotify_add_watch(2) 时,可以在 mask 中指定以下位,并且这些位可能会出现在 read(2) 返回的 mask 字段中:
IN_ACCESS

文件被访问(读取)(*)。

IN_ATTRIB

元数据发生更改,例如权限、时间戳、扩展属性、链接数(自 Linux 2.6.25 起)、UID、GID 等 (*)。

IN_CLOSE_WRITE

以写入方式打开的文件被关闭 (*)。

IN_CLOSE_NOWRITE

非以写入方式打开的文件被关闭 (*)。

IN_CREATE

在被监控目录中创建了文件/目录 (*)。

IN_DELETE

从被监控目录中删除了文件/目录 (*)。

IN_DELETE_SELF

被监控的文件/目录本身被删除。

IN_MODIFY

文件被修改 (*)。

IN_MOVE_SELF

被监控的文件/目录本身被移动。

IN_MOVED_FROM

文件被移出被监控目录 (*)。

IN_MOVED_TO

文件被移入被监控目录 (*)。

IN_OPEN

文件被打开 (*)。

当监控一个目录时,上面标有星号 (*) 的事件可能发生在目录内的文件上,在这种情况下,返回的 inotify_event 结构体中的 name 字段会标识该目录内文件的名称。

IN_ALL_EVENTS 宏定义为上述所有事件的位掩码。调用 inotify_add_watch(2) 时,可以将此宏用作 mask 参数。

两个额外的便捷宏是 IN_MOVE(等同于 IN_MOVED_FROM|IN_MOVED_TO)和 IN_CLOSE(等同于 IN_CLOSE_WRITE|IN_CLOSE_NOWRITE)。

调用 inotify_add_watch(2) 时,可以在 mask 中指定以下附加位:

IN_DONT_FOLLOW (自 Linux 2.6.15 起)

如果 pathname 是符号链接,则不对其进行解引用。

IN_EXCL_UNLINK (自 Linux 2.6.36 起)

默认情况下,在监控目录的子项事件时,即使子项已从目录中取消链接(unlink),系统仍会为其生成事件。对于某些应用程序,这可能会导致生成大量不感兴趣的事件(例如,如果监控 /tmp,许多应用程序会创建名称被立即取消链接的临时文件)。指定 IN_EXCL_UNLINK 可以改变此默认行为,使得子项从被监控目录取消链接后不再生成事件。

IN_MASK_ADD

如果该路径名的监控项已存在,则将事件添加到(按位或)监控掩码中(而不是替换掩码)。

IN_ONESHOT

仅监控 pathname 的一个事件,然后将其从监控列表中移除。

IN_ONLYDIR (自 Linux 2.6.15 起)

仅在 pathname 为目录时才进行监控。

read(2) 返回的 mask 字段中,可能会设置以下位:
IN_IGNORED

监控项已被显式移除 (inotify_rm_watch(2)) 或自动移除(文件被删除或文件系统被卸载)。

IN_ISDIR

此事件的对象是一个目录。

IN_Q_OVERFLOW

事件队列溢出(此事件的 wd 为 -1)。

IN_UNMOUNT

包含被监控对象的文件系统已被卸载。

/proc 接口

以下接口可用于限制 inotify 占用的内核内存量:
/proc/sys/fs/inotify/max_queued_events
当应用程序调用 inotify_init(2) 时,将使用此文件中的值来设置相应 inotify 实例可排队事件数量的上限。超出此限制的事件会被丢弃,但始终会生成一个 IN_Q_OVERFLOW 事件。
/proc/sys/fs/inotify/max_user_instances
这指定了每个实际用户 ID 可创建的 inotify 实例数量的上限。
/proc/sys/fs/inotify/max_user_watches
这指定了每个实际用户 ID 可创建的监控项数量的上限。

版本

Inotify 在 2.6.13 Linux 内核中被合并。所需的库接口于 glibc 2.4 版本中添加。(IN_DONT_FOLLOWIN_MASK_ADDIN_ONLYDIR 仅在 2.5 版本中添加。)

符合

Inotify API 是 Linux 特有的。

说明

Inotify 文件描述符可以使用 select(2)、poll(2) 和 epoll(7) 进行监控。当有事件可用时,文件描述符会显示为可读状态。

自 Linux 2.6.25 起,inotify 文件描述符支持信号驱动 I/O 通知;请参阅 fcntl(2) 中关于 F_SETFL(用于设置 O_ASYNC 标志)、F_SETOWNF_SETSIG 的讨论。传递给信号处理程序的 siginfo_t 结构体(在 sigaction(2) 中描述)设置了以下字段:si_fd 设置为 inotify 文件描述符编号;si_signo 设置为信号编号;si_code 设置为 POLL_IN;并且在 si_band 中设置了 POLLIN

如果连续在 inotify 文件描述符上产生的输出事件相同(相同的 wdmaskcookiename),且较旧的事件尚未被读取,则它们会被合并为一个单一事件(但请参阅 BUGS 部分)。

通过读取 inotify 文件描述符返回的事件构成一个有序队列。因此,例如,可以保证当从一个目录重命名到另一个目录时,inotify 文件描述符会以正确的顺序产生事件。

FIONREAD ioctl(2) 会返回可从 inotify 文件描述符读取的字节数。

限制和注意事项

Inotify 对目录的监控不是递归的:要监控目录下的子目录,必须创建额外的监控项。对于大型目录树,这可能需要花费大量时间。

Inotify API 不提供有关触发 inotify 事件的用户或进程的信息。特别是,通过 inotify 监控事件的进程无法轻易区分自己触发的事件与由其他进程触发的事件。

请注意,事件队列可能会溢出。在这种情况下,事件会丢失。健壮的应用程序应优雅地处理事件丢失的可能性。

Inotify API 通过文件名标识受影响的文件。然而,当应用程序处理 inotify 事件时,文件名可能已经被删除或重命名。

如果监控整个目录子树,且该树中创建了新的子目录,请注意:在您为新子目录创建监控项时,新文件可能已经在该子目录中创建。因此,您可能需要在添加监控项后立即扫描子目录的内容。

错误

在 2.6.16 之前的内核中,IN_ONESHOT mask 标志无效。

在 2.6.25 内核之前,旨在合并连续相同事件的内核代码(即如果较旧的事件尚未读取,两个最近的事件可能会被合并)反而检查了最近的事件是否可以与最旧的未读事件合并。

参见

inotify_add_watch(2), inotify_init(2), inotify_init1(2), inotify_rm_watch(2), read(2), stat(2)

Linux 内核源码树中的 Documentation/filesystems/inotify.txt

引用自

credentials(7), cron(8), guestfish(1), guestfs(3), init(5), inotifywait(1), inotifywatch(1), iv_inotify_watch_unregister(3), nscd(8), proc(5), ptrace(2), status(8), telinit(8)