What’s Notifier?
如果你要寫一個 Linux Application,其中必須要監視一個檔案的變化,當此檔案被修改或是更新時,需要發送一個即時網路封包通知管理者,在這樣的情況之下你會怎麼實作?Endless polling?
為了解決諸如此類的情境,Linux Kernel 提供一個檔案系統 notification 機制給 User Space 應用程式使用。Application 在 User Mode 對核心註冊後,核心就會監看該特定目錄或是檔案的狀態,當特定的變化發生(稱之為 EVENT),就會通知該 Application。而這樣的一個機制在 Linux Kernel 中被稱為 Notifier。
目前的 Linux Kernel 提供幾種 Notifier,分別為 dnotify, inotify, 與 fanotify,簡述如下。
dnotify
開始出現於 linux kernel 2.4 版本,作為一個 directory notifier,故稱為 dnotify。當一個特定目錄下的檔案發生 EVENT (讀取,修改,改變權限…)時,kernel 就會發送 signal 給 user application。核心在編譯時必須先選上 CONFIG_DNOTIFY 才可支援 dnotify,一般 Linux distribution 預設都會包含,但若是嵌入式系統則需留意。
dnotify 支援的 EVENT 如下:
DN_ACCESS 該目錄下的檔案被存取(read) DN_MODIFY 該目錄下的檔案被修改(write) DN_CREATE 該目錄下有檔案被建立 DN_DELETE 該目錄下有檔案被刪除(unlink) DN_RENAME 該目錄下有檔案被更名 DN_ATTRIB 該目錄下有檔案屬性被修改(chmod,chown)
User Application 實作 dnotify 的方式為:
- 預先設置好 signal handler。
- 開啟目錄的 file descriptor。
- 藉由 fcnctl() 操作該 fd,對核心註冊要監控的 EVENT。
範例程式碼:
#define _GNU_SOURCE /* needed to get the defines */ #include <fcntl.h> /* in glibc 2.2 this has the needed values defined */ #include <signal.h> #include <stdio.h> #include <unistd.h> static volatile int event_fd; static void handler(int sig, siginfo_t *si, void *data) { event_fd = si->si_fd; } int main(void) { struct sigaction act; int fd; act.sa_sigaction = handler; sigemptyset(&act.sa_mask); act.sa_flags = SA_SIGINFO; sigaction(SIGRTMIN + 1, &act, NULL); fd = open(".", O_RDONLY); fcntl(fd, F_SETSIG, SIGRTMIN + 1); fcntl(fd, F_NOTIFY, DN_MODIFY|DN_CREATE|DN_MULTISHOT); /* we will now be notified if any of the files in "." is modified or new files are created */ while (1) { pause(); printf("Got event on fd=%d\n", event_fd); } }
inotify
Kernel 版本 2.6.13 開始支援 inotify。取代了 directory-based 的 dnotify, 改變為 inode-based ,因此稱為 inotify。inotify 提供比 dnotify 還要強大的功能,其所支援的 EVENT:
IN_ACCESS 檔案被存取(read) (*). IN_ATTRIB Metadata 已改變,包含權限, 時戳, 屬性, UID, GID 等. IN_CLOSE_WRITE 可寫入的檔案被關閉 (*). IN_CLOSE_NOWRITE 不可寫入的檔案被關閉 (*). IN_CREATE 監控的資料夾下有檔案被建立 (*). IN_DELETE 監控的資料夾下有檔案被刪除 (*). IN_DELETE_SELF 監控的目標被刪除 IN_MODIFY 檔案被修改 (*). IN_MOVE_SELF 監控的目標被移動. IN_MOVED_FROM 檔案被移出監控的資料夾外 (*). IN_MOVED_TO 檔案被移入監控的資料夾內 (*). IN_OPEN 檔案被開啟 (*). (*)代表可以套用在
inotify 有專屬的 system call 提供給 user space 呼叫。
#include <sys/inotify.h> int inotify_init(void); int inotify_init1(int flags); int inotify_add_watch(int fd, const char *pathname, uint32_t mask); int inotify_rm_watch(int fd, int wd);
除此之外,甚至還有 kernel API 提供給 kernel 其他 module 使用:
struct inotify_handle *inotify_init(struct inotify_operations *ops); inotify_init_watch(struct inotify_watch *watch); s32 inotify_add_watch(struct inotify_handle *ih, struct inotify_watch *watch, struct inode *inode, u32 mask); s32 inotify_find_watch(struct inotify_handle *ih, struct inode *inode, struct inotify_watch **watchp); s32 inotify_find_update_watch(struct inotify_handle *ih, struct inode *inode, u32 mask); int inotify_rm_wd(struct inotify_handle *ih, u32 wd); int inotify_rm_watch(struct inotify_handle *ih, struct inotify_watch *watch); void inotify_remove_watch_locked(struct inotify_handle *ih, struct inotify_watch *watch); void inotify_destroy(struct inotify_handle *ih); void get_inotify_watch(struct inotify_watch *watch); void put_inotify_watch(struct inotify_watch *watch);
這麼強大的 inotify 其實還是有些限制的,第一,無法監控目錄下的子目錄,所有的子目錄都必須獨立監控。第二,無法監控 /proc 與 /sys。同樣地,核心要支援 inotify 必須在編譯時選上 CONFIG_INOTIFY_USER。
dnotify v.s inotify
以下要點方式敘述兩者之間的差異
- dnotify 只能針對目錄最監控,inotify 可以針對任意檔案做監控。
- dnotify 要對 fd 操作,因此受限於單一 process 最大 fd 上限。inotify 則不會。
- dnotify 提供給 user-space 的界面是個災難(不是我說的XD),kernel 只能使用 signal 來通知 AP,況且有些 signal 還並非 real-time 。inotify 則有專屬且良好的 system call 提供給 user-space。
- dnotify 已註冊的情況下不允許 unmount。inotify 在 unmount 時會發送訊息。
- inotify 支援更多的 EVENT。
fanotify
全名為 fscking all notifiction and file access system。自 Kernel 版本 2.6.36 開始支援。主要 API 為:
#include <sys/fanotify.h> int fanotify_init(unsigned int flags, unsigned int event_f_flags); int fanotify_mark (int fanotify_fd, unsigned int flags, uint64_t mask, int dfd, const char *pathname);
相較於 inotify 有以下差異:
- fanotify 有 global 模式可以監控整個檔案系統的變化,藉以彌補 inotify 無法監控子目錄的不足。即便如此,fanotify 還是需要加以判斷以便後續功能使用。
- fanotify 可以阻斷 EVENT。
- fanotify 支援的 EVENT 較 inotfiy 少得許多。如不支援 Delete, move 等等。
如同前面兩個 notifier,如果要支援 fanotify,核心在編譯時要選上 CONFIG_FANOTIFY。
小結
截至目前為止,對於 fanotify 相關的實作與文件(包含 man page)還不如 inotify 來得多。許多人說 fanotify 是要替代 inotify (就如同 inotify 替代 dontify 一樣),但我並不這麼認為,最重要的一點就是 inotify 還是比 fanotify 強大好用(至少現在是如此)。最初 fanotify 作者 Eric Paris 會增加此機制最大的原因是為了商業防毒軟體所撰寫,我想這也是目前 fanotify 功能進步緩慢的原因了。於此,如果要實作檔案監控,目前最佳的選擇仍然是 inotify。