一、概述
某些用于程序需要对文件或目录进行监控,已侦测是否发送特定事件。
使用inotify API关键步骤:
-
应用程序使用
inotify_init()
来创建 一inotify
实例,该系统调用所返回的文件描述符用于在后续操作中指代该实例 -
应用程序使用
inotify_add_watch()
向inoify
实例(由步骤 1创建) 的监控列表添加条目,告知内核哪些文件是自己的兴趣所在。每个监控项都包含一个路径名以及相关的位掩码。位掩码针对路径名指明了所要监控的事件集合。作为函数结果,inotify_add_watch()
将返回一监控描述符,用于在后续操作中指代该监控项。(系统调用inotify_rm_watch()执行其逆向操作,将之前添加入inotify实例的监控项移除)。 -
为获得事件通知,应用程序需针对inotify 文件描述符执行read()操作。每次对 read()的成功调用,都会返回一个或多个
inotify_ event
结构, 其中各自记录了处于inoify
实例监控之下的某一路径名所发生的事件。 -
应用程序在结束监控时会关闭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事件
-
将监控项在监控列表中等级后,应用程序可用
read()
从inotify
文件描述符中读取事件,判定发生那些事件。 -
读取时没有任何事件发生,
read
函数会阻塞,直至有事件产生。 -
传递给read()的缓冲区过小,无法容纳下一个
inotify_event
结构,那么read以失败告终,并以EINVAL
错误向应用程序报告这一情况。但应用程序可再次以更大的缓冲区执行read()操作。解决方法:传递给read()的缓冲区至少为sizeof(struct inotify_event)+NAME_MAX+1
字节。NAME_MAX
文件最大长度,加上终止空字符。 -
采用的缓冲区大小如大于最小值,则可自单个read()中读取多个事件,效率极高。
-
文件描述符fd调用
ioctl(fd,FIONREAD,&number)
,会返回其所指定的inotify实例中的当前可读字节数。 -
从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个文件来调整这些限制:
max_queued_enent
:调用inotify_init函数时,使用该值来为新inotify实例队列中的事件数量设置上限。超出上限,系统生成IN_Q_OVERFLOW
事件,丢弃多余的事件。溢出事件的wd字段值为-1。max_user_instances
:对由每个真实用户ID创建的inotify实例数的限制值。max_user_watches
:对由每个真实用户ID创建的监控项数量的限制值。
3个文件的典型默认值分别为16384、128和8192。