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

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

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

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

使用场景

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

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

系统调用 与 inotify 有关的系统调用主要有三个:inotify_initinotify_add_watchinotify_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 *pathname, uint32_t mask);
  • fd 指代 inotify_init 系统调用返回的 notify 实例
  • pathname 指代需要被监听事件的文件或者目录路径
  • mask 事件掩码,表明需要监听的事件类型。具体的事件类型下文会进行描述

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

 

 

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

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

inotify_rm_watch

#include <sys inotify.h="">
int inotify_rm_watch(int fd, uint32_t wd);
  • 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_FROMIN_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_OPENIN_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支持的事件类型,可以看出来支持的事件类型非常丰富,基本满足了我们对于文件监听的各种诉求。除了上述的事件类型外,在这一小节我们会简单描述一下inotifyevent结构,通过事件的数据结构可以看出,从事件中我们可以获取到哪些信息。事件的具体数据结构如下:

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下的三个文件来达到控制文件事件监听的目的。

  • 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),完美支持

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     文件系统取消挂载,之后不再监听此文件系统。

 

 

 

 

 
posted on 2022-09-08 17:29  小破孩楼主  阅读(2741)  评论(0编辑  收藏  举报