1 Overview
最近公司的一个任务须要实时监控文件系统中某个文件的内容变化。因为程序自己由Java编写,所以使用了inotify- java(http://code.google.com/p/inotify-java/)。inotify-java只是对Linux中 inotify相关的内核调用进行了封装,所以在使用inotify-java以前有必要了解一下inotify。
inotify是一种基于inode的文件系统监控机制。从2.6.13-rc3版本起被集成到Linux 内核中,做为dnotify的替代。跟dnotify相比,inotify除了更易于使用以外还有如下主要的优势:java
2 inotify
2.1 interfaces
若是C库支持inotify,那么在C程序中直接#include <sys/inotify.h> 便可。
经过int inotify_init (void)系统调用进行初始化。返回值小于0说明调用失败;不然会在内核中建立一个inotify实例,而且返回对应的文件描述符。该文件描述符用于读 取inotify事件(inotify event),读取的方式既能够是阻塞式,例如read,也能够是非阻塞式,例如select,poll和epoll(java6已经支持epoll,但 是不支持对FileChannel进行select)。
经过int inotify_add_watch (int fd, const char *path, __u32 mask)系统调用添加监控(watch)。参数fd是inotify_init调用返回的文件描述符;参数path是监控对象的路径(文件,目录等); 参数mask是指望获得通知的事件类型的位掩码。inotify能够监控如下类型的事件:opens、closes、reads、writes、 creates、deletes、moves、metadata changes 和 unmounts。能够向一个inotify实例添加多个监控。该系统调用在成功状况下返回一个监控描述符(watch descriptor),它被用来标识不一样的监控。mask参数的可选值以下:node
Event | Description |
IN_ACCESS | File was read from. |
IN_MODIFY | File was written to. |
IN_ATTRIB | File's metadata (inode or xattr) was changed. |
IN_CLOSE_WRITE | File was closed (and was open for writing). |
IN_CLOSE_NOWRITE | File was closed (and was not open for writing). |
IN_OPEN | File was opened. |
IN_MOVED_FROM | File was moved away from watch. |
IN_MOVED_TO | File was moved to watch. |
IN_DELETE | File was deleted. |
IN_DELETE_SELF | The watch itself was deleted. |
IN_CLOSE | IN_CLOSE_WRITE | IN_CLOSE_NOWRITE |
IN_MOVE | IN_MOVED_FROM | IN_MOVED_TO |
IN_ALL_EVENTS | Bitwise OR of all events. |
IN_ONESHOT | One shot support |
假设但愿监控/home/user1/data.txt文件的读取和修改事件,那么可使用IN_ACCESS和IN_MODIFY,例如:cookie
假设但愿只监控/home/user1/data.txt文件的修改事件一次,那么可使用IN_MODIFY 和IN_ONESHOT,例如:并发
经过int inotify_rm_watch (int fd, int wd)系统调用移除监控。参数fd是inotify_init调用返回的文件描述符;参数wd是要被移除的监控描述符。若是调用成功,那么返回0;不然返回负值。
经过int close (int fd)系统调用销毁inotify实例,以及关联的全部监控和未决事件。参数fd是inotify_init调用返回的文件描述符。
2.2 configuration
inotify能够经过procfs和sysctl进行配置。/proc/sys/fs/inotify/目录下有如下三个文件:app
2.3 notifications
inotify的事件是异步通知的,而且在内部进行了排队。可是对事件的读取必须以同步方式进行。若是以read读取,那么该方法一直阻塞到有事件到达,而且一次会读入全部排队中的事件。inotify的通知事件由inotify_event结构体定义,以下:异步
其中wd是监控描述符,跟调用inotify_add_watch()时返回的监控描述符对应。若是应用程序想知道与之对应的文件,那么应用程序自己须要 创建监控描述符和文件之间的对应关系;mask是事件的位掩码;cookie用于关联两个独立的事件(例如IN_MOVED_FROM和 IN_MOVED_TO事件);len是name的长度;name是发生事件的对象名,若是监控的是目录,那么name是发生事件的文件名。若是监控的是 文件,那么name是null。
若是被监控的目录或者文件被unmount卸载,那么inotify会发送IN_UNMOUNT。假设监控对象是个目录,若是将被监控目录中某个文件移动 到被监控目录外,那么inotify会发送IN_MOVED_FROM事件;若是将被监控目录外的某个文件移动到被监控目录中,那么inotify会发送 IN_MOVED_TO;若是将被监控目录中的某个文件更名(即移动到相同目录中),那么inotify会发送IN_MOVED_FROM和 IN_MOVED_TO两个事件,而且这两个事件的cookie相同。
须要注意的是,若是使用vi对被监控的文件进行编辑,那么不会获得IN_MODIFY事件,而是会获得IN_DELETE_SELF、 IN_MOVE_SELF和IN_IGNORED三个事件。这是由于vi实际上是在一个副本上进行编辑,保存的时候将原文件覆盖。收到IN_IGNORED 事件说明监控已经自动地从inotify实例中移除(因为监控对象已经被删除,或者所在的文件系统被unmount)。因为监控已被移除,因此 inotify实例之后也不会再发送此文件相关的任何事件。函数
3 inotify-java
inotify-java并不复杂,每一个Inotify实例都会建立两个线程:readerThread和queueThread。 readerThread在循环中调用native read方法接收事件,并将接收到的事件放入BlockingQueue中。queueThread从BlockingQueue中take事件,回调 InotifyEventListener。
inotify-java使用起来也比较简单。首先须要实例化inotify对象(在其构造函数中会调用 System.loadLibrary("inotify-java"));而后在inotify对象上注册InotifyEventListener; 最后经过inotify对象的addWatch方法添加监控便可。如下是段示例代码:ui