首页 > 系统相关 >Linux 使用 inotify 监控文件或目录变化

Linux 使用 inotify 监控文件或目录变化

时间:2024-05-05 11:33:05浏览次数:23  
标签:文件 inotify int 事件 监控 Linux event

转载:https://www.cnblogs.com/PikapBai/p/14480881.html

作者:PikapBai

 

1 运行环境#

  • 操作系统:Ubuntu 18

2 inotify 简介#

  • inotify 是一个 Linux 内核特性(监视文件系统事件),它用于监控文件系统,比如删除、读、写操作等,当发生对应事件时,则会触发 inotify。当监控目录时,与该目录自身以及该目录下面的文件都会被监控,其上有事件发生时都会通知给应用程序

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

  • 使用 inotify:创建一个文件描述符,附加一个或多个监视器(一个监视器 是一个路径和一组事件),然后使用 read() 方法从描述符获取事件信息。read() 并不会用光整个周期,它在事件发生之前是被阻塞的。

  • 因为 inotify 通过传统的文件描述符工作,可使用 select(),poll(),epoll() 以及由信号驱动的 I/O 来监控 inotify 文件描述符

  • 要使用 inotify,必须具备一台带有 2.6.13 或更新内核的 Linux 机器(以前的 Linux 内核版本使用更低级的文件监控器 dnotify)。如果您不知道内核的版本,请转到 shell,输入 uname -a

3 inotify API#

3.1 inotify_init#

创建一个 inotify 实例并返回一个引用 inotify 实例的文件描述符

函数原型

#include<sys/inotify.h> 
 
int inotify_init(void);  

返回值

  • 成功:该函数的返回值为一个文件描述符,该文件描述符所指代的文件中将会保存所监控的 文件/目录 所发生的 事件集

  • 失败:返回 -1,并且将 errno 设置为对应错误。

使用及解释

int fd = inotify_init();

fd 为所指的 inotify 实例的 监控列表,系统调用 inotify_add_watch() 可以向该 fd 追加 新的监控项

3.2 inotify_add_watch#

针对 fd 所指的 inotify 实例的 监控列表 追加 新的监控项

函数原型

#include<sys/inotify.h> 

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

返回值

  • 成功:返回值为一个用于 唯一指代此 监控项 的描述符

  • 失败:返回值 < 0 ,则代表添加该监控项失败,需要检测 pathname 是否有可读权限,是否存在,系统的监控队列是否已满等

参数

  • pathname 为想要创建的监控项所对应的文件,特别注意调用该接口必须要对该文件有读权限,该函数只对文件做一次检查,如果在监控时修改了所监控的文件读权限,则不会影响继续监控此文件

  • mask 为一位掩码,针对 pathname 定义了想要监控的事件,此函数的返回值为一个用于唯一指代此监控项的描述符(将在 4 inotify 事件 中介绍)

4 inotify 常用监控事件#

  • IN_ACCESS:文件 被访问时 触发事件,例如 read,execve

  • IN_ATTRIB:文件属性 发生变化 触发事件。例如 权限 chmod,时间戳 setxattr,链接数 link 等

  • IN_CLOSE_WRITE:一个文件被打开 写入操作结束,文件被关闭时 触发事件

  • IN_CLOSE_NOWRITE:一个文件被打开 没有任何写操作,文件被关闭时 触发事件

  • IN_CREATE:在监控列表下 创建一个文件或目录 时 触发事件,例如 open O_CREAT,mkdir 等

  • IN_DELETE:在监控列表下 文件或目录 被删除时 触发事件

  • IN_DELETE_SELF:监控文件或目录 本身被删除时 触发事件,而且,如果一个文件或目录被移到其它地方,比如使用 mv 命令,也会触发该事件,因为 mv 命令本质上是拷贝一份当前文件,然后删除当前文件的操作。此时监控终止,并且将收到一个 IN_IGNORED 事件。

  • IN_MODIFY:文件 被修改时 触发事件,例如:有写操作(write)或者文件内容被清空(truncate)操作。不过需要注意的是,IN_MODIFY 可能会连续触发多次。

  • IN_MODIFY_SELF:所监控的文件或目录本身 发生移动时 触发事件

  • IN_MOVED_FROM:将文件或目录 移除 监控列表 触发事件

  • IN_MOVED_TO:将文件或目录 移入 监控列表 触发事件

  • IN_OPEN:文件被打开 触发事件

  • IN_ALL_EVENTS:监控所有事件

  • IN_MOVE:IN_MOVED_FROM | IN_MOVED_TO 事件的统称

5 存储 inotify 事件 结构体 struct inotify_event#

将 监控项 在 监控列表 中登记后,应用程序可以用 read() 从 inotify 的文件描述符 中读取事件以判定发生了那些事件。若读取之时还没有发生任何事件,则 read() 会阻塞,直至有事件产生。事件发生后,每次调用 read() 会返回一个缓存区,内含一个或多个如下类型的结构体:

struct inotify_event 
{  
    int      wd;       // 指向发生事件的监控项的文件描述符,该字段值由之前对 inotify_add_watch() 的调用返回。用于区分是哪个监控项触发了该事件
    uint32_t mask;     // inotify 事件的一位掩码
    uint32_t cookie;   // 唯一的关联 inotify 事件的值 
    uint32_t len;      // 分配给 name 的字节数
    char     name[];   // 标识触发该事件的文件名
}; 

注意:

如果是监控目录,此时目录下的文件触发事件,会输出对应的文件名。但是如果只监控文件,则无法根据 event->name 输出对应更改的文件名,原因参考 7.1 监控文件时,无法根据 event->name 输出对应更改的文件名

6 inotify 示例#

6.1 代码#

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

#define EVENT_NUM  12

const char *event_str[EVENT_NUM] =
{
	"IN_ACCESS",
	"IN_MODIFY",
	"IN_ATTRIB",
	"IN_CLOSE_WRITE",
	"IN_CLOSE_NOWRITE",
	"IN_OPEN",
	"IN_MOVED_FROM",
	"IN_MOVED_TO",
	"IN_CREATE",
	"IN_DELETE",
	"IN_DELETE_SELF",
	"IN_MOVE_SELF"
};


int inotifyTask(char *argv[]) 
{
	int errTimes = 0;

	int fd = -1;

INIT_INOTIFY:
	fd = inotify_init();
	if(fd < 0)
	{
		fprintf(stderr, "inotify_init failed\n");

		printf("Error no.%d: %s\n", errno, strerror(errno));

		goto INOTIFY_FAIL;
	}
 
	int wd1 = -1;
	int wd2 = -1;

	struct inotify_event *event;

	int length;
	int nread;
	
	char buf[BUFSIZ];
		
	int i = 0;

	buf[sizeof(buf) - 1] = 0;

INOTIFY_AGAIN:
	wd1 = inotify_add_watch(fd, argv[1], IN_ALL_EVENTS);
	if(wd1 < 0)
	{
		fprintf(stderr, "inotify_add_watch %s failed\n", argv[1]);

		printf("Error no.%d: %s\n", errno, strerror(errno));

		if(errTimes < 3)
		{			
			goto INOTIFY_AGAIN;
		}
		else
		{
			goto INOTIFY_FAIL;
		}
	}

	wd2 = inotify_add_watch(fd, argv[2], IN_ALL_EVENTS);
	if(wd2 < 0)
	{
		fprintf(stderr, "inotify_add_watch %s failed\n", argv[2]);

		printf("Error no.%d: %s\n", errno, strerror(errno));

		if(errTimes < 3)
		{
			goto INOTIFY_AGAIN;
		}
		else
		{
			goto INOTIFY_FAIL;
		}
	}
	
	length = read(fd, buf, sizeof(buf) - 1);

	nread = 0;

	// inotify 事件发生时
	while(length > 0)
	{
		printf("\n");
		
		event = (struct inotify_event *)&buf[nread];

		// 遍历所有事件
		for(i = 0; i< EVENT_NUM; i++)
		{			
			// 判断事件是否发生
			if( (event->mask >> i) & 1 )
			{	
				// 该监控项为目录或目录下的文件时
				if(event->len > 0)
				{
					fprintf(stdout, "%s --- %s\n", event->name, event_str[i]);
				}
				// 该监控项为文件时
				else if(event->len == 0)
				{
					if(event->wd == wd1)
					{
						fprintf(stdout, "%s --- %s\n", argv[1], event_str[i]);
					}
					if(event->wd == wd2)
					{
						fprintf(stdout, "%s --- %s\n", argv[2], event_str[i]);
					}
				}
			}
		}
		
		nread = nread + sizeof(struct inotify_event) + event->len;
		
		length = length - sizeof(struct inotify_event) - event->len;
	}

	goto INOTIFY_AGAIN;

	close(fd);

	return 0;

INOTIFY_FAIL:
	return -1;
}

int main(int argc, char *argv[])
{	
	if(argc < 3)
	{
		fprintf(stderr, "Usage: %s path path\n", argv[0]);
		
		return -1;
	}

	if(inotifyTask(argv) == -1)
	{
		return -1;
	}
		
	return 0;
}

6.2 编译#

编译命令:

gcc inotify.c -o out

如下图所示:

6.3 运行截图#

6.3.1 不加任何参数#

此时会提示信息,需要输入两个路径用于监控,如下图所示:

6.3.2 监控两个文件#

监控 /etc/a,/etc/b

如下图所示:

6.3.3 监控两个目录#

监控 /etc,/tmp

如下图所示:

7 inotify 监控文件时常见问题#

7.1 监控文件时,无法根据 event->name 输出对应更改的文件名#

原因:

在 linux 手册中关于 inotify 的描述有对应解释。 如果是监控目录,此时目录下的文件触发事件,会输出对应的文件名。但是如果只监控文件,则无法输出对应更改的文件名。如下图所示:

7.2 监控文件时,无法持续监控,第二次更改文件时,它没有响应#

原因:

这是由于 vim 的工作机制引起的,vim 会先将源文件复制为另一个文件,然后在另一文件基础上编辑(后缀名为 swp),保存的时候再将这个文件覆盖源文件。此时原来的文件已经被后来的新文件代替,因此监视对象所监视的文件已经不存在了,所以自然不会产生任何事件。

解决方法:

重新使用 inotify_add_watch,将该文件加入监控队列。

标签:文件,inotify,int,事件,监控,Linux,event
From: https://www.cnblogs.com/linxisuo/p/18173322

相关文章

  • Nexpose v6.6.248 for Linux & Windows - 漏洞扫描
    Nexposev6.6.248forLinux&Windows-漏洞扫描Rapid7VulnerabilityManagement,ReleaseApr24,2024请访问原文链接:Nexposev6.6.248forLinux&Windows-漏洞扫描,查看最新版。原创作品,转载请保留出处。作者主页:sysin.org您的本地漏洞扫描程序搜集通过实时......
  • Nessus 10.7 Auto Installer for RHEL 9/AlmaLinux 9/Rocky Linux 9 (updated Apr 202
    Nessus10.7AutoInstallerforRHEL9/AlmaLinux9/RockyLinux9(updatedApr2024)发布Nessus试用版自动化安装程序,支持macOSSonoma、RHEL9和Ubuntu22.04请访问原文链接:https://sysin.org/blog/nessus-auto-install-for-rhel-9/,查看最新版。原创作品,转载请保留出处......
  • 在Linux中,什么是冷备份和热备份?
    在数据备份领域,冷备份(ColdBackup)和热备份(HotBackup)是两种不同的备份策略,它们根据系统是否运行来区分:1.冷备份(ColdBackup)冷备份是指在系统不运行或者关闭状态下进行的数据备份。在这种情况下,没有数据被修改或访问,因此可以确保备份的数据是一致的。特点:简单:不需要特殊的软......
  • 在Linux中,如何进行日志审计?
    在Linux系统中,日志审计是确保系统安全性的重要部分,它可以帮助系统管理员监控和记录系统中发生的各种事件。以下是进行日志审计的步骤和一些常用的工具:1.启用系统审计Linux系统中的审计系统通常由auditd守护进程提供。首先,确保auditd已经安装并启动:sudoapt-getinstallauditd......
  • 在Linux中,如何恢复备份的文件?
    在Linux中恢复备份的文件通常取决于你使用的备份工具和备份的类型。以下是一些常见的备份工具和它们恢复文件的基本步骤:tar使用tar命令创建的备份通常是一个压缩的文件,可以使用以下命令来恢复:tar-xvfbackup.tar如果备份文件是使用gzip压缩的,可以使用:tar-zxvfbacku......
  • 在Linux中,如何创建文件系统的备份?
    在Linux中创建文件系统备份的方法有很多,这里介绍几种常见的方法:1.使用tar命令tar(tapearchive)是一个常用的归档工具,可以用来创建文件和目录的备份。创建备份:sudotar-czvf/path/to/backup.tar.gz/path/to/directoryc代表创建归档。z代表用gzip压缩归档。v代表在......
  • 在Linux中,什么是快照备份?
    以下是关于快照备份的一些详细解释:1.文件系统快照原理:文件系统快照通过捕获文件系统在某个时间点的状态来工作。这通常是通过复制文件系统的元数据(如inode和目录结构)和正在使用的数据块来完成的,同时保持对原始文件系统的写操作,这样写操作会在快照之后继续发生在原始文件系统的......
  • OpenSBI介绍,OpenSBI启动,及其和Linux交互
    基于OpenSBI1.2分析。 1OpenSBI介绍1.1RISC-V基础RISC-VCore支持三种Mode:UserMode、SupervisorMode、MachineMode:ABI:ApplicationBinaryInterface;SBI:SupervisorBinaryInterface;SEE:SupervisorExecutionEnvironment。如果支持虚拟化,还包括HypervisorMode:......
  • Linux网络设置
    Linux网络设置目录Linux网络设置一、网络配置命令1、查看网络接口信息——ifconfig1.1格式1.2字段分析1.3实例临时1.3.1ifconfig1.3.2ifconfig网卡名1.3.3ifconfig网卡名up/down开启和关闭1.3.4ifconfig网卡名:0ip/子网掩码 设置临时网卡1.3.5ifconfig-a 显示......
  • linux的yum软件包管理器
    一、yum基于RPM包管理,能够从指定的服务器自动下载RPM包并且安装,可以处理依赖性关系,并且一次安装所有依赖的软件包,无需繁琐下载多个文件。yum与yum镜像源密切相关,yum镜像源可以理解为一个在线的镜像软件仓库,你所需要下载的软件需要在里面有。yuminstallxxx*安装yumremove......