Linux文件系统事件监控框架--inotify

[转]http://www.infoq.com/cn/articles/inotify-linux-file-system-event-monitoring
概要 - 为什么需要监控文件系统?在日常工作中,人们往往需要知道在某些文件(夹)上都有那些变化,比如:
[list]
[]通知配置文件的改变
[
]跟踪某些关键的系统文件的变化
[]监控某个分区磁盘的整体使用情况
[
]系统崩溃时进行自动清理
[]自动触发备份进程
[
]向服务器上传文件结束时发出通知
[/list]通常使用文件轮询的通知机制,但是这种机制只适用于经常改变的文件(因为它可以确保每过x秒就可以得到i/o),其他情况下都非常低效,并且有时候会丢失某些类型的变化,例如文件的修改时间没有改变。像Tripwire这样的数据完整性系统,它们基于时间调度来跟踪文件变化,但是如果想实时监控文件的变化的话,那么时间调度就束手无策了。Inotify就这样应运而生了。本文将简要介绍inotify,告诉我们如何监控文件夹,如何一有变化就报告相关消息事件,并介绍了一些相关工具, 我们可以把它们添加到自己的工具箱中。
Inotify到底是什么?Inotify是一种文件变化通知机制,Linux内核从2.6.13开始引入。在BSD和Mac OS系统中比较有名的是kqueue,它可以高效地实时跟踪Linux文件系统的变化。近些年来,以fsnotify作为后端,几乎所有的主流Linux发行版都支持Inotify机制。如何知道你的Linux内核是否支持Inotify机制呢?很简单,执行下面这条命令:

% grep INOTIFY_USER /boot/config-$(uname -r)
CONFIG_INOTIFY_USER=y

如果输出(‘CONFIG_INOTIFY_USER=y’),那么你可以马上享受Inotify之旅了。简单的文件变化通知样例:好的开始是成功的一半,对于了解Inotify机制来说,让我们从使用inotifywait程序开始,它包含在inotify-tools工具包中。假如我们打算监控/srv/test文件夹上的操作,只需执行:

% inotifywait -rme modify,attrib,move,close_write,create,delete,delete_self /srv/test
Setting up watches.  Beware: since -r was given, this may take a while!
Watches established.

上述任务运行的同时,我们在另一个shell里依次执行以下操作:创建文件夹,然后在新文件夹下创建文件,接着删除新创建的文件:

% mkdir /srv/test/infoq
% echo TODO > /srv/test/infoq/article.txt
% rm /srv/test/infoq/article.txt


在运行inotifywait的shell中将会打印以下信息:

/srv/test/ CREATE,ISDIR infoq
/srv/test/infoq/ CREATE article.txt
/srv/test/infoq/ MODIFY article.txt
/srv/test/infoq/ CLOSE_WRITE,CLOSE article.txt
/srv/test/infoq/ DELETE article.txt

显而易见,只要有变化我们就会收到相关的通知。如果想了解关于Inotify提供的事件(如modify, atrrib等)的详细信息,请参考inotifywatch的manpage。实际使用时,如果并不想监控某个大文件夹,那么就可以使用inotifywait的exclude选项。例如:我们要忽略文件夹/srv/test/large,那么就可以这样来建立监控:

% inotifywait --exclude '^/srv/test/(large|ignore)/' -rme modify,attrib,move,close_write,create,delete,delete_self /srv/test
Setting up watches.  Beware: since -r was given, this may take a while!
Watches established.

上面的例子中,在exclude选项的匹配串中我们使用了正则表达式,因为我们不想将名称中含有large或ignore的文件也排除掉。我们可以测试一下:

% echo test > /srv/test/action.txt
% echo test > /srv/test/large/no_action.txt
% echo test > /srv/test/ignore/no_action.txt
% echo test > /srv/test/large-name-but-action.txt

这里inotifywait应该只会报告’action.txt’和’large-name-but-action.txt’两个文件的变化,而忽略子文件夹’large’和’ignore’下的文件,结果也确实如此;

/srv/test/ CREATE action.txt
/srv/test/ MODIFY action.txt
/srv/test/ CLOSE_WRITE,CLOSE action.txt
/srv/test/ CREATE large-name-but-action.txt
/srv/test/ MODIFY large-name-but-action.txt
/srv/test/ CLOSE_WRITE,CLOSE large-name-but-action.txt

另外,通过使用-t选项我们还可以定义inotifywait的监控时间,既可以让它执行一段时间,也可以让它一直运行。util-linux-ng的logger命令也可以实现此功能,不过得先把相关的消息事件发送到syslog,然后从syslog服务器再分析整合。Inotifywatch - 使用inotify来统计文件系统访问信息Inotify-tools中还有一个工具叫inotifywatch,它会先监听文件系统的消息事件,然后统计每个被监听文件或文件夹的消息事件,之后输出统计信息。比如我们想知道某个文件夹上有那些操作:

% inotifywatch -v -e access -e modify -t 120 -r ~/InfoQ
Establishing watches...
Setting up watch(es) on /home/mika/InfoQ
OK, /home/mika/InfoQ is now being watched.
Total of 58 watches.
Finished establishing watches, now collecting statistics.
Will listen for events for 120 seconds.
total  modify  filename
2      2       /home/mika/InfoQ/inotify/

很显然,这里我们监控的是~/InfoQ文件夹,并且可以看到/home/mika/InfoQ/inotify上发生了两个事件。方法虽简单,但却很有效。Inotify的配置选项使用Inotify时,要特别注意内核中关于它的两个配置。首先/proc/sys/fs/inotify/max_user_instances 规定了每个用户所能创建的Inotify实例的上限;其次/proc/sys/fs/inotify/max_user_watches规定了每个Inotify实例最多能关联几个监控(watch)。你可以很容易地试验在运行过程中达到上限,如:

% inotifywait -r /
Setting up watches.  Beware: since -r was given, this may take a while!
Failed to watch /; upper limit on inotify watches reached!
Please increase the amount of inotify watches allowed per user via `/proc/sys/fs/inotify/max_user_watches'.

如果要改变这些配置,只要向相应的文件写入新值即可,如下所示:

# cat /proc/sys/fs/inotify/max_user_watches
8192
# echo 16000 > /proc/sys/fs/inotify/max_user_watches
# cat /proc/sys/fs/inotify/max_user_watches
16000

使用Inotify的一些工具近一段时间出现了很多基于Inotify的超炫的工具,如incron,它是一个类似于cron的守护进程(daemon),传统的cron守护进程都是在规定的某个时间段内执行,而incron由于使用了Inotify,可以由事件触发执行。同时incron的安装简单而直观,比如在debian上,首先在/etc/incron.allow中添加使用incron的用户(debian默认不允许用户使用incron,因为如果incron使用不慎的话,例如形成死循环,则会导致系统宕机):

# echo username > /etc/incron.allow

然后调用”incrontab -e“, 在弹出的编辑器中插入我们自己的规则,例如下面的这条简单的规则,文件一变化incron就会发邮件通知我们:

/srv/test/ IN_CLOSE_WRITE mail -s "$@/$#\n" root

从现在开始,一旦/src/test文件夹中的文件被修改,就会发送一封邮件。但是注意不要让incron监控整个子目录树,因为Inotify只关注inodes,而不在乎是文件还是文件夹,所以基于Inotify的软件都需要自己来处理/预防递归问题。关于incontab详细使用,请参考incrontab的manpage。
如果你还要处理incoming文件夹,那么你可能需要inoticoming。当有文件进入incoming文件夹时Inoticoming就会执行某些动作,从而可以用inoticoming来管理debian的软件仓库(例如软件仓库中一旦有上传源码包或是新添加的二进制包就立刻自动进行编译),另外,还可以用它来监控系统是否有新上传文件,如果有就发送通知。类似的工具还有(它们都各有专长):inosync(基于消息通知机制的文件夹同步服务),iwatch(基于Inotify的程序,对文件系统进行实时监控),以及lsyncd(一个守护进程(daemon),使用rsync同步本地文件夹)。
Inotify甚至对传统的Unix工具也进行了改进,例如tail。使用inotail,同时加上-f选项,就可以取代每秒轮询文件的做法。此外,GNU 的coreutils从版本7.5开始也支持Inotify了,我们可以运行下面的命令来确认:

# strace -e inotify_init,inotify_add_watch tail -f ~log/syslog
[...]
inotify_init()                          = 4
inotify_add_watch(4, "/var/log/syslog", IN_MODIFY|IN_ATTRIB|IN_DELETE_SELF|IN_MOVE_SELF) = 1

从现在开始,通过轮询来确实文件是否需要重新读取的方法应该作为古董了。
在脚本中使用InotifyInotify机制并不局限于工具,在脚本语言中也完全可以享受Inotify的乐趣,如Python中可以使用pyinotifyinotifyx,Perl中有Filesys-Notify-SimpleLinux-Inotify2,Inotify的Ruby版有ruby-inotifyrb-inotyfssm
总结综上所述,Inotify为Linux提供了一套高效监控和跟踪文件变化的机制,它可以实时地处理、调试以及监控文件变化,而轮询是一种延迟机制。对于系统管理员,关于实现事件驱动的服务如系统备份,构建服务以及基于文件操作的程序调试等,Inotify无疑提供了强大的支持。
查看英文原文:Inotify: Efficient, Real-Time Linux File System Event Monitoring

如果Linux内核低于2.6.13版本,就需要重新编译内核加入inotify的支持,
也可以用如下方法判断,内核是否支持inotify:
[root@localhost webdata]# uname -r
2.6.18-164.11.1.el5PAE
[root@localhost webdata]# ll /proc/sys/fs/inotify
总计 0
-rw-r–r-- 1 root root 0 04-13 19:56 max_queued_events
-rw-r–r-- 1 root root 0 04-13 19:56 max_user_instances
-rw-r–r-- 1 root root 0 04-13 19:56 max_user_watches
如果有上面三项输出,表示系统已经默认支持inotify。