Linux內核Inotify機制學習筆記

1、Inotify簡介:java

Inotify是一種文件變化通知機制,Linux內核從2.6.13開始引入。它是一個內核用於通知用戶空間程序文件系統變化的機制。開源社區提出用戶態須要內核提供一些機制,以便用戶態可以及時地得知內核
或底層硬件設備發生了什麼,從而可以更好地管理設備,給用戶提供更好的服務,如 hotplug、udev 和 inotify 就是這種需求催生的。
Hotplug 是一種內核向用戶態應用通報關於熱插拔設備一些事件發生的機制,桌面系統可以利用它對設備進行有效的管理,
udev 動態地維護 /dev 下的設備文件,
inotify 是一種文件系統的變化通知機制,如文件增長、刪除等事件能夠馬上讓用戶態得知。node

Inotify 是爲替代 dnotify 而設計的,它克服了 dnotify 的缺陷,提供了更好用的,簡潔而強大的文件變化通知機制:linux

(1) Inotify 不須要對被監視的目標打開文件描述符,並且若是被監視目標在可移動介質上,那麼在 umount 該介質上的文件系統後,被監視目標對應的 watch 將被自動刪除,而且會產生一個 umount 事件。
(2) Inotify 既能夠監視文件,也能夠監視目錄。
(3) Inotify 使用系統調用而非 SIGIO 來通知文件系統事件。
(4) Inotify 使用文件描述符做爲接口,於是可使用一般的文件 I/O 操做select 和 poll 來監視文件系統的變化。android

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)cookie

 

2、使用方法函數

經過inotify_init()建立一個文件描述符,而後使用inotify_add_watch()附加一個或多個監視器(一個監視器是一個路徑和一組事件),接着使用 read()方法從描述符獲取事件信息,read()函數在事件發生以前是被阻塞的。
也能夠在函數inotify_init()返回的文件描述符fd 上使用 select() 或poll(), 也能夠在fd上使用ioctl命令FIONREAD來獲得當前隊列的長度。close(fd)將刪除全部添加到fd中的watch並作必要的清理。測試

系統調用接口:this

第一步 建立 inotify 實例spa

int fd = inotify_init ();

第二步 添加一個 watch.net

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

每個inotify 實例對應一個獨立的排序的隊列。文件系統的變化事件使用Watch對象來描述,每個Watch是一個二元組(目標,事件掩碼),目標能夠是文件或目錄,事件掩碼錶示應用但願關注的inotify 事件,每個位對應一個 inotify 事件。Watch經過文件或目錄的路徑名來添加。目錄Watch將返回在該目錄下的全部文件上面發生的事件。
參數:
fd 是 inotify_init() 返回的文件描述符,path是被監視的目標的路徑名(即文件名或目錄名),mask是事件掩碼, 在頭文件 linux/inotify.h 中定義了每一位表明的事件。可使用一樣的方式來修改事件掩碼,即改變但願被通知的inotify 事件。

第三步 使用read系統調用讀取處理事件

 

1.刪除一個watch

int ret = inotify_rm_watch (fd, wd);

fd是inotify_init()返回的文件描述符句柄,wd是 inotify_add_watch()返回的watch描述符句柄。Ret是函數的返回值。文件事件用一個 inotify_event結構表示,它經過讀取inotify_init()返回的文件描述符句柄來得到。

struct inotify_event { __s32 wd; /* 被監視目標的 watch 描述符 */ __u32 mask; /* 事件掩碼 */ __u32 cookie; /* cookie to synchronize two events */ __u32 len; /* name字符串的長度 */
    char            name[0];        /* 被監視目標的路徑名 */ };

 

3、使用demo

#include <stdio.h> #include <sys/inotify.h> #include <unistd.h> #include <errno.h> #include <string.h>
 
/* * 指定一個目錄,當目錄中建立或者刪除文件時,把相應的信息打印出來 * Usage : inotify <dir> */
int main(int argc, char *argv[]) { int fd; int result; char event_buf[512]; int event_pos = 0; struct inotify_event *event; struct inotify_event *pos_event; if(2 != argc) { printf("Usage : %s <dir>\n", argv[0]); return -1; } /* 1.初始化一個inotify的實例,得到一個該實例的文件描述符 */ fd = inotify_init(); if (fd == -1) { printf("inotify init error: %s", strerror(errno)); return -1; } /* 2.添加一個用於監視的目錄: 監視該目錄中文件的添加和移除修改 */ result = inotify_add_watch(fd, argv[1], IN_DELETE | IN_CREATE | IN_MODIFY); if(-1 == result) { printf("inotify_add_watch error:%s\n", strerror(errno)); return -1; } /* 不停的監視當前目錄中是否有添加或者刪除文件 */
    while(1) { /* 讀取inotify的監視事件的信息 */ memset(event_buf, 0, sizeof(event_buf)); pos_event = (struct inotify_event *)event_buf; /* 阻塞讀取 */ result = read(fd, event_buf, sizeof(event_buf)); if (result < (int)sizeof(struct inotify_event)) { printf("could not read event: %s\n",strerror(errno)); return -1; } /* 將得到的inotify信息打印出來 */
        while (result >= (int)sizeof(struct inotify_event)) { event = pos_event; if (event->len) { if (event->mask & IN_CREATE) { printf("create : file is %s\n", event->name); } else if (event->mask & IN_DELETE) { printf("delete : file is %s\n", event->name); } else if (event->mask & IN_MODIFY) { printf("modify : file is %s\n", event->name); } } /* 更新位置信息,以便得到下一個 inotify_event 對象的首地址 */ pos_event++; result -= (int)sizeof(struct inotify_event); } } /* 關閉這個文件描述符 */ close(fd); return 0; }

測試:

# ./inotify_test /mytest/ & # touch a.txt create : file is a.txt # cp /etc/profile ./ create : file is profile modify : file is profile # t# echo hello > a.txt modify : file is a.txt modify : file is a.txt # # echo good >> a.txt modify : file is a.txt # 

 

4、內核實現

1.在內核中,每個inotify實例對應一個 inotify_device 結構,inotify_device在用戶態調用inotify_init()時建立,當關閉 inotify_init()返回的文件描述符時將被釋放。
2.inotify_watch結構在用戶態調用inotify_add_watch()時建立,在用戶態調用inotify_rm_watch()或close(fd)時被釋放。不管是目錄仍是文件,在內核中都對應一個inode結構,inotify 系統在inode結構中增長了兩個字段:

#ifdef CONFIG_INOTIFY struct list_head    inotify_watches; /* watches on this inode */
    struct semaphore    inotify_sem;    /* protects the watches list */
#endif

inotify_watches 指向被監視目標上的watch列表,每當用戶調用inotify_add_watch()時,內核就爲添加的watch建立一個inotify_watch結構,並把它插入到被監視目標對應的inode的inotify_watches列表。inotify_sem用於同步對inotify_watches列表的訪問。

3.當文件系統發生事件時,相應的文件系統將顯示調用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_inode_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上。

 

五.Android中對inotify的使用

Android的文件觀察Observer機制就是在Linux文件系統的Inotify機制上實現的

FileObserver.java
    |
android_util_FileObserver.cpp
    |
fs/notify/inotify

 

 

 

 

參考:

https://blog.csdn.net/yangwen123/article/details/13996361

相關文章
相關標籤/搜索