当前位置: 首页 > 工具软件 > iNotify > 使用案例 >

INotify的介绍和使用

叶德本
2023-12-01

INotify的介绍和使用

inotify – Linux 2.6 内核中的文件系统变化通知机制

INotify的介绍

inotify 是一种文件系统的变化通知机制,如文件增加、删除等事件可以立刻让用户态得知。

  1. Inotify 不需要对被监视的目标打开文件描述符,而且如果被监视目标在可移动介质上,那么在 umount 该介质上的文件系统后,被监视目标对应的 watch 将被自动删除,并且会产生一个 umount 事件。
  2. Inotify 既可以监视文件,也可以监视目录。
  3. Inotify 使用系统调用而非 SIGIO 来通知文件系统事件。
  4. Inotify 使用文件描述符作为接口,因而可以使用通常的文件 I/O 操作select 和 poll 来监视文件系统的变化。
Inotify 可以监视的文件系统事件包括:
IN_ACCESS,即文件被访问
IN_MODIFY,文件被 write
IN_ATTRIB,文件属性被修改,如 chmod、chown、touch 等
IN_CLOSE_WRITE,可写文件被 close
IN_CLOSE_NOWRITE,不可写文件被 close
IN_OPEN,文件被 open
IN_MOVED_FROM,文件被移走,如 mv
IN_MOVED_TO,文件被移来,如 mv、cp
IN_CREATE,创建新文件
IN_DELETE,文件被删除,如 rm
IN_DELETE_SELF,自删除,即一个可执行文件在执行时删除自己
IN_MOVE_SELF,自移动,即一个可执行文件在执行时移动自己
IN_UNMOUNT,宿主文件系统被 umount
IN_CLOSE,文件被关闭,等同于(IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)
IN_MOVE,文件被移动,等同于(IN_MOVED_FROM | IN_MOVED_TO)

注:上面所说的文件也包括目录。

INotify 用户接口、使用过程

在用户态,inotify 通过三个系统调用和在返回的文件描述符上的文件 IO 操作来使用,使用 inotify 的第一步是创建 inotify 实例:

 int fd = inotify_init ();

每一个 inotify 实例对应一个独立的排序的队列。
文件系统的变化事件被称做 watches 的一个对象管理,每一个 watch 是一个二元组(目标,事件掩码),目标可以是文件或目录,事件掩码表示应用希望关注的 inotify 事件,每一个位对应一个 inotify 事件。Watch 对象通过 watch描述符引用,watches 通过文件或目录的路径名来添加。目录 watches 将返回在该目录下的所有文件上面发生的事件。

下面函数用于添加一个 watch:

    int wd = inotify_add_watch (fd, path, mask);


fd   是 inotify_init() 返回的文件描述符,  
path 是被监视的目标的路径名(即文件名或目录名),  
mask 是事件掩码, 在头文件 linux/inotify.h 中定义了每一位代表的事件。可以使用同样的方式来修改事件掩码,即改变希望被通知的inotify 事件。  
Wd   是 watch 描述符。

下面的函数用于删除一个 watch:

    int ret = inotify_rm_watch (fd, wd);

fd 是 inotify_init() 返回的文件描述符,  
wd 是 inotify_add_watch() 返回的 watch 描述符。  
Ret 是函数的返回值。

文件事件用一个 inotify_event 结构表示,它通过由 inotify_init() 返回的文件描述符使用通常文件读取函数 read 来获得

struct inotify_event {
        __s32           wd;             /* watch descriptor */
        __u32           mask;           /* watch mask */
        __u32           cookie;         /* cookie to synchronize two events */
        __u32           len;            /* length (including nulls) of name */
        char            name[0];        /* stub for possible name */
}; 

wd 为被监视目标的 watch 描述符,
mask 为事件掩码,
len 为 name字符串的长度,
name 为被监视目标的路径名,该结构的 name 字段为一个桩,它只是为了用户方面引用文件名,文件名是变长的,它实际紧跟在该结构的后面,文件名将被 0 填充以使下一个事件结构能够 4 字节对齐。注意,len 也把填充字节数统计在内。

通过 read 调用可以一次获得多个事件,只要提供的 buf 足够大。

 size_t len = read (fd, buf, BUF_LEN);

 buf 是一个 inotify_event 结构的数组指针,
 BUF_LEN 指定要读取的总长度,
 buf 大小至少要不小于 BUF_LEN,该调用返回的事件数取决于 BUF_LEN 以及事件中文件名的长度。
 Len 为实际读去的字节数,即获得的事件的总长度。

可以在函数 inotify_init() 返回的文件描述符 fd 上使用 select() 或poll(), 也可以在 fd 上使用 ioctl 命令 FIONREAD 来得到当前队列的长度。close(fd)将删除所有添加到 fd 中的 watch 并做必要的清理。

Inotify使用实例

该程序将监视发生在当前目录下的文件 tmp_file 与当前目录下的目录 tmp_dir 上的所有文件系统事件, 同时它也将监视发生在文件 /mnt/sda3/windows_file 上的文件系统事件,注意,/mnt/sda3 是 SATA 硬盘分区 3 的挂接点。

#include <linux/unistd.h>
#include <linux/inotify.h>
#include <errno.h>
_syscall0(int, inotify_init)
_syscall3(int, inotify_add_watch, int, fd, const char *, path, __u32, mask)
_syscall2(int, inotify_rm_watch, int, fd, __u32, mask)
char * monitored_files[] = {
    "./tmp_file",
    "./tmp_dir",
    "/mnt/sda3/windows_file"
};
struct wd_name {
    int wd;
    char * name;
};
#define WD_NUM 3
struct wd_name wd_array[WD_NUM];
char * event_array[] = {
    "File was accessed",
    "File was modified",
    "File attributes were changed",
    "writtable file closed",
    "Unwrittable file closed",
    "File was opened",
    "File was moved from X",
    "File was moved to Y",
    "Subfile was created",
    "Subfile was deleted",
    "Self was deleted",
    "Self was moved",
    "",
    "Backing fs was unmounted",
    "Event queued overflowed",
    "File was ignored"
};
#define EVENT_NUM 16
#define MAX_BUF_SIZE 1024

int main(void)
{
    int fd;
    int wd;
    char buffer[1024];
    char * offset = NULL;
    struct inotify_event * event;
    int len, tmp_len;
    char strbuf[16];
    int i = 0;

    fd = inotify_init();
    if (fd < 0) {
        printf("Fail to initialize inotify.\n");
        exit(-1);
    }
    for (i=0; i<WD_NUM; i++) {
        wd_array[i].name = monitored_files[i];
        wd = inotify_add_watch(fd, wd_array[i].name, IN_ALL_EVENTS);
        if (wd < 0) {
            printf("Can't add watch for %s.\n", wd_array[i].name);
            exit(-1);
        }
        wd_array[i].wd = wd;
    }
    while(len = read(fd, buffer, MAX_BUF_SIZE)) {
        offset = buffer;
        printf("Some event happens, len = %d.\n", len);
        event = (struct inotify_event *)buffer;
        while (((char *)event - buffer) < len) {
            if (event->mask & IN_ISDIR) {
                memcpy(strbuf, "Direcotory", 11);
            }
            else {
                memcpy(strbuf, "File", 5);
            }
            printf("Object type: %s\n", strbuf);
            for (i=0; i<WD_NUM; i++) {
                if (event->wd != wd_array[i].wd) continue;
                printf("Object name: %s\n", wd_array[i].name);
                break;
            }
            printf("Event mask: %08X\n", event->mask);
            for (i=0; i<EVENT_NUM; i++) {
                if (event_array[i][0] == '\0') continue;
                if (event->mask & (1<<i)) {
                    printf("Event: %s\n", event_array[i]);
                }
            }
            tmp_len = sizeof(struct inotify_event) + event->len;
            event = (struct inotify_event *)(offset + tmp_len); 
            offset += tmp_len;
        }
    }
}
 类似资料: