Inotify——实现Linux文件系统的监控

Inotify简介

Inotify是Linux中用于监控文件系统变化的API,自Linux 2.6.13开始集成到Linux内核中,并取代了以往的dnotify。该API可以实现用非轮询的方式,近似实时地监控目录或文件发生的变化。
利用Inotify可以十分方便地实现诸如文件实时同步、目录索引自动更新、配置文件自动重加载等功能。在Linux中可以通过man 7 inotify查阅Inotify的手册。

API使用简介

Inotify API由头文件<sys/inotify.h>提供,其中包括函数

inotify_init()          inotify_init1()          inotify_add_watch()          inotify_rm_watch()。

inotify_init()和inotify_init1()功能相似,若成功调用可以初始化一个inotify的实例,并返回一个用于读取文件系统事件的文件描述符,若失败则会返回-1。
不同之处在于,后者提供了一个参数。当传入0时,二者没有区别,但传入IN_NONBLOCK IN_CLOEXEC掩码后会对返回的文件描述符提供额外的控制功能,这两个掩码分别提供与fcntl()中的O_NONBLOCK和open()中的FD_CLOEXEC相似的功能,在此不对这两个掩码的功能作详细介绍。

在成功调用inotify_init()或inotify_init1()后,其返回值作为文件描述符,可在后续使用read()系统调用来读取文件系统监控事件的信息,并且该读取是阻塞式的,当没有事件发生时,read()将不会返回。

inotify_add_watch()和inotify_rm_watch()分别用于从监控列表中添加和删除目录/文件项。inotify_add_watch()接受参数为inotify实例的文件描述符、需要监控的目标文件/目录路径,以及需要监控的事件掩码,成功调用后返回一个监控描述符(Watch descriptor),对应于目标路径在文件系统中的inode对象,若失败则会返回-1并设置errno。inotify_rm_watch()接受参数为inotify实例的文件描述符和监控文件或目录的监控描述符,可以停止监控对应的文件或目录。

对于一个文件或目录,可以使用位或运算符合并掩码来同时监控多个事件。可监控事件列表如下:

事件类型

常规事件类型

在这里插入图片描述

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_MOVEIN_MOVED_FROM|IN_MOVED_TO 事件的统称
IN_CLOSEIN_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_ONLYDIRpathname 不为目录时会失败

将上述 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 事件。

在设置监控成功后,通过read()系统调用从inotify实例提供的文件描述符中读取文件系统监控事件的信息,该信息使用inotify_event结构体来记录,该结构体的声明如下:

struct inotify_event {

        int wd;                                    //监控描述符号,唯一指代一个监控项目   

        uint32_t mask;                     //监控的事件类型        

        uint32_t cookie;                  //只有重命名才会使用到该字段

        uint32_t len;                       //下面 name 数组的尺寸

        char name[];                     //当受控目录下的文件有变更时,该字符串会记录发生变更的文件的文件名 };

示例代码

#include <stdio.h>
#include <sys/inotify.h>
#include <unistd.h>

int main(void)
{
int eventFd = inotify_init();
if (eventFd < 0)
{
printf("Failed to create inotify instance\n");
return 1;
}

int wd = inotify_add_watch(
eventFd,
"/home/kayo",
IN_CREATE | IN_DELETE | IN_MODIFY | IN_CLOSE_WRITE);

if (wd < 0)
{
printf("Failed to add watch\n");
return 1;
}

printf("Start to watch directory /home/kayo\n");
char buffer[4096];

while (1)
{
ssize_t len = read(eventFd, buffer, sizeof(buffer));
if (len == -1)
{
printf("End watch\n");
break;
}

ssize_t i = 0;
while (i < len)
{
struct inotify_event *event = (struct inotify_event *)(buffer + i);
if (event->mask & IN_CREATE)
{
printf("IN_CREATE: %s\n", event->name);
}

if (event->mask & IN_MODIFY)
{
printf("IN_MODIFY: %s\n", event->name);
}

if (event->mask & IN_DELETE)
{
printf("IN_DELETE: %s\n", event->name);
}

if (event->mask & IN_CLOSE_WRITE)
{
printf("IN_CLOSE_WRITE: %s\n", event->name);
}

i += event->len + sizeof(struct inotify_event);
}
}

return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值