INotify的介紹和使用

INotify的介紹和使用

inotify – Linux 2.6 內核中的文件系統變化通知機制

INotify的介紹

inotify 是一種文件系統的變化通知機制,如文件增加、刪除等事件可以立刻讓用戶態得知。

  1. Inotify 不需要對被監視的目標打開文件描述符,而且如果被監視目標在可移動介質上,那麼在 umount 該介質上的文件系統後,被監視目標對應的 watch 將被自動刪除,並且會產生一個 umount 事件。
  2. Inotify 既可以監視文件,也可以監視目錄。
  3. Inotify 使用系統調用而非 SIGIO 來通知文件系統事件。
  4. Inotify 使用文件描述符作爲接口,因而可以使用通常的文件 I/O 操作select 和 poll 來監視文件系統的變化。
Inotify 可以監視的文件系統事件包括:
IN_ACCESS,即文件被訪問
IN_MODIFY,文件被 write
IN_ATTRIB,文件屬性被修改,如 chmodchown、touch 等
IN_CLOSE_WRITE,可寫文件被 close
IN_CLOSE_NOWRITE,不可寫文件被 close
IN_OPEN,文件被 open
IN_MOVED_FROM,文件被移走,如 mv
IN_MOVED_TO,文件被移來,如 mv、cp
IN_CREATE,創建新文件
IN_DELETE,文件被刪除,如 rm
IN_DELETE_SELF,自刪除,即一個可執行文件在執行時刪除自己
IN_MOVE_SELF,自移動,即一個可執行文件在執行時移動自己
IN_UNMOUNT,宿主文件系統被 umount
IN_CLOSE,文件被關閉,等同於(IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)
IN_MOVE,文件被移動,等同於(IN_MOVED_FROM | IN_MOVED_TO)

注:上面所說的文件也包括目錄。

INotify 用戶接口、使用過程

在用戶態,inotify 通過三個系統調用和在返回的文件描述符上的文件 IO 操作來使用,使用 inotify 的第一步是創建 inotify 實例:

 int fd = inotify_init ();

每一個 inotify 實例對應一個獨立的排序的隊列。
文件系統的變化事件被稱做 watches 的一個對象管理,每一個 watch 是一個二元組(目標,事件掩碼),目標可以是文件或目錄,事件掩碼錶示應用希望關注的 inotify 事件,每一個位對應一個 inotify 事件。Watch 對象通過 watch描述符引用,watches 通過文件或目錄的路徑名來添加。目錄 watches 將返回在該目錄下的所有文件上面發生的事件。

下面函數用於添加一個 watch:

    int wd = inotify_add_watch (fd, path, mask);


fd   是 inotify_init() 返回的文件描述符,  
path 是被監視的目標的路徑名(即文件名或目錄名),  
mask 是事件掩碼, 在頭文件 linux/inotify.h 中定義了每一位代表的事件。可以使用同樣的方式來修改事件掩碼,即改變希望被通知的inotify 事件。  
Wd   是 watch 描述符。

下面的函數用於刪除一個 watch:

    int ret = inotify_rm_watch (fd, wd);

fd 是 inotify_init() 返回的文件描述符,  
wd 是 inotify_add_watch() 返回的 watch 描述符。  
Ret 是函數的返回值。

文件事件用一個 inotify_event 結構表示,它通過由 inotify_init() 返回的文件描述符使用通常文件讀取函數 read 來獲得

struct inotify_event {
        __s32           wd;             /* watch descriptor */
        __u32           mask;           /* watch mask */
        __u32           cookie;         /* cookie to synchronize two events */
        __u32           len;            /* length (including nulls) of name */
        char            name[0];        /* stub for possible name */
}; 

wd 爲被監視目標的 watch 描述符,
mask 爲事件掩碼,
len 爲 name字符串的長度,
name 爲被監視目標的路徑名,該結構的 name 字段爲一個樁,它只是爲了用戶方面引用文件名,文件名是變長的,它實際緊跟在該結構的後面,文件名將被 0 填充以使下一個事件結構能夠 4 字節對齊。注意,len 也把填充字節數統計在內。

通過 read 調用可以一次獲得多個事件,只要提供的 buf 足夠大。

 size_t len = read (fd, buf, BUF_LEN);

 buf 是一個 inotify_event 結構的數組指針,
 BUF_LEN 指定要讀取的總長度,
 buf 大小至少要不小於 BUF_LEN,該調用返回的事件數取決於 BUF_LEN 以及事件中文件名的長度。
 Len 爲實際讀去的字節數,即獲得的事件的總長度。

可以在函數 inotify_init() 返回的文件描述符 fd 上使用 select() 或poll(), 也可以在 fd 上使用 ioctl 命令 FIONREAD 來得到當前隊列的長度。close(fd)將刪除所有添加到 fd 中的 watch 並做必要的清理。

Inotify使用實例

該程序將監視發生在當前目錄下的文件 tmp_file 與當前目錄下的目錄 tmp_dir 上的所有文件系統事件, 同時它也將監視發生在文件 /mnt/sda3/windows_file 上的文件系統事件,注意,/mnt/sda3 是 SATA 硬盤分區 3 的掛接點。

#include <linux/unistd.h>
#include <linux/inotify.h>
#include <errno.h>
_syscall0(int, inotify_init)
_syscall3(int, inotify_add_watch, int, fd, const char *, path, __u32, mask)
_syscall2(int, inotify_rm_watch, int, fd, __u32, mask)
char * monitored_files[] = {
    "./tmp_file",
    "./tmp_dir",
    "/mnt/sda3/windows_file"
};
struct wd_name {
    int wd;
    char * name;
};
#define WD_NUM 3
struct wd_name wd_array[WD_NUM];
char * event_array[] = {
    "File was accessed",
    "File was modified",
    "File attributes were changed",
    "writtable file closed",
    "Unwrittable file closed",
    "File was opened",
    "File was moved from X",
    "File was moved to Y",
    "Subfile was created",
    "Subfile was deleted",
    "Self was deleted",
    "Self was moved",
    "",
    "Backing fs was unmounted",
    "Event queued overflowed",
    "File was ignored"
};
#define EVENT_NUM 16
#define MAX_BUF_SIZE 1024

int main(void)
{
    int fd;
    int wd;
    char buffer[1024];
    char * offset = NULL;
    struct inotify_event * event;
    int len, tmp_len;
    char strbuf[16];
    int i = 0;

    fd = inotify_init();
    if (fd < 0) {
        printf("Fail to initialize inotify.\n");
        exit(-1);
    }
    for (i=0; i<WD_NUM; i++) {
        wd_array[i].name = monitored_files[i];
        wd = inotify_add_watch(fd, wd_array[i].name, IN_ALL_EVENTS);
        if (wd < 0) {
            printf("Can't add watch for %s.\n", wd_array[i].name);
            exit(-1);
        }
        wd_array[i].wd = wd;
    }
    while(len = read(fd, buffer, MAX_BUF_SIZE)) {
        offset = buffer;
        printf("Some event happens, len = %d.\n", len);
        event = (struct inotify_event *)buffer;
        while (((char *)event - buffer) < len) {
            if (event->mask & IN_ISDIR) {
                memcpy(strbuf, "Direcotory", 11);
            }
            else {
                memcpy(strbuf, "File", 5);
            }
            printf("Object type: %s\n", strbuf);
            for (i=0; i<WD_NUM; i++) {
                if (event->wd != wd_array[i].wd) continue;
                printf("Object name: %s\n", wd_array[i].name);
                break;
            }
            printf("Event mask: %08X\n", event->mask);
            for (i=0; i<EVENT_NUM; i++) {
                if (event_array[i][0] == '\0') continue;
                if (event->mask & (1<<i)) {
                    printf("Event: %s\n", event_array[i]);
                }
            }
            tmp_len = sizeof(struct inotify_event) + event->len;
            event = (struct inotify_event *)(offset + tmp_len); 
            offset += tmp_len;
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章