天天看點

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,檔案屬性被修改,如 chmod、chown、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 執行個體:

每一個 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[];        /* stub for possible name */
}; 

wd 為被監視目标的 watch 描述符,
mask 為事件掩碼,
len 為 name字元串的長度,
name 為被監視目标的路徑名,該結構的 name 字段為一個樁,它隻是為了使用者方面引用檔案名,檔案名是變長的,它實際緊跟在該結構的後面,檔案名将被  填充以使下一個事件結構能夠  位元組對齊。注意,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[];
    char * offset = NULL;
    struct inotify_event * event;
    int len, tmp_len;
    char strbuf[];
    int i = ;

    fd = inotify_init();
    if (fd < ) {
        printf("Fail to initialize inotify.\n");
        exit(-);
    }
    for (i=; 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 < ) {
            printf("Can't add watch for %s.\n", wd_array[i].name);
            exit(-);
        }
        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", );
            }
            else {
                memcpy(strbuf, "File", );
            }
            printf("Object type: %s\n", strbuf);
            for (i=; 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=; i<EVENT_NUM; i++) {
                if (event_array[i][] == '\0') continue;
                if (event->mask & (<<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;
        }
    }
}
           

繼續閱讀