在Linux中,inotify API提供了一种机制来监控文件系统的事件信息。Inotify可以监控文件亦或者是目录。当一个目录被监控时,inotify会返回这个目录本身所发生的事件以及目录内的文件所发生的事件信息。
常用的API:
想要知道被监控的文件或目录发生了什么,需要用read()函数读取inotify文件描述符。如果在read()函数被调用时尚未发生任何事件,那么read()函数将会一直阻塞等待事件的发生,除非收到了中断信号。read()函数每一次成功的调用,都会返回一个包含着监控事件的结构体:
struct inotify_event {
int wd; /* Watch descriptor */
uint32_t mask; /* Mask describing event */
uint32_t cookie; /* Unique cookie associating related events (for rename(2)) */
uint32_t len; /* Size of name field */
char name[]; /* Optional null-terminated name */
};
wd
即为监控描述符独一无二的编号mask
包含着描述事件类型的位信息,下面还会介绍。cookie
是连接着相关联的事件的独一无二的一个整数。目前来说,这只能用于重命名事件,允许应用程序连接IN_MOVED_FROM
和IN_MOVED_TO
事件的结果集。除了这两个事件之外,其他的事件的cookie
都被设置为0。name
字段只会在监控一个目录的事件时才会被提供,如果只是监控一个文件,这个字段将为空。len
字段记录着name
字段的长度。每一个inotify_event
结构体的长度是sizeof(struct
inotify_event)+len
,不要以为len
就是inotify_event
结构体的长度。当读取的buffer大小不足时,返回值会根据内核的不同而不同。内核2.6.21以前会返回0,从2.6.21以后会返回EINVAL
。sizeof(struct inotify_event) + NAME_MAX + 1
能够保证至少读取一个事件的信息。
IN_ACCESS
:文件被访问IN_ATTRIB
:元数据被更改。比如访问权限、访问时间等等。IN_CLOSE_WRITE
:已写入的文件被关闭。(原文:File opened for writing was closed.)(可写文件被关闭)IN_CLOSE_NOWRITE
:未写入的文件或目录被关闭(原文:File or directory not opened for writing was closed)(文件不可写或目录已经关闭)IN_CREATE
:文件或目录被创建IN_DELETE
:文件或目录从监控的目录中删除IN_DELETE_SELF
:被监控的目录自己被删除IN_MODIFY
:文件被修改IN_MOVE_SELF
:监控的目录自己被移动IN_MOVED_FROM
:Generated for the directory containing the old filename when a file is renamedIN_MOVED_TO
:Generated for the directory containing the new filename when a file is renamedIN_OPEN
:文件或目录被打开IN_MOVE
:相当于IN_MOVED_FROM | IN_MOVED_TO
IN_CLOSE
:相当于IN_CLOSE_WRITE | IN_CLOSE_NOWRITE
IN_DONT_FOLLOW
(since Linux 2.6.15):如果是符号链接,不要取消引用路径名IN_EXCL_UNLINK
(since Linux 2.6.36):By default, when watching events on the children of a directory, events are generated for children even after they have been unlinked from the directory. This can result in large numbers of uninteresting events for some applications (e.g.,if watching /tmp, in which many applications create temporary files whose names are immediately unlinked). Specifying IN_EXCL_UNLINK
changes the default behavior, so that events are not generated for children after they have been unlinked from the watched directory.IN_MASK_ADD
:对于某个路径名,如果监控实例已经存在,则将mask
中的事件添加到监控项目的mask
中,而不是直接替换mask
。IN_ONESHOT
:监控相应的路径,获得一次事件后就从监控列表里移除自己。IN_ONLYDIR
(since Linux 2.6.15):只监控目录。下列的mask
标志位可能被read()
函数设置的值:
- IN_IGNORED
:监控明确的被inotify_rm_watch()
函数移除又或者文件已经被删除、文件系统未挂载。
- IN_ISDIR
:这个事件的主题是一个目录.
- IN_Q_OVERFLOW
:事件队列溢出了。(wd
将被置为-1)
- IN_UNMOUNT
:被监控的文件系统未挂载。
fd = open("dir/myfile", O_RDWR);
Generates IN_OPEN events for both dir and dir/myfile.
read(fd, buf, count);
Generates IN_ACCESS events for both dir and dir/myfile.
write(fd, buf, count);
Generates IN_MODIFY events for both dir and dir/myfile.
fchmod(fd, mode);
Generates IN_ATTRIB events for both dir and dir/myfile.
close(fd);
Generates IN_CLOSE_WRITE events for both dir and dir/myfile.
link("dir1/myfile", "dir2/new");
Generates an IN_ATTRIB event for myfile and an `IN_CREATE` event for dir2.
rename("dir1/myfile", "dir2/myfile");
Generates an `IN_MOVED_FROM` event for dir1, an `IN_MOVED_TO` event for
dir2, and an `IN_MOVE_SELF` event for myfile. The `IN_MOVED_FROM` and
`IN_MOVED_TO` events will have the same cookie value.
unlink("dir2/yy");
Generates an IN_ATTRIB event for xx (because its link count
changes) and an IN_DELETE event for dir2.
unlink("dir1/xx");
Generates IN_ATTRIB, IN_DELETE_SELF, and IN_IGNORED events
for xx, and an IN_DELETE event for dir1.
mkdir("dir/new", mode);
Generates an IN_CREATE | IN_ISDIR event for dir.
rmdir("dir/subdir");
Generates IN_DELETE_SELF and IN_IGNORED events for subdir,
and an IN_DELETE | IN_ISDIR event for dir.
poll函数原型
NAME
poll, ppoll - wait for some event on a file descriptor
SYNOPSIS
#include
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include
#include
int ppoll(struct pollfd *fds, nfds_t nfds,
const struct timespec *tmo_p, const sigset_t *sigmask);
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};
read()函数原型
NAME
read - read from a file descriptor
SYNOPSIS
#include
ssize_t read(int fd, void *buf, size_t count);
inotify_add_watch()函数原型
NAME
inotify_add_watch - add a watch to an initialized inotify instance
SYNOPSIS
#include
int inotify_add_watch(int fd, const char *pathname, uint32_t mask);
RETURN VALUE
On success, inotify_add_watch() returns a nonnegative watch descriptor.
On error, -1 is returned and errno is set appropriately.
inotify_init()函数原型
NAME
inotify_init, inotify_init1 - initialize an inotify instance
SYNOPSIS
#include
int inotify_init(void);
int inotify_init1(int flags);
DESCRIPTION
For an overview of the inotify API, see inotify(7).
inotify_init() initializes a new inotify instance and returns a file
descriptor associated with a new inotify event queue.
If flags is 0, then inotify_init1() is the same as inotify_init(). The
following values can be bitwise ORed in flags to obtain different
behavior:
IN_NONBLOCK Set the O_NONBLOCK file status flag on the new open file
description. Using this flag saves extra calls to fcntl(2)
to achieve the same result.
IN_CLOEXEC Set the close-on-exec (FD_CLOEXEC) flag on the new file
descriptor. See the description of the O_CLOEXEC flag in
open(2) for reasons why this may be useful.
RETURN VALUE
On success, these system calls return a new file descriptor. On error,
-1 is returned, and errno is set to indicate the error.
inotify_rm_watch()函数原型
NAME
inotify_rm_watch - remove an existing watch from an inotify instance
SYNOPSIS
#include
int inotify_rm_watch(int fd, int wd);
测试程序
#include
#include
#include
#include
#include
#include
/*fd - 文件描述符
wd - 参数argv里的所有目录的监控描述符表
argc - 参数argv的个数
argv - 目录列表
*/
static void
handle_events(int fd, int *wd, int argc, char *argv[])
{
char buf[4096];
const struct inotify_event *event;
int i;
ssize_t len;
char *ptr;
/*循环读取事件*/
for(;;) {
len = read(fd, buf, sizeof buf);
if (len == -1 && errno != EAGAIN) {
perror("read");
exit(EXIT_FAILURE);
}
/*当非阻塞read()发现没有事件可读取,会返回-1且设置errno为EAGAIN,此时退出循环*/
if (len < 0)
break;
/*读取在buffer中的事件并处理*/
for (ptr = buf; ptr < buf + len;
ptr += sizeof(struct inotify_event) + event->len) {
event = (const struct inotify_event *) ptr;
/*打印事件名字*/
if (event->mask & IN_OPEN)
printf("IN_OPEN: ");
if (event->mask & IN_CLOSE_NOWRITE)
printf("IN_CLOSE_NOWRITE: ");
if (event->mask & IN_CLOSE_WRITE)
printf("IN_CLOSE_WRITE: ");
/*打印被监测的目录名字*/
for (i = 1; i < argc; ++i) {
if (wd[i] == event->wd) {
printf("%s/", argv[i]);
break;
}
}
/*打印文件名*/
if (event->len)
printf("%s", event->name);
if (event->mask & IN_ISDIR)
printf(" [directory] \n");
else
printf(" [file] \n");
}
}
}
int main(int argc, char* argv[]) {
char buf;
int fd, i, poll_num;
int *wd;
nfds_t nfds;
struct pollfd fds[2];
if (argc < 2) {
printf("Usage: %s PATH [PATH ...] \n", argv[0]);
exit(EXIT_FAILURE);
}
printf("Press ENTER key to terminate.\n");
/*非阻塞调用*/
fd = inotify_init1(IN_NONBLOCK);
if (fd == -1) {
perror("inotify_init1");
exit(EXIT_FAILURE);
}
/*为wd申请内存*/
wd = calloc(argc, sizeof(int));
if (wd == NULL) {
perror("calloc");
exit(EXIT_FAILURE);
}
/*监控事件IN_OPEN 、 IN_CLOSE*/
for (i = 1; i < argc; i++) {
wd[i] = inotify_add_watch(fd, argv[i], IN_OPEN | IN_CLOSE);
if (wd[i] == -1) {
fprintf(stderr, "Cannot watch '%s'\n", argv[i]);
perror("inotify_add_watch");
exit(EXIT_FAILURE);
}
}
/*为polling做准备*/
nfds = 2;
fds[0].fd = STDIN_FILENO;//控制台输入
fds[0].events = POLLIN;
fds[1].fd = fd;//inotify输入
fds[1].events = POLLIN;
/*等待事件的发生或者控制台的输入*/
printf("Listening for events.\n");
while(1) {
poll_num = poll(fds, nfds, -1);//-1使poll()一直挂起直到一个指定事件发生
if (poll_num == -1) {
if (errno == EINTR)//忽略读写时的中断
continue;
perror("poll");
exit(EXIT_FAILURE);
}
if (poll_num > 0) {
if (fds[0].revents & POLLIN) {
while(read(STDIN_FILENO, &buf, 1) > 0 && buf != '\n')
continue;
break;
}
if (fds[1].revents & POLLIN) {
handle_events(fd, wd, argc, argv);
}
}
}
printf("Listening for evnets stopped.\n");
close(fd);
free(wd);
exit(EXIT_SUCCESS);
}