Linux系统编程:监视文件系统变化(inotify)

inotify简介

你是否有过这样的需求,监控某个文件的修改情况,一旦某个文件的内容被修改你的进程就能观察到,对准对情况进行文件备份之类的操作。在Linux 环境下,内核提供了inotify API 用于监控文件系统的情况,可以用于监视文件的创建,修改,删除等事件。

inotify用于当内核发生文件系统相关的某种事件后,用于通知用户空间,方便用户做出具体的操作。inotify 可以监控单个的文件,也可以监控整个目录,当一个目录被监控时,inotify会返回关于该目录本身及其内部文件的事件。

inotify接口

inotify 接口的使用十分的简单,调用时一般需要引用头文件 <sys/inotify.h> ,其常用的接口就三种,下面我们来介绍一下各个接口的使用。

inotify_init()

接口inotify_init 的函数签名如下:

int inotify_init();

这个接口用于创建一个inotify 实例,并返回一个文件描述符指向这个实例的事件队列。inotify API还提供一个类似的借口用于创建inotify,即inotify_init1() ,可以格外接受一个参数用于提供格外的机制,这里不展开。

inotify_add_watch

函数签名如下:

int inotify_add_watch(int fd, const char* pathname, uint32_t mask);

这个接口用于为一个已经初始化后的inotify 实例创建一个新的监视项,或者修改一个已经存在的监视项。接口的参数的含义如下:

  • fd: inotify 实例对应的文件描述符,即inotify_init() 返回的结果。
  • pathname:监视的文件或者目录的路径
  • mask:用来指定监视的事件类型

其中可以设置的事件如下:

IN_ACCESS	文件被访问
IN_ATTRIB	文件元数据改变
IN_CLOSE_WRITE	关闭为了写入而打开的文件
IN_CLOSE_NOWRITE	关闭只读方式打开的文件
IN_CREATE	在监听目录内创建了文件/目录
IN_DELETE	在监听目录内删除文件/目录
IN_DELETE_SELF	监听目录/文件本身被删除。
IN_MODIFY	文件被修改
IN_MOVE_SELF	受监控目录/文件本身被移动
IN_MOVED	文件被移
IN_OPEN	文件被打开
IN_ALL_EVENTS	以上所有输出事件的统称

成功的inotify_add_watch 调用会为inotify 实例和 文件系统对象创建一个唯一的 监控描述符。如果该文件系统没有被该 inotify 实例监视过,那么该监控描述符就是新分配的,否则会返回已经存在的监控描述符。

inotify_rm_watch()

函数原型如下:

int inotify_rm_watch(int fd, int wd);

参数的含义如下:

  • fd: inotify 实例的文件描述符。
  • wd: 一个监控项的监控描述符,即inotify_add_watch 的返回值。

该接口用于从一个inotify 实例中移除一个已经存在的监控项。

读取事件

inotify 并没有提供格外的借口来提供对变动事件的获取,而是通过通用的read 来读取,当没有触发任何事件时,默认情况下调用read 会阻塞线程。read 的函数签名如下:

int read(int fd, void* event, size_t len);

参数的介绍如下:

  • fd: inotify 实例的文件描述符
  • event: 用于存放事件变动的缓冲区
  • len: 表示缓冲区的大小

event参数用于存放变动事件,一般强制转换为struct inotify_event 结构体数组来使用,这里转换为数组是因为read一次调用时,可能同时得到多个事件的响应,如果你觉得不能理解,可以查看后面的示例之后再来体会。

数据结构

struct inotify_event 结构体的定义如下:

struct inotify_event {
   int         wd;      // 被监控文件或目录的描述符(由inotify_add_watch)
   uint32_t    mask;    // 变动的事件
   uint32_t    cookie;  // 比较少使用,可以忽略
   uint32_t    len;     // name的长度
   char        name[];  // 用于存放发生变动的文件或目录名称
};

示例

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <string.h>
#include <sys/inotify.h>


#define MAX_EVENTS 10
#define EVENT_SIZE  (sizeof(struct inotify_event) + NAME_MAX + 1)
#define BUFFER_SIZE (1024 * (EVENT_SIZE))

void print_event(int wd, struct inotify_event *event) {
    if(event->mask & IN_CREATE)
    {
        printf("create file: %s\n",event->name);
    }
    else if(event->mask & IN_MODIFY)
    {
        printf("modify file: %s\n",event->name);
    }
    else {
        printf("delete file: %s\n",event->name);
    }
}

int main() {
    int fd, i;
    unsigned long events;
    char buffer[BUFFER_SIZE];
    struct inotify_event *event;

    // 创建 inotify 实例
    fd = inotify_init();
    if (fd < 0) {
        perror("ERROR: inotify_init failed");
        return EXIT_FAILURE;
    }

    // 添加需要监控的路径
    int wd = inotify_add_watch(fd, "./", IN_CREATE | IN_DELETE | IN_MODIFY);
    if (wd < 0) {
        perror("ERROR: inotify_add_watch failed");
        close(fd);
        return EXIT_FAILURE;
    }

    // 读取事件
    for (;;) {
        events = read(fd, buffer, BUFFER_SIZE);
        if (events < 0) {
            perror("ERROR: read failed");
            break;
        }

		// 遍历触发的事件列表
        for (i = 0; i < events; ) {
            printf("%d: ", i);
            event = (struct inotify_event *) &buffer[i];	
            print_event(wd, event);
            i += EVENT_SIZE + event->len;
        }
    }

    // 清理
    inotify_rm_watch(fd, wd);
    close(fd);

    return 0;
}

注意事项

当你希望使用 inotify 来监视某一个文件的创建和删除时,因为inotify 无法监视一个不存在的文件,你应该使用监视文件所在目录的方式来监听。

inofity 的句柄文件描述符和普通的文件描述符一样,你可以使用epoll 来监听inotify 句柄的读事件来高效的响应文件的变动事件,这里不做演示。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值