Linux inotify使用安裝創建設備
想知道到Linux inotify系統的真相么,想知道Linux inotify系統中藏有的內在奧義么,只有我來給大家全面講解介紹Linux inotify系統使用 Linux inotify 監控 Linux 文件系統事件.
Linux inotify 是文件系統事件監控機制,計劃包含在即將發布的 Linux 內核中作為 dnotify 的有效替代。dnotify 是較早內核支持的文件監控機制。Linux inotify一種強大的、細粒度的、異步的機制,它滿足各種各樣的文件監控需要,不僅限于安全和性能。
下面讓我們一起學習如何安裝 Linux inotify 和如何構建一個示例用戶空間應用程序來響應文件系統事件。文件系統事件監控對于從文件管理器到安全工具的各種程序都是必要的,但是 dnotify(早期內核中的標準)存在一些局限性,這使我們期待出現一種更加完善的機制。抱著這種期待,我們發現了 Linux inotify,一種更加現代化的文件系統事件監控替代品。
為什么使用 Linux inotify?
使用 Linux inotify 取代 dnotify 的原因有很多。***個原因是,dnotify 需要您為每個打算監控是否發生改變的目錄打開一個文件描述符。當同時監控多個目錄時,這會消耗大量的資源,因為有可能達到每個進程的文件描述符限制。
除此之外,文件描述符會鎖定目錄,不允許卸載(unmount)支持的設備,這在存在可移動介質的環境中會引發問題。在使用 Linux inotify 時,如果正在監控被卸載的文件系統上的文件,那么監控會被自動移除并且您會接收到一個卸載事件。
dnotify 不如 Linux inotify 的第二個原因是 dnotify 有點復雜。注意,使用 dnotify 基礎設施的簡單文件系統監控粒度只停留于目錄級別。為了使用 dnotify 進行更細粒度的監控,應用程序編程人員必須為每個受監控的目錄保留一個 stat 結構的緩存。
該用戶空間的 stat 結構緩存需要用來明確確定當接收到通知信號時目錄發生了什么變化。當獲得通知信號時,生成 stat 結構列表并與***的狀態相比較。顯而易見,這種技術是不理想的。
Linux inotify 的另一個優點是它使用文件描述符作為基本接口,使應用程序開發者使用 select 和 poll 來監控設備。這允許有效的多路 I/O 和與 Glib 的 mainloop 的集成。相反,dnotify 所使用的信號常常使程序員頭疼并且感覺不太優雅。
Linux inotify 通過提供一個更優雅的 API 解決了這些問題,該 API 使用最少的文件描述符,并確保更細粒度的監控。與 Linux inotify 的通信是通過設備節點提供的。基于以上原因,對于監控 Linux 2.6 平臺上的文件,Linux inotify 是您最明智的選擇。
回頁首安裝 Linux inotify
安裝 Linux inotify 的***步是確定您使用的 Linux 內核是否支持它。檢查發行版的最簡單方法是,尋找是否存在 /dev/Linux inotify 設備。如果存在該設備,您可以跳到 在簡單應用程序中使用 Linux inotify 一節。
在撰寫本文時,Linux inotify 包含在 Andrew Morton 的 Linux 2.6-mm 目錄樹中,而且一些 Linux 發行版正在提供支持 Linux inotify 的內核(包括 Gentoo 和 Ubuntu)或者具有提供支持的補充內核包(例如 Fedora 和 SuSE)。
因為 Andrew 可能會根據需要從目錄樹刪除對 Linux inotify 的支持,并且 Linux inotify 版本還處于頻繁的開發階段,所以強烈建議您從頭開始打補丁。 如果缺少該設備,您可能需要對內核打補丁并創建該設備。
為 Linux inotify 對內核打補丁可以從 Linux Kernel Archives 獲得 Linux inotify 補丁(請參閱 參考資料 一節的鏈接)。 您應該為特定的內核應用***版本編號的補丁。每個發行版處理內核的安裝都有所不同,但以下介紹的是一個通用指導。注意:從 Linux Kernel Archives 獲取發行版 2.6 Linux 內核源文件,如果合適,請獲取***的穩定版本。
從進入內核源文件目錄開始: bash:~$ cd /usr/src 因為您早先安裝了內核源文件,現在需要將它解壓縮: bash:~$ sudo tar jxvf linux-source-2.6.8.1.tar.bz2 現在,使您的 symlink 指向新的源文件目錄樹: bash:~$ sudo ln -sf linux-source-2.6.8.1 linux 改變當前目錄到剛才創建的內核源文件目錄:
bash:~$ cd linux 拷貝 Linux inotify 補丁: bash:~$ sudo cp ~/Linux inotify* /usr/src 將內核打補?。?bash:~$ sudo patch -p1 < ../Linux inotify*.patch 構建內核: bash:~$ sudo make menuconfig
像平時一樣配置您的內核,確保 Linux inotify 工作正常。如果必要,請將新內核添加到引導加載程序中,但是一定要記住維護舊內核的映像和引導加載程序選項。這一步對于不同引導加載程序有所不同(請參閱 參考資料 了解關于特定引導加載程序的更多信息)。
重新引導計算機并選擇啟用 Linux inotify 的新內核。在繼續往下操作前,測試您的新內核以確保它工作正常。
創建 Linux inotify 設備
接下來,您需要確保創建 /dev/Linux inotify 設備。以下步驟帶領您完成這個過程。重要注意:次設備編號可能會發生改變,所以您需要多加注意以確保它隨時更新!如果 Linux 安裝支持 udev 功能,它將會自動保持更新。
在重新引導到新內核后,您必須獲取次設備編號: bash:~$ dmesg | grep ^Linux inotify 返回結果示例如下: Linux inotify device minor=63 因為 Linux inotify 是 misc 設備,所以主設備編號是 10。要創建設備節點作為根用戶,請執行以下命令: bash:~$ mknod /dev/Linux inotify c 10 63
注意:如有必要,請使用合適的次設備編號替換“63”。 您可以隨意設置您想要的權限。一個示例權限設置如下所示: bash:~$ chown root:root /dev/Linux inotify bash:~$ chmod 666 /dev/Linux inotify 現在準備使用 Linux inotify 設備進行文件系統監控。 回頁首
在簡單應用程序中使用 Linux inotify為演示 Linux inotify 的使用,我將展示如何為文件系統事件構造一個監控任意目錄(或單個文件)的示例程序。我將站在一個較高的層次上來展示 Linux inotify 使文件系統監控變得多么容易。
Main 方法這個簡單的示例向我們展示 Linux inotify 在任意目錄上設置監控是多么容易。稍后我們將看到主要的幫助器例程。您可以在本文的 下載 一節獲取這些例子中使用的示例代碼。
清單 1. 在目錄上設置監控
- /* This program will take as argument a directory name and monitor it,
- printing event notifications to the console.
- */
- int main (int argc, char **argv)
- {
- /* This is the file descriptor for the Linux inotify device */
- int Linux inotify_fd;
- /* First we open the Linux inotify dev entry */
- Linux inotify_fd = open_Linux inotify_dev();
- if (Linux inotify_fd < 0)
- {
- return 0;
- }
- /* We will need a place to enqueue Linux inotify events,
- this is needed because if you do not read events
- fast enough, you will miss them.
- */
- queue_t q;
- q = queue_create (128);
- /* Watch the directory passed in as argument
- Read on for why you might want to alter this for
- more efficient Linux inotify use in your app.
- */
- watch_dir (Linux inotify_fd, argv[1], ALL_MASK);
- process_Linux inotify_events (q, Linux inotify_fd);
- /* Finish up by destroying the queue, closing the fd,
- and returning a proper code
- */
- queue_destroy (q);
- close_Linux inotify_dev (Linux inotify_fd);
- return 0;
- }
重要的幫助器方法
以下是每個基于 Linux inotify 的應用程序共同的最重要的幫助器例程: 為讀取而打開 Linux inotify 設備。 對從該設備讀取的事件進行排隊。 允許應用程序對事件通知進行有用處理的實際的每事件處理器。
我不會深入鉆研事件排隊的細節,因為我們能夠使用一些策略來避免排隊。提供的代碼中就展示了一個這樣的方法;更先進的多線程方法可以并且已經在其他地方實現。在那些實現中,讀者線程簡單地在 Linux inotify 設備上執行 select(),然后將事件拷貝到一些線程共享的存儲空間(或者一些像 Glib 的異步消息隊列的東西),以后處理器線程會處理這里的事件。
清單 2. 打開 Linux inotify 設備
- /* This simply opens the Linux inotify node in dev (read only) */
- int open_Linux inotify_dev ()
- {
- int fd;
- fd = open("/dev/Linux inotify", O_RDONLY);
- if (fd < 0)
- {
- perror ("open(\"/dev/Linux inotify\", O_RDONLY) = ");
- }
- return fd;
- }
這對任何一個在 Linux 系統上進行過文件編程的人來說都應該是熟悉的。
清單 3. 實際的事件處理例程
- /* This method does the dirty work of determining what happened,
- then allows us to act appropriately
- */
- void handle_event (struct Linux inotify_event *event)
- {
- /* If the event was associated with a filename, we will store it here */
- char * cur_event_filename = NULL;
- /* This is the watch descriptor the event occurred on */
- int cur_event_wd = event->wd;
- if (event->len)
- {
- cur_event_filename = event->filename;
- }
- printf("FILENAME=%s\n", cur_event_filename);
- printf("\n");
- /* Perform event dependent handler routines */
- /* The mask is the magic that tells us what file operation occurred */
- switch (event->mask)
- {
- /* File was accessed */
- case IN_ACCESS:
- printf("ACCESS EVENT OCCURRED: File \"%s\" on WD #%i\n",
- cur_event_filename, cur_event_wd);
- break;
- /* File was modified */
- case IN_MODIFY:
- printf("MODIFY EVENT OCCURRED: File \"%s\" on WD #%i\n",
- cur_event_filename, cur_event_wd);
- break;
- /* File changed attributes */
- case IN_ATTRIB:
- printf("ATTRIB EVENT OCCURRED: File \"%s\" on WD #%i\n",
- cur_event_filename, cur_event_wd);
- break;
- /* File was closed */
- case IN_CLOSE:
- printf("CLOSE EVENT OCCURRED: File \"%s\" on WD #%i\n",
- cur_event_filename, cur_event_wd);
- break;
- /* File was opened */
- case IN_OPEN:
- printf("OPEN EVENT OCCURRED: File \"%s\" on WD #%i\n",
- cur_event_filename, cur_event_wd);
- break;
- /* File was moved from X */
- case IN_MOVED_FROM:
- printf("MOVE_FROM EVENT OCCURRED: File \"%s\" on WD #%i\n",
- cur_event_filename, cur_event_wd);
- break;
- /* File was moved to X */
- case IN_MOVED_TO:
- printf("MOVE_TO EVENT OCCURRED: File \"%s\" on WD #%i\n",
- cur_event_filename, cur_event_wd);
- break;
- /* Subdir was deleted */
- case IN_DELETE_SUBDIR:
- printf("DELETE_SUBDIR EVENT OCCURRED: File \"%s\" on WD #%i\n",
- cur_event_filename, cur_event_wd);
- break;
- /* File was deleted */
- case IN_DELETE_FILE:
- printf("DELETE_FILE EVENT OCCURRED: File \"%s\" on WD #%i\n",
- cur_event_filename, cur_event_wd);
- break;
- /* Subdir was created */
- case IN_CREATE_SUBDIR:
- printf("CREATE_SUBDIR EVENT OCCURRED: File \"%s\" on WD #%i\n",
- cur_event_filename, cur_event_wd);
- break;
- /* File was created */
- case IN_CREATE_FILE:
- printf("CREATE_FILE EVENT OCCURRED: File \"%s\" on WD #%i\n",
- cur_event_filename, cur_event_wd);
- break;
- /* Watched entry was deleted */
- case IN_DELETE_SELF:
- printf("DELETE_SELF EVENT OCCURRED: File \"%s\" on WD #%i\n",
- cur_event_filename, cur_event_wd);
- break;
- /* Backing FS was unmounted */
- case IN_UNMOUNT:
- printf("UNMOUNT EVENT OCCURRED: File \"%s\" on WD #%i\n",
- cur_event_filename, cur_event_wd);
- break;
- /* Too many FS events were received without reading them
- some event notifications were potentially lost. */
- case IN_Q_OVERFLOW:
- printf("Warning: AN OVERFLOW EVENT OCCURRED: \n");
- break;
- case IN_IGNORED:
- printf("IGNORED EVENT OCCURRED: \n");
- break;
- /* Some unknown message received */
- default:
- printf ("UNKNOWN EVENT OCCURRED for file \"%s\" on WD #%i\n",
- cur_event_filename, cur_event_wd);
- break;
- }
- }
在每一條 case 語句中,您可以隨意執行任意已實現并且滿足需要的方法。 至于性能監控,您可以確定哪些文件是最經常被讀取的和它們打開的持續時間。這種監控非常方便,因為在某些情況下,如果文件在短時間內被應用程序重復地讀取,它會將文件緩存在內存中而不用返回磁盤去讀取,從而提高性能。
很容易舉出一些執行有趣操作的特定于事件的處理器的例子。比如,如果您是在為底層文件系統實現一個元數據存儲索引,您可能會尋找文件創建事件,不久還會在該文件上觸發一個元數據挖掘操作。在安全環境中,如果文件被寫入一個無人可以寫入的目錄,您會觸發某些形式的系統警報。
請注意,Linux inotify 支持許多非常細粒度的事件 —— 例如 CLOSE 與 CLOSE_WRITE。 本文中的代碼所列舉的許多事件,可能您并不希望在每次代碼運行時都看到。實際上,只要可能,您可以并且應該只請求對您的應用程序有用的事件子集。
出于測試目的,本文章提供的代碼通過嚴格使用完整掩碼(如可下載的示例代碼[請參閱 參考資料] 中 main 方法的第 51 行附近或者上面的 清單 1 中的第 29 行所執行的)展示了許多事件。應用程序員通常想要有更多選擇,而您則需要更特定的掩碼來滿足您的需要。這使您可以從上述的 handle_event() 方法中的 catch 語句刪除不感興趣的條目。
【編輯推薦】