监控文件事件

一、概述

某些用于程序需要对文件或目录进行监控,已侦测是否发送特定事件。

使用inotify API关键步骤:

  1. 应用程序使用inotify_init()来创建 一 inotify 实例,该系统调用所返回的文件描述符用于在后续操作中指代该实例

  2. 应用程序使用 inotify_add_watch()inoify实例(由步骤 1创建) 的监控列表添加条目,告知内核哪些文件是自己的兴趣所在。每个监控项都包含一个路径名以及相关的位掩码。位掩码针对路径名指明了所要监控的事件集合。作为函数结果,inotify_add_watch()将返回一监控描述符,用于在后续操作中指代该监控项。(系统调用inotify_rm_watch()执行其逆向操作,将之前添加入inotify实例的监控项移除)。

  3. 为获得事件通知,应用程序需针对inotify 文件描述符执行read()操作。每次对 read()的成功调用,都会返回一个或多个inotify_ event结构, 其中各自记录了处于inoify实例监控之下的某一路径名所发生的事件。

  4. 应用程序在结束监控时会关闭inotify文件描述符。这会自动清除与inotify实例相关的所有监控项。

  • inotify机制可用于监控文件或目录。当监控目录时,与路径自身及其所含文件相关的事件都会通知给应用程序。

    • inotify监控机制为非递归。若应用程序有意监控整个目录子树内的事件,则需对该树中的每个目录发起inotify_add_watch()调用。

      • 可使用select()、poll()、 epoll 以及由信号驱动的I/O来监控inotify文件描述符。只要有事件可供读取,上述API便会将inotify 文件描述符标记为可读。

二、inotify API

//创建新的inotify实例
#include<sys/inotify.h>
int inotify_init(void);
//返回一个文件描述符用于在后续操作中指代inotigy实例。

针对文件描述符fd所指定inotify实例的监控列表,系统调用inotify_add_watch()既可以追加新的监控项,也可以修改现有监控项。
在这里插入图片描述

#include<sys/inotify.h>
int inotify_add_watch(int fd,const char *pathname,uint32_t mask);
//pathname:表示欲创建或修改的监控项所对应的文件。
//mask:掩码
//未将pathname加入fd监控列表,此函数会创建新监控项;加入此函数修改现有pathname监控项的掩码,返回监控描述符。
//调用程序需对该文件具有读权限。

系统调用此函数从文件描述符fd所指代的inotify实例中删除由wd所定义的监控项。

#include<sys/inotify.h>
int inotify_rm_watch(int fd,uint32_t wd);
//wd:监控描述符,由inotify_add_watch返回。

三、inotify事件

inotify_add_watch删除和修改监控项时,位掩码参数mask标识了针对给定路径名而要监控的事件。下表列出了可在mask中定义的事件位。

在这里插入图片描述
在这里插入图片描述

四、读取inotify事件

  1. 将监控项在监控列表中等级后,应用程序可用read()inotify文件描述符中读取事件,判定发生那些事件。

  2. 读取时没有任何事件发生,read函数会阻塞,直至有事件产生。

  3. 传递给read()的缓冲区过小,无法容纳下一个inotify_event结构,那么read以失败告终,并以EINVAL错误向应用程序报告这一情况。但应用程序可再次以更大的缓冲区执行read()操作。解决方法:传递给read()的缓冲区至少为sizeof(struct inotify_event)+NAME_MAX+1字节。NAME_MAX文件最大长度,加上终止空字符。

  4. 采用的缓冲区大小如大于最小值,则可自单个read()中读取多个事件,效率极高。

  5. 文件描述符fd调用ioctl(fd,FIONREAD,&number),会返回其所指定的inotify实例中的当前可读字节数。

  6. 从inotify文件描述符中读取的事件形成了一个有序队列。在事件队列末尾追加一个新事件时,若此新事件与队列当前的尾部事件拥有相同的wd、mask、cookie和mask值,内核会将两者合并。

每次调用read()会返回一个缓冲区,内含一个或多个如下类型结构:

struct inotify_event{
		int 		wd;//指明发生事件是哪个监控描述符。
		uint32_t	mask;//返回该事件位掩码。
		uint32_t	cookie;//将相关事件联系在一起。
		uint32_t	len;  //表示分配给name字段的字节数
		char		name[];//受监控目录中有文件发生事件时,返回一个空字符结尾的字符串,标识文件。没有则不使用该字段,将len置为0。
}
		

在这里插入图片描述

五、运用inotify API

#include <sys/inotify.h>
#include <limits.h>

//显示inotify_event结构中的信息
static void displayInotifyEvent(struct inotify_event *i)
{
    printf("    wd =%2d; ", i->wd);
    if (i->cookie > 0)
        printf("cookie =%4d; ", i->cookie);

    printf("mask = ");
    if (i->mask & IN_ACCESS)        printf("IN_ACCESS ");
    if (i->mask & IN_ATTRIB)        printf("IN_ATTRIB ");
    if (i->mask & IN_CLOSE_NOWRITE) printf("IN_CLOSE_NOWRITE ");
    if (i->mask & IN_CLOSE_WRITE)   printf("IN_CLOSE_WRITE ");
    if (i->mask & IN_CREATE)        printf("IN_CREATE ");
    if (i->mask & IN_DELETE)        printf("IN_DELETE ");
    if (i->mask & IN_DELETE_SELF)   printf("IN_DELETE_SELF ");
    if (i->mask & IN_IGNORED)       printf("IN_IGNORED ");
    if (i->mask & IN_ISDIR)         printf("IN_ISDIR ");
    if (i->mask & IN_MODIFY)        printf("IN_MODIFY ");
    if (i->mask & IN_MOVE_SELF)     printf("IN_MOVE_SELF ");
    if (i->mask & IN_MOVED_FROM)    printf("IN_MOVED_FROM ");
    if (i->mask & IN_MOVED_TO)      printf("IN_MOVED_TO ");
    if (i->mask & IN_OPEN)          printf("IN_OPEN ");
    if (i->mask & IN_Q_OVERFLOW)    printf("IN_Q_OVERFLOW ");
    if (i->mask & IN_UNMOUNT)       printf("IN_UNMOUNT ");
    printf("\n");

    if (i->len > 0)
        printf("        name = %s\n", i->name);
}

#define BUF_LEN (10 * (sizeof(struct inotify_event) + NAME_MAX + 1))

int main(int argc, char *argv[])
{
    int inotifyFd, wd, j;
    char buf[BUF_LEN] __attribute__ ((aligned(8)));
    ssize_t numRead;
    char *p;
    struct inotify_event *event;

    if (argc < 2 || strcmp(argv[1], "--help") == 0)
        usageErr("%s pathname...\n", argv[0]);

    inotifyFd = inotify_init();      //创建inotify实例 
    if (inotifyFd == -1)
        errExit("inotify_init");

    for (j = 1; j < argc; j++) {
    //将命令行参数中指定的每个文件加入监控项
        wd = inotify_add_watch(inotifyFd, argv[j], IN_ALL_EVENTS);
        if (wd == -1)
            errExit("inotify_add_watch");

        printf("Watching %s using wd %d\n", argv[j], wd);
    }

    for (;;) {             
       //从inotify描述符读取事件缓冲区
        numRead = read(inotifyFd, buf, BUF_LEN);
        if (numRead == 0)
            fatal("read() from inotify fd returned 0!");

        if (numRead == -1)
            errExit("read");

        printf("Read %ld bytes from inotify fd\n", (long) numRead);

       //处理read()返回的缓冲区中的所有事件

        for (p = buf; p < buf + numRead; ) {
            event = (struct inotify_event *) p;
            //显示上述缓冲区中各inotify_event结构内容
            displayInotifyEvent(event);
            

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

    exit(EXIT_SUCCESS);
}

五、队列限制和/proc文件

对inotify事件做排队处理,需要消耗内核内存,内核会对inotify机制的操作施以各种操作。超级用户可配置/proc/sys/fs/inotify路径中的3个文件来调整这些限制:

  1. max_queued_enent:调用inotify_init函数时,使用该值来为新inotify实例队列中的事件数量设置上限。超出上限,系统生成IN_Q_OVERFLOW事件,丢弃多余的事件。溢出事件的wd字段值为-1。
  2. max_user_instances:对由每个真实用户ID创建的inotify实例数的限制值。
  3. max_user_watches:对由每个真实用户ID创建的监控项数量的限制值。

3个文件的典型默认值分别为16384、128和8192。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值