Linux inotify功能及實現原理

來源:互聯網
上載者:User
1. inotify主要功能

它是一個核心用於通知使用者空間程式檔案系統變化的機制。

眾所周知,Linux 案頭系統與 MAC 或 Windows 相比有許多不如人意的地方,為了改善這種狀況,開源社區提出使用者態需要核心提供一些機制,以便使用者態能夠及時地得知核心或底層硬體裝置發生了什麼,從而能夠更好地管理裝置,給使用者提供更好的服務,如
hotplug、udev 和 inotify 就是這種需求催生的。Hotplug 是一種核心向使用者態應用通報關於熱插拔裝置一些事件發生的機制,案頭系統能夠利用它對裝置進行有效管理,udev 動態地維護 /dev 下的裝置檔案,inotify 是一種檔案系統的變化通知機制,如檔案增加、刪除等事件可以立刻讓使用者態得知,該機制是著名的案頭搜尋引擎項目 beagle 引入的,並在 Gamin 等項目中被應用。

2. 使用者介面

在使用者態,inotify 通過三個系統調用和在返回的檔案描述符上的檔案 I/ 操作來使用,使用 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 並做必要的清理。

                int inotify_init (void);        int inotify_add_watch (int fd, const char *path, __u32 mask);        int inotify_rm_watch (int fd, __u32 mask);        
3. 核心實現原理

 在核心中,每一個 inotify 執行個體對應一個 inotify_device 結構:

 

struct inotify_device {        wait_queue_head_t       wq;             /* wait queue for i/o */        struct idr              idr;            /* idr mapping wd -> watch */        struct semaphore        sem;            /* protects this bad boy */        struct list_head        events;         /* list of queued events */        struct list_head        watches;        /* list of watches */        atomic_t                count;          /* reference count */        struct user_struct      *user;          /* user who opened this dev */        unsigned int            queue_size;     /* size of the queue (bytes) */        unsigned int            event_count;    /* number of pending events */        unsigned int            max_events;     /* maximum number of events */        u32                     last_wd;        /* the last wd allocated */};

    d_list 指向所有 inotify_device 組成的列表的,i_list 指向所有被監視 inode 組成的列表,count 是引用計數,dev 指向該 watch 所在的 inotify 執行個體對應的 inotify_device 結構,inode 指向該 watch 要監視的 inode,wd 是分配給該 watch 的描述符,mask 是該 watch 的事件掩碼,表示它對哪些檔案系統事件感興趣。

    結構 inotify_device 在使用者態調用 inotify_init() 時建立,當關閉 inotify_init()返回的檔案描述符時將被釋放。結構 inotify_watch 在使用者態調用 inotify_add_watch()時建立,在使用者態調用 inotify_rm_watch() 或 close(fd) 時被釋放。

    無論是目錄還是檔案,在核心中都對應一個 inode 結構,inotify 系統在 inode 結構中增加了兩個欄位:

struct inotify_watch {        struct list_head        d_list; /* entry in inotify_device's list */        struct list_head        i_list; /* entry in inode's list */        atomic_t                count;  /* reference count */        struct inotify_device   *dev;   /* associated device */        struct inode            *inode; /* associated inode */        s32                     wd;     /* watch descriptor */        u32                     mask;   /* event mask for this watch */};

    d_list 指向所有 inotify_device 組成的列表的,i_list 指向所有被監視 inode 組成的列表,count 是引用計數,dev 指向該 watch 所在的 inotify 執行個體對應的 inotify_device 結構,inode 指向該 watch 要監視的 inode,wd 是分配給該 watch 的描述符,mask 是該 watch 的事件掩碼,表示它對哪些檔案系統事件感興趣。

    結構 inotify_device 在使用者態調用 inotify_init() 時建立,當關閉 inotify_init()返回的檔案描述符時將被釋放。結構 inotify_watch 在使用者態調用 inotify_add_watch()時建立,在使用者態調用 inotify_rm_watch() 或 close(fd) 時被釋放。

    無論是目錄還是檔案,在核心中都對應一個 inode 結構,inotify 系統在 inode 結構中增加了兩個欄位:

#ifdef CONFIG_INOTIFYstruct list_headinotify_watches; /* watches on this inode */struct semaphoreinotify_sem;/* protects the watches list */#endif

    inotify_watches 是在被監視目標上的 watch 列表,每當使用者調用 inotify_add_watch()時,核心就為添加的 watch 建立一個 inotify_watch 結構,並把它插入到被監視目標對應的 inode 的 inotify_watches 列表。inotify_sem 用於同步對 inotify_watches 列表的訪問。當檔案系統發生第一部分提到的事件之一時,相應的檔案系統代碼將顯示調用fsnotify_*
來把相應的附隨報告給 inotify 系統,其中*號就是相應的事件名,目前實現包括:

    fsnotify_move,檔案從一個目錄移動到另一個目錄fsnotify_nameremove,檔案從目錄中刪除fsnotify_inoderemove,自刪除fsnotify_create,建立新檔案fsnotify_mkdir,建立新目錄fsnotify_access,檔案被讀fsnotify_modify,檔案被寫fsnotify_open,檔案被開啟fsnotify_close,檔案被關閉fsnotify_xattr,檔案的擴充屬性被修改fsnotify_change,檔案被修改或原資料被修改有一個例外情況,就是
inotify_unmount_inodes,它會在檔案系統被 umount 時調用來通知 umount 事件給 inotify 系統。

    以上提到的通知函數最後都調用 inotify_inode_queue_event(inotify_unmount_inodes直接調用 inotify_dev_queue_event ),該函數首先判斷對應的inode是否被監視,這通過查看 inotify_watches 列表是否為空白來實現,如果發現 inode 沒有被監視,什麼也不做,立刻返回,反之,遍曆 inotify_watches 列表,看是否當前的檔案操作事件被某個 watch
監視,如果是,調用 inotify_dev_queue_event,否則,返回。函數inotify_dev_queue_event 首先判斷該事件是否是上一個事件的重複,如果是就丟棄該事件並返回,否則,它判斷是否 inotify 執行個體即 inotify_device 的事件隊列是否溢出,如果溢出,產生一個溢出事件,否則產生一個當前的檔案操作事件,這些事件通過kernel_event 構建,kernel_event 將建立一個 inotify_kernel_event 結構,然後把該結構插入到對應的 inotify_device
的 events 事件列表,然後喚醒等待在inotify_device 結構中的 wq 指向的等待隊列。想監視檔案系統事件的使用者態進程在inotify 執行個體(即 inotify_init() 返回的檔案描述符)上調用 read 時但沒有事件時就掛在等待隊列 wq 上。

4. 使用樣本

下面是一個使用 inotify 來監視檔案系統事件的例子:

 

#include #include #include _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 3struct 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 1024int 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; wd="inotify_add_watch(fd," add (event- if { len) < buffer) - *)event (((char while *)buffer; inotify_event event len); len='%d.\n",' happens, printf(?Some offset="buffer;" MAX_BUF_SIZE)) buffer, while(len="read(fd," } wd_array[i].wd="wd;" exit(-1); wd_array[i].name); %s.\n?, for watch printf(?Can?t 0) (wd IN_ALL_EVENTS); wd_array[i].name, wd_array[i].name="monitored_files[i];" i++)>mask & IN_ISDIR) {memcpy(strbuf, "Direcotory", 11);}else {memcpy(strbuf, "File", 5);}printf("Object type: %s\n", strbuf);for (i=0; iwd != 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; imask & (1<len;event = (struct inotify_event *)(offset + tmp_len); offset += tmp_len;}}}

轉自:http://linux.chinaitlab.com/kernel/391147_4.html

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.