1. 程式人生 > >linux檢測檔案和目錄變化的api------inotify

linux檢測檔案和目錄變化的api------inotify

   最近遇到了一個問題就是如何去檢視一個目錄下是否新增加了一個檔案,千方百計終於搞到了很好的武功大法------inotify

inotify是用來監視檔案系統事件的機制,在Linux 2.6.13核心中引入。該機制可以用來監視檔案和目錄,當檔案或目錄發生變化時,核心會將檔案或目錄的變化傳送給inotify檔案描述符,在應用層只需呼叫read()就可以讀取這些事件,非常的方便。更好的是,inotify檔案描述符還可以使用select、poll、epoll這些介面來監聽,當有事件發生是,inotify檔案描述符會可讀。

 一、介面介紹

1、inotify_init()

  宣告如下:

#include <sys/inotify.h>  
  
int inotify_init(void);  
int inotify_init1(int flags); 
  inotify_init()用來初始化一個新的inotify例項,並返回一個檔案描述符。這個描述符在inotify_add_watch()中會用到,發生的事件也是從這個描述中讀取。

  除了這個介面外,還有一個相同功能的介面inotify_init1()。inotify_init1()中多了一個引數flags,用來在初始化時設定inotify檔案描述符的屬性。flags中可以設定的標誌有兩個:IN_NONBLOCK和IN_CLOEXEC。這兩個標誌不難理解,前一個是用來將inotify檔案描述設定為非阻塞狀態,後一個是設定close-on-exec(FD_CLOEXEC)標誌。通過使用這兩個標誌就避免在建立inotify檔案描述後再呼叫fcntl()的消耗了,程式碼看起來也會簡潔一些。

2、inotify_add_watch()

  宣告如下:

#include <sys/inotify.h>  
      
int inotify_add_watch(int fd, const char *pathname, uint32_t mask);  

 其中fd是inotify檔案描述符,inotify_init()的返回值;pathname是要監聽的檔案的路徑;mask是指定要監視哪些事件,在後面具體介紹。

  inotify_add_watch()用於將要監視的檔案或目錄新增到inotify中,返回值是一個inotify標識,注意不要和inotify_init()的返回值搞混淆了。inotify_init()的返回值是在讀取事件、註冊監聽檔案時使用,而inotify_add_watch()的返回值用來判斷返回的事件屬於哪個監聽的檔案(後面介紹inotify_event結構時會看到),以及移除監聽檔案時使用。

  3、inotify_rm_watch()

  宣告如下:

#include <sys/inotify.h>  
      
int inotify_rm_watch(int fd, uint32_t wd); 
  inotify_rm_watch()用於移除對某個檔案的監聽,其中fd是inotify檔案描述符,由inotify_init()返回;wd是inotify標識,由inotify_add_watch()返回。

二、結構及事件介紹
  當有事件發生時,notify檔案描述符會變為可讀,呼叫read()可以讀取發生的事件,事件的描述結構為inotify_event結構體,定義如下:

struct inotify_event {  
   int      wd;       /* Watch descriptor */  
   uint32_t mask;     /* Mask of events */  
   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是inotify識別符號,inotify_add_watch()的返回值;mask就是發生的事件掩碼;cookie這個好像只在rename中使用,name就是檢測改變的檔案的名字

接下來介紹inotify中的事件,及mask的取值。下面的這些巨集代表不同的事件,這些值在inotify_add_watch()中的mask引數和inotify_event結構中的mask成員中都可以使用,如下所示:

    IN_ACCESS: 檔案被訪問

             IN_ATTRIB:元資料被改變,例如許可權、時間戳、擴充套件屬性、連結數、UID、GID等

             IN_CLOSE_WRITE:關閉開啟寫的檔案

             IN_CLOSE_NOWRITE: 和IN_CLOSE_WRITE剛好相反,關閉不是開啟寫的檔案

             IN_CREATE:這個是用於目錄,在監控的目錄中建立目錄或檔案時發生

             IN_DELETE:這個也是用於目錄,在監控的目錄中刪除目錄或檔案時發生

             IN_DELETE_SELF:監控的目錄或檔案本身被刪除

             IN_MODIFY:檔案被修改,這種事件會用到inotify_event中的cookie。

             IN_MOVE_SELF:監控的檔案或目錄本身被移動

             IN_MOVED_FROM: 從監控的目錄中移出檔案

             IN_MOVED_TO:向監控的目錄中移入檔案

             IN_OPEN: 檔案被開啟

(注:linuxi下都可以抽象為檔案,如果沒有特別說明,檔案可以指普通檔案或目錄)。

   inotify還為我們定義了一個IN_ALL_EVENTS巨集,這個巨集包含了上面提到的所有事件,這個巨集在呼叫inotify_add_watch()中使用。

  下面的這些bit位只在呼叫inotify_add_watch()中會用到:

  IN_DONT_FOLLOW:如果監控的檔案時一個符號連結,只監控符號連結本身,而不是連結指向的檔案

  IN_MASK_ADD:對已監控的檔案增加要監控的的事件(不是替換原先的掩碼)

  IN_ONESHOT:只監控指定的檔案一次,事件發生後從監控列表移除

  IN_ONLYDIR:如果監控的是一個目錄,只監控目錄本身(待定)

  下面的這些bit位可能在read()讀取到的事件中設定:

  IN_IGNORED:監控被顯式移除(呼叫inotify_rm_watch()),或者自動移除(檔案被刪除或者檔案系統被解除安裝)

  IN_ISDIR:引發事件的是一個目錄

  IN_Q_OVERFLOW:事件佇列溢位(這種情況下inotify_event結構中的wd為-1)

  IN_UMOUNT:包含監控物件的檔案系統被解除安裝

總結一下,其實就是建立了一個描述符來監聽事件是否觸發,觸發的事件就是上述所列舉的情況

我寫了一個程式碼測試檔案是否被開啟

#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/inotify.h>
using namespace std;

void watch_inotify(void)
{
    int fd = 0;
    char data[255] = {0};
    int num = 0;

    fd = inotify_init();//初始化fd
    inotify_add_watch(fd, "lt", IN_OPEN);//監聽檔案是否開啟
    num = read(fd, data, 255);//沒開啟,那麼就會阻塞
    cout << num << endl;//如果開啟,那麼會繼續跑到這裡
}
int main()
{
    watch_inotify();
    return 0;
}
結果是如果我用vim或者open函式開啟時,會輸出一個num這個數字。