• inotify-java linux系统监听文件发生变化,实时通知java程序


    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除了更易于使用之外还有以下主要的优点:

    • inotify使用异步的事件通知机制。
    • 可以监控文件系统中的任何对象(dnotify只能监控目录)。如果inotify监控的是目录,那么在目录中的某个文件发生变化时,inotify可以通知发生变化的文件名(dnotify只能报告有变化,应用程序本身需要判断是哪个文件发生变化)。
    • 对于每个被监控的文件,inotify不需要维护一个打开的文件描述符,因此不会影响unmount之类的操作,相反会在被监控文件所在的文件系统被unmount时得到一个通知。

    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参数的可选值如下:

    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,例如:

    C代码  收藏代码
    1. int wd;  
    2. wd = inotify_add_watch (fd, "/home/user1/data.txt", IN_ACCESS | IN_MODIFY);  

        假设希望只监控/home/user1/data.txt文件的修改事件一次,那么可以使用IN_MODIFY 和IN_ONESHOT,例如:

    C代码  收藏代码
    1. int wd;  
    2. wd = inotify_add_watch (fd, "/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/目录下有以下三个文件:

    • max_queued_events 最大排队的事件个数。如果排队事件个数达到此值,那么新到的时间会被丢弃,并发送IN_Q_OVERFLOW事件。默认值16,384。
    • max_user_instances 每个用户可以创建的inotify实例最大值。默认值128。
    • max_user_watches 每个用户可以创建的监控的最大值。默认值8,192。

    2.3 notifications
        inotify的事件是异步通知的,并且在内部进行了排队。但是对事件的读取必须以同步方式进行。如果以read读取,那么该方法一直阻塞到有事件到达,并且一次会读入所有排队中的事件。inotify的通知事件由inotify_event结构体定义,如下:

    C代码  收藏代码
    1. struct inotify_event {  
    2.         __s32 wd;             /* watch descriptor */  
    3.         __u32 mask;           /* watch mask */  
    4.         __u32 cookie;         /* cookie to synchronize two events */  
    5.         __u32 len;            /* length (including nulls) of name */  
    6.         char name[0];        /* stub for possible name */  
    7. };  

        其中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方法添加监控即可。以下是段示例代码:

    Java代码  收藏代码
      1. import java.util.HashMap;  
      2. import java.util.Map;  
      3.   
      4. import com.den_4.inotify_java.Constants;  
      5. import com.den_4.inotify_java.EventQueueFull;  
      6. import com.den_4.inotify_java.Inotify;  
      7. import com.den_4.inotify_java.InotifyEvent;  
      8. import com.den_4.inotify_java.InotifyEventListener;  
      9.   
      10. public class Test {  
      11.     //  
      12.     private static final Map<Integer, String> MASKS = new HashMap<Integer, String>();  
      13.     static {  
      14.         MASKS.put(Constants.IN_ACCESS, "IN_ACCESS");  
      15.         MASKS.put(Constants.IN_MODIFY, "IN_MODIFY");  
      16.         MASKS.put(Constants.IN_ATTRIB, "IN_ATTRIB");  
      17.         MASKS.put(Constants.IN_CLOSE_WRITE, "IN_CLOSE_WRITE");  
      18.         MASKS.put(Constants.IN_CLOSE_NOWRITE, "IN_CLOSE_NOWRITE");  
      19.         MASKS.put(Constants.IN_OPEN, "IN_OPEN");  
      20.         MASKS.put(Constants.IN_MOVED_FROM, "IN_MOVED_FROM");  
      21.         MASKS.put(Constants.IN_MOVED_TO, "IN_MOVED_TO");  
      22.         MASKS.put(Constants.IN_CREATE, "IN_CREATE");  
      23.         MASKS.put(Constants.IN_DELETE, "IN_DELETE");  
      24.         MASKS.put(Constants.IN_DELETE_SELF, "IN_DELETE_SELF");  
      25.         MASKS.put(Constants.IN_MOVE_SELF, "IN_MOVE_SELF");  
      26.         MASKS.put(Constants.IN_UNMOUNT, "IN_UNMOUNT");  
      27.         MASKS.put(Constants.IN_Q_OVERFLOW, "IN_Q_OVERFLOW");  
      28.         MASKS.put(Constants.IN_IGNORED, "IN_IGNORED");  
      29.         MASKS.put(Constants.IN_ONLYDIR, "IN_ONLYDIR");  
      30.         MASKS.put(Constants.IN_DONT_FOLLOW, "IN_DONT_FOLLOW");  
      31.         MASKS.put(Constants.IN_MASK_ADD, "IN_MASK_ADD");  
      32.         MASKS.put(Constants.IN_ISDIR, "IN_ISDIR");  
      33.         MASKS.put(Constants.IN_ONESHOT, "IN_ONESHOT");  
      34.     }  
      35.       
      36.     public static void main(String args[]) {  
      37.         try {  
      38.             Inotify i = new Inotify();  
      39.             InotifyEventListener e = new InotifyEventListener() {  
      40.   
      41.                 public void filesystemEventOccurred(InotifyEvent e) {  
      42.                     System.out.println("inotify event, mask: " + getMask(e.getMask()) + ", name: " + e.getName() + ", now: " + System.currentTimeMillis());  
      43.                 }  
      44.   
      45.                 public void queueFull(EventQueueFull e) {  
      46.                     System.out.println("inotify event queue: " + e.getSource() +  " is full");  
      47.                 }  
      48.             };  
      49.             i.addInotifyEventListener(e);  
      50.             i.addWatch("./test/", Constants.IN_MODIFY);  
      51.         } catch (Throwable e) {  
      52.             e.printStackTrace();  
      53.         }  
      54.     }  
      55.       
      56.     public static String getMask(int mask) {  
      57.         StringBuilder sb = new StringBuilder();  
      58.         for(Integer m : MASKS.keySet()) {  
      59.             if((mask & m) != 0) {  
      60.                 if(sb.length() > 0) {  
      61.                     sb.append("|");  
      62.                 }  
      63.                 sb.append(MASKS.get(m));  
      64.             }  
      65.         }  
      66.         return sb.toString();  
      67.     }  
  • 相关阅读:
    go编程资料库
    ubuntu上安装notepadpp
    ubuntu上安装firefox
    golang之下载安装配置
    ubuntu上制作应用程序的快捷图标启动
    ubuntu上安装字体
    装机必备
    uml设计之多重性
    数据库冗余是否必要
    GitHub上传文件不能超过100M的解决办法
  • 原文地址:https://www.cnblogs.com/wang3680/p/5593805.html
Copyright © 2020-2023  润新知