某些应用程序需要对文件或目录进行监控,以感知这些文件或目录发生了特定事件。在 Linux 中提供了 inotify 机制允许应用程序可以监听文件(目录)事件。

本文主要从以下几个方面对 inotify 进行介绍:

  • inotify 使用场景
  • inotify 机制关联的相关系统调用
  • inotify 支持的事件类型
  • inotify 使用示例

使用场景

监听文件或者目录的变更,最终目的一定是基于不同的变更事件采取相对应的处理措施。比较常见的使用场景如下:

  • 配置文件热加载,当配置文件发生变化时进程可以自动感知并重新 reload 配置文件,如 golang 的明星项目--​​viper​
  • 配置保持功能,当我们需要保持服务器上某些文件不被改动时,可以监听需要保持的文件。当文件出现变更时做相应的恢复处理
  • 当文件移出或者加入到某个目录下的时候,图形化文件管理器需要根据对应的事件作出相对应的调整

系统调用 与 inotify 有关的系统调用主要有三个:inotify_init,inotify_add_watch,inotify_rm_watch

inotify_init

#include <sys inotify.h="">
int inotify_init(void);

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

inotify_add_watch

#include <sys inotify.h="">
int inotify_add_watch(int fd, const char
  • fd 指代​​inotify_init​​​ 系统调用返回的​​notify​​ 实例
  • pathname 指代需要被监听事件的文件或者目录路径
  • mask 事件掩码,表明需要监听的事件类型。具体的事件类型下文会进行描述

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

linux 文件监控之 inotify、及inotify-tools的安装和使用_描述符

 


 

上图展示了一个​​notify​​实例,以及该实例维护的一组监控项

  • 监控描述符就是​​inotify_add_watch​​系统调用的返回,唯一指代一个监控描述项
  • 掩码即是 mask 用来定义具体的监听事项
  • pathname 即是完整的待监听文件或目录的合法路径

inotify_rm_watch

#include <sys inotify.h="">
int inotify_rm_watch(int
  • fd 指代​​inotify_init​​​ 系统调用返回的​​notify​​ 实例
  • wd 指代监控项描述符

事件类型

常规事件类型

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​​添加,仅当出现一些其他异常情况时发出。

  • ​IN_ISDIR​​​事件表明被监听的 pathname 指代的是一个目录,举例来说 mkdir /tmp/xxx 这个系统命令会产生​​IN_CREATE|IS_DIR​​ 事件。

事件结构

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

mask 标志

描述

IN_IGNORED

监控项为内核或者应用程序移除

IN_ISDIR

被监听的是一个目录的路径

IN_Q_OVERFLOW

事件队列溢出

IN_UNMOUNT

包含对象的文件系统遭到卸载

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

​inotify​​​的事件队列并不是无限大的,因为队列也是需要消耗内核内存的,因此会设置一些上限加以限制,具体的配置可以通过修改对​​/proc/sys/fs/inotify​​下的三个文件来达到控制文件事件监听的目的。

linux 文件监控之 inotify、及inotify-tools的安装和使用_描述符_02

  • ​max_queued_events​​​: 规定了​​inotify​​​事件队列的数量上限,一旦超出这个限制,系统就会生成​​IN_Q_OVERFLOW​​事件,该事件上文有详细描述,这里不再赘述。
  • ​max_user_instances​​​:对由每个真实用户 ID 创建的​​inotify​​实例数的限制值。
  • ​max_user_watchers​​:对由每个真实用户 ID 创建的监控项数量的限制值。

inotify-tools的安装和使用

inotify-tools提供两种工具,一是 inotifywait,它是用来监控文件或目录的变化,二是inotifywatch,它是用来统计文件系统访问的次数。
kernel 2.6.13以上都支持(直接安装使用)运行测试命令
[root@hostname inotify]# grep INOTIFY_USER /boot/config-$(uname -r)
CONFIG_INOTIFY_USER=y
如果结果=y(yes),完美支持

linux 文件监控之 inotify、及inotify-tools的安装和使用_描述符_03

​wget http:​​​​//github​​​​.com​​​​/downloads/rvoicilas/inotify-tools/inotify-tools-3​​​​.14.​​​​tar​​​​.gz  ​​​​#下载最新版​

​tar​​​ ​​zxvf inotify-tools-3.14.​​​​tar​​​​.gz  ​​​​#解压到当前目录​

​cd​​​ ​​inotify-tools-3.14 ​​​​#进入目录​

​.​​​​/configure​​​ ​​--prefix=​​​​/opt/ideatech/inotify/​​​ ​​#编译配置,这里是指定位置(你的文件目录)​

​make​​​ ​​#编译​

​make​​​ ​​install​​​ ​​#安装​

​make​​​ ​​clean ​​​​#从源文件夹清除二进制对象等 ​

​cd​​​ ​​/opt/ideatech/inotify/bin​

​.​​​​/inotifywait​​​ ​​--help inofify帮助命令​

​#为方便使用可以选择设置​

​#设置系统环境变量,添加软连接​

​echo "PATH=$PATH:/opt/ideatech/inotify/bin" >>/etc/profile.d/inotify.sh ​

​source /etc/profile.d/inotify.sh #使设置立即生效​

​echo "/opt/ideatech/inotify/lib" >/etc/ld.so.conf.d/inotify.conf && ldconfig​

​ln -s /opt/ideatech/inotify/include /usr/include/inotify​

inotifywait:监控文件或目录变化
语法:
    inotifywait [-hcmrq] [-e ] [-t ] [--format ] [--timefmt ] [ ... ]
参数:
    -h,–help
    输出帮助信息
    @
    排除不需要监视的文件,可以是相对路径,也可以是绝对路径。
    –fromfile
    从文件读取需要监视的文件或排除的文件,一个文件一行,排除的文件以@开头。
    -m, –monitor
    接收到一个事情而不退出,无限期地执行。默认的行为是接收到一个事情后立即退出。
    -d, –daemon
    跟–monitor一样,除了是在后台运行,需要指定–outfile把事情输出到一个文件。也意味着使用了–syslog。
    -o, –outfile
    输出事情到一个文件而不是标准输出。
    -s, –syslog
    输出错误信息到系统日志
    -r, –recursive
    监视一个目录下的所有子目录。
    -q, –quiet
    指定一次,不会输出详细信息,指定二次,除了致命错误,不会输出任何信息。
    –exclude
    正则匹配需要排除的文件,大小写敏感。
    –excludei
    正则匹配需要排除的文件,忽略大小写。
    -t , –timeout
    设置超时时间,如果为0,则无限期地执行下去。
    -e , –event
    指定监视的事件。
    -c, –csv
    输出csv格式。
    –timefmt
    指定时间格式,用于–format选项中的%T格式。
    –format
    指定输出格式。
    %w 表示发生事件的目录
    %f 表示发生事件的文件
    %e 表示发生的事件
    %Xe 事件以“X”分隔
    %T 使用由–timefmt定义的时间格式

inotifywatch:统计文件的系统访问次数
语法:
    inotifywatch [-hvzrqf] [-e ] [-t ] [-a ] [-d ] [ ... ]
参数:
    -h, –help
    输出帮助信息
    -v, –verbose
    输出详细信息
    @
    排除不需要监视的文件,可以是相对路径,也可以是绝对路径。
    –fromfile
    从文件读取需要监视的文件或排除的文件,一个文件一行,排除的文件以@开头。
    -z, –zero
    输出表格的行和列,即使元素为空
    –exclude
    正则匹配需要排除的文件,大小写敏感。
    –excludei
    正则匹配需要排除的文件,忽略大小写。
    -r, –recursive
    监视一个目录下的所有子目录。
    -t , –timeout
    设置超时时间
    -e , –event
    只监听指定的事件。
    -a , –ascending
    以指定事件升序排列。
    -d , –descending
    以指定事件降序排列。
可监听事件
    access     文件读取
    modify     文件更改。
    attrib     文件属性更改,如权限,时间戳等。
    close_write     以可写模式打开的文件被关闭,不代表此文件一定已经写入数据。
    close_nowrite     以只读模式打开的文件被关闭。
    close     文件被关闭,不管它是如何打开的。
    open     文件打开。
    moved_to     一个文件或目录移动到监听的目录,即使是在同一目录内移动,此事件也触发。
    moved_from     一个文件或目录移出监听的目录,即使是在同一目录内移动,此事件也触发。
    move     包括moved_to和 moved_from
    move_self     文件或目录被移除,之后不再监听此文件或目录。
    create     文件或目录创建
    delete     文件或目录删除
    delete_self     文件或目录移除,之后不再监听此文件或目录
    unmount     文件系统取消挂载,之后不再监听此文件系统。

linux 文件监控之 inotify、及inotify-tools的安装和使用_系统调用_04

 

linux 文件监控之 inotify、及inotify-tools的安装和使用_系统调用_05