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除了更易於使用以外還有如下主要的優勢:java

  • 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參數的可選值以下: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

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/目錄下有如下三個文件:app

  • 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方法添加監控便可。如下是段示例代碼:ui

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.     }  
相關文章
相關標籤/搜索