inotify 是 Linux 内核提供的一种高效文件系统事件监控机制,允许应用程序实时监控文件或目录的变化(如创建、修改、删除等),而无需轮询(polling)。当监控一个目录时,inotify 会返回该目录本身及其内部文件的事件。
函数介绍
int inotify_init(void);
int inotify_init1(int flags);//较新的API
描述:
用于创建一个 inotify 实例,并返回一个引用该实例的文件描述符。如果 flags 参数为 0,则 inotify_init1() 的行为与 inotify_init() 完全相同。
参数:
flags值如下(可以按位或组合):
IN_NONBLOCK:非阻塞
IN_CLOEXEC: 设置执行时关闭close-on-exec (FD_CLOEXEC)标志;指定此标志可避免程序额外调用 fcntl的 F_SETFD 操作来设置 FD_CLOEXEC 标志。
返回值:
inotify 实例的文件描述符
int inotify_add_watch(int fd, const char *pathname, uint32_t mask);
描述:
指定路径(pathname)的文件或目录添加一个新的监视项(watch),或修改一个已存在的监视项。调用此函数的进程必须对该文件具有读权限。
参数:
fd : inotify_init返回的文件描述符
pathname:要添加的文件或目录路径
mask:掩码,指定要监视的事件类型
mask取值(按位或):
IN_ACCESS //文件被读取(如 read()、execve())。
IN_MODIFY //文件内容被修改(如 write()、truncate())。
IN_OPEN //文件被打开。
IN_CLOSE_WRITE //以写入方式打开的文件被关闭。
IN_CLOSE_NOWRITE //以非写入方式打开的文件被关闭。
IN_ATTRIB //文件元数据被修改(如权限、时间戳、扩展属性、链接数、UID、GID 等)。
IN_CREATE //在监视的目录中创建文件或目录。
IN_DELETE //从监视的目录中删除文件或目录。
IN_DELETE_SELF //被监视的文件或目录本身被删除。
IN_MOVED_FROM //文件被移出被监视的目录。
IN_MOVED_TO //文件被移入被监视的目录。
IN_MOVE_SELF //被监视的文件或目录本身被移动或重命名。
IN_ONLYDIR //仅当 pathname 是目录时才允许监视。
IN_DONT_FOLLOW //不跟随符号链接(如果 pathname 是符号链接,则不解析其指向的目标)。
IN_EXCL_UNLINK //不为已取消链接的文件生成事件(避免监视已被删除但仍有进程引用的文件)。
IN_MASK_ADD //如果监视项已存在,则将新事件添加到现有掩码中,而不是替换原有掩码。
IN_ONESHOT //只监视一次事件,事件发生后自动移除监视。
IN_ALL_EVENTS //监视所有事件(通常为上述事件的按位或组合)。
IN_MOVE //等价于 IN_MOVED_FROM | IN_MOVED_TO。
IN_CLOSE //等价于 IN_CLOSE_WRITE | IN_CLOSE_NOWRITE。
返回值:
监视描述符(可能新分配或复用已有描述符);
监视的 文件系统对象(文件或目录)未被当前 fd 关联的 inotify 实例监视过 时,内核会为其分配一个新的 wd。
监视的 文件系统对象已被当前 fd 关联的 inotify 实例监视过 时,内核会复用已存在的 wd,而非分配新值。
示例代码:
int fd = inotify_init(); // 初始化 inotify 实例
int wd = inotify_add_watch(fd, "/home/user/docs", IN_CREATE); // 添加监视项
if (wd == -1) {
perror("inotify_add_watch");
exit(EXIT_FAILURE);
}
fd:inotify 实例的全局标识符,整个 inotify 实例(可管理多个监视项)
wd:监视项的局部标识符,单个监视项(如某个文件或目录)
int inotify_rm_watch(int fd, int wd);
描述:
从与文件描述符 fd 关联的 inotify 实例中移除与监视描述符 wd 对应的监视项。
参数:
fd:inotify 实例
wd:监视项
返回值:
成功:0
失败:-1,并且设置errno变量,通过perror获取
信息结构
事件触发后发送的消息格式:struct inotify_event:
#include <sys/inotify.h>
struct inotify_event {
int wd; // 监视描述符(标识哪个监视项触发事件)
uint32_t mask; // 事件掩码(标识触发的事件类型)
uint32_t cookie; // 用于关联事件的唯一标识(通常用于重命名事件)
uint32_t len; // 事件附带数据的长度(如文件名长度)
char name[]; // 可选:文件名(仅当事件涉及目录内文件时存在)
};
事件触发
可以通过两种方法来处理事件触发:
1.使用select/epoll等来监视内核通知(监视inotify_init返回的文件描述符fd);
2.使用read接口主动读取(读取inotify_init返回的文件描述符fd));
示例代码(read为例):
char buffer[4096];
ssize_t len;
while (1) {
len = read(fd, buffer, sizeof(buffer));
if (len == -1) {
perror("read");
break;
}
// 解析事件
struct inotify_event *event = (struct inotify_event *)buffer;
while ((char *)event < buffer + len) {
if (event->mask & IN_MODIFY) {
printf("File modified. WD=%d, Path=%s\n", event->wd, event->name);
}
if (event->mask & IN_DELETE) {
printf("File deleted. WD=%d, Path=%s\n", event->wd, event->name);
}
}
}