linux c监控文件事件
某些应用程序需要对文件或目录进行监控,来侦测其发生特定事件。例如当文件加入或移出一目录,图形化文件管理器应能判定此目录是否在其当前显示之列,而守护进程可能也要监控自己的配置文件是否修改。
创建新的inotify实例。#include
int inotify_init(void);
//成功返回文件描述符,错误-1
追加新的监控项#include
int inotify_add_watch(int fd, const char *pathname, uint32_t mask);
//返回监控描述符,失败-1
参数pathname标识欲创建或修改的监控项所对应的文件。调用者必须有读的权限(就算后来权限改了,只要注册上了,后面还是会收到事件)。
如果pathname没有加入到fd监控列表则加入,已加入的则修改。参数mask为掩码,定义了欲监控的事件。
删除由wd指定的监控项,wd是监控描述符,由inotify_add_watch() 返回。删除监控项会为该监控描述符生成 IN_IGNORED 事件。#include
int inotify_rm_watch(int fd, uint32_t wd);
//成功返回0,失败-1
使用inotify_add_watch() 删除或修改监控项时,位掩码参数mask标识了 pathname 要监控的事件。下表 in 列列出可在mask中定义的事件位。
读取inotify事件
事件读取可用read() 读取,默认会阻塞,除非设置了 O_NONBLOCK 标志,若设置了且无事件发生,报错 EAGAIN。
事件发生后,每次调用会返回一个缓冲区,内含一个或多了如下结构,缓冲区大小应至少为 sizeof(struct inotify_event) + NAME_MAX + 1 字节。struct inotify_event {
int wd; /* 发生事件的描述符 */
uint32_t mask; /* Bits describing event that occurred */
uint32_t cookie; /* Cookie for related events (for rename()) */
uint32_t len; /* Size of 'name' field */
char name[]; /* Optional null-terminated filename */
};
上图是一个有序的队列,字段wd表明是哪个监控描述符,当应用程序要监控同一 inotify 文件描述符下多个文件和目录时,字段wd就非常有用,可以用它来判定发生在哪个目录,哪个文件。不过要做到这一点,必须自己维护监控描述符和路径名之间的关系。
mask 返回描述该事件的位掩码。由表 19-1 out列展示可出现于 mask中的位范围。cookie 字段只有在对文件重命名时才会用到。在待重命名目录产生 IN_MOVED_FROM 事件。目标目录产生 IN_MOVED_TO 事件。如果同一目录,cookie相等。
如果受监控的目录发生事件,name字段以空字符结尾来表示该文件。若为本身,不使用name字段,len为0。
事件队列末尾追加新事件时如果和队列当前尾部事件有相同的wd,mask,cookie,mask则会合并。
队列限制和 /proc 文件
对inotify事件做排队处理要消耗内存。所以内核会施加各种限制。超级用户可以配置 /proc/sys/fs/inotify 路径中的3个文件来调整限制。
max_queued_events inotify实例队列事件数上限。超出系统生成 IN_Q_OVERFLOW 事件,并丢弃多余事件。溢出事件 wd 为-1。
max_user_instances 对每个真实用户id创建的inotify实例数的限制值。
max_user_watches 对每个真实用户id创建的监控项数量的限制值。
例子#include
#include
#include /* Type definitions used by many programs */
#include /* Standard I/O functions */
#include /* Prototypes of commonly used library functions,
plus EXIT_SUCCESS and EXIT_FAILURE constants */
#include /* Prototypes for many system calls */
#include /* Declares errno and defines error constants */
#include /* Commonly used string-handling functions */
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];
ssize_t numRead;
char *p;
struct inotify_event *event;
if (argc < 2 || strcmp(argv[1], "--help") == 0){
printf("%s pathname...\n", argv[0]);
return 1;
}
inotifyFd = inotify_init(); /* Create inotify instance */
if (inotifyFd == -1)
perror("inotify_init");
for (j = 1; j < argc; j++) {
wd = inotify_add_watch(inotifyFd, argv[j], IN_ALL_EVENTS);
if (wd == -1)
perror("inotify_add_watch");
printf("Watching %s using wd %d\n", argv[j], wd);
}
for (;;) { /* Read events forever */
numRead = read(inotifyFd, buf, BUF_LEN);
if (numRead == 0){
printf("read() from inotify fd returned 0!");
return 1;
}
if (numRead == -1)
perror("read");
printf("Read %ld bytes from inotify fd\n", (long) numRead);
/* Process all of the events in buffer returned by read() */
for (p = buf; p < buf + numRead; ) {
event = (struct inotify_event *) p;
displayInotifyEvent(event);
p += sizeof(struct inotify_event) + event->len;
}
}
return 0;
}