当前位置: 首页 > news >正文

监控文件事件

监控文件事件

  • 一、概述
    • 二、inotify API
      • 三、inotify事件
      • 四、读取inotify事件
      • 五、运用inotify API
      • 五、队列限制和/proc文件

一、概述

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

使用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。

相关文章:

  • 【Node.js】官网学习笔记
  • 番茄ToDo语句精选
  • 树状数组。 数组修改某个元素的数值/求出前n个元素的和,需要在一百毫秒处理上百万个数字
  • 【操作系统】第一章 计算机系统概述
  • 【Vue】Vue的Mustache插值语法、v-bind指令
  • Android7.1.1系统,Toast的Exception: android.view.WindowManager$BadTokenException解决
  • TiKV 监控指标详解
  • 嵌入式系统开发笔记92:感受开源之美
  • VLC 编译安装 [for android, linux, windows]
  • 字节内部私藏的数据结构与算法刷题笔记,太顶了熬夜刷上头
  • 前端性能优化方法与实战开篇词 开启刻意练习之路,进阶前端性能技术专家
  • 实战java高并发程序设计(第2版)学习(1-3)
  • TiCDC 重要监控指标详解
  • T1063 最大跨度值(信息学一本通C++)
  • JavaSE 一些技巧 03——Stream流常用API
  • .pyc 想到的一些问题
  • 【Linux系统编程】快速查找errno错误码信息
  • C++11: atomic 头文件
  • ECMAScript6(0):ES6简明参考手册
  • ES6系列(二)变量的解构赋值
  • gitlab-ci配置详解(一)
  • magento 货币换算
  • MQ框架的比较
  • MyEclipse 8.0 GA 搭建 Struts2 + Spring2 + Hibernate3 (测试)
  • python docx文档转html页面
  • Python socket服务器端、客户端传送信息
  • vue-router的history模式发布配置
  • 读懂package.json -- 依赖管理
  • 构建工具 - 收藏集 - 掘金
  • 聊聊flink的TableFactory
  • 猫头鹰的深夜翻译:JDK9 NotNullOrElse方法
  • 前端性能优化——回流与重绘
  • 区块链将重新定义世界
  • 通过来模仿稀土掘金个人页面的布局来学习使用CoordinatorLayout
  • 昨天1024程序员节,我故意写了个死循环~
  • #APPINVENTOR学习记录
  • #LLM入门|Prompt#1.7_文本拓展_Expanding
  • (10)ATF MMU转换表
  • (26)4.7 字符函数和字符串函数
  • (第二周)效能测试
  • (经验分享)作为一名普通本科计算机专业学生,我大学四年到底走了多少弯路
  • (蓝桥杯每日一题)love
  • (三)Hyperledger Fabric 1.1安装部署-chaincode测试
  • (十六)串口UART
  • (转)负载均衡,回话保持,cookie
  • (转)可以带来幸福的一本书
  • .bat批处理出现中文乱码的情况
  • .Net 4.0并行库实用性演练
  • .net core IResultFilter 的 OnResultExecuted和OnResultExecuting的区别
  • .NET MVC第五章、模型绑定获取表单数据
  • .NET 材料检测系统崩溃分析
  • .NET 使用 ILMerge 合并多个程序集,避免引入额外的依赖
  • .NET 中选择合适的文件打开模式(CreateNew, Create, Open, OpenOrCreate, Truncate, Append)
  • .Net8 Blazor 尝鲜
  • .Net的DataSet直接与SQL2005交互