• 监控文件事件


    一、概述

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

    使用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
    int inotify_init(void);
    //返回一个文件描述符用于在后续操作中指代inotigy实例。
    

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

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

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

    #include
    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 
    #include 
    
    //显示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。

  • 相关阅读:
    LeetCode 第 388 场周赛个人题解
    quarkus实战之七:使用配置
    反射_集合,,220817,,
    强化学习-学习笔记1 | 基础概念
    三分靠策略 七分靠执行
    RabbitMQ【RabbitMQ】
    【技术积累】HTML+CSS+JavaScript中的基础知识【二】
    《最新出炉》系列入门篇-Python+Playwright自动化测试-46-鼠标滚轮操作
    linux驱动开发day6--(epoll实现IO多路复用、信号驱动IO、设备树以及节点和属性解析相关API使用)
    栈和队列(c++)
  • 原文地址:https://blog.csdn.net/weixin_50866517/article/details/127039668