- 論壇徽章:
- 0
|
Inotify 是文件系統(tǒng)事件監(jiān)控機制,計劃包含在即將發(fā)布的 Linux 內(nèi)核中作為dnotify 的有效替代。dnotify 是較早內(nèi)核支持的文件監(jiān)控機制。Inotify一種強大的、細粒度的、異步的機制,它滿足各種各樣的文件監(jiān)控需要,不僅限于安全和性能。下面讓我們一起學習如何安裝 inotify 和如何構(gòu)建一個示例用戶空間應用程序來響應文件系統(tǒng)事件。
文件系統(tǒng)事件監(jiān)控對于從文件管理器到安全工具的各種程序都是必要的,但是 dnotify(早期內(nèi)核中的標準)存在一些局限性,這使我們期待出現(xiàn)一種更加完善的機制。抱著這種期待,我們發(fā)現(xiàn)了 inotify,一種更加現(xiàn)代化的文件系統(tǒng)事件監(jiān)控替代品。
為什么使用 inotify?
使用 inotify 取代 dnotify 的原因有很多。第一個原因是,dnotify 需要您為每個打算監(jiān)控是否發(fā)生改變的目錄打開一個文件描述符。當同時監(jiān)控多個目錄時,這會消耗大量的資源,因為有可能達到每個進程的文件描述符限制。
除此之外,文件描述符會鎖定目錄,不允許卸載(unmount)支持的設備,這在存在可移動介質(zhì)的環(huán)境中會引發(fā)問題。在使用 inotify 時,如果正在監(jiān)控被卸載的文件系統(tǒng)上的文件,那么監(jiān)控會被自動移除并且您會接收到一個卸載事件。
dnotify 不如 inotify 的第二個原因是 dnotify 有點復雜。注意,使用 dnotify 基礎(chǔ)設施的簡單文件系統(tǒng)監(jiān)控粒度只停留于目錄級別。為了使用 dnotify 進行更細粒度的監(jiān)控,應用程序編程人員必須為每個受監(jiān)控的目錄保留一個 stat 結(jié)構(gòu)的緩存。該用戶空間的 stat 結(jié)構(gòu)緩存需要用來明確確定當接收到通知信號時目錄發(fā)生了什么變化。當獲得通知信號時,生成 stat 結(jié)構(gòu)列表并與最新的狀態(tài)相比較。顯而易見,這種技術(shù)是不理想的。
inotify 的另一個優(yōu)點是它使用文件描述符作為基本接口,使應用程序開發(fā)者使用 select 和 poll 來監(jiān)控設備。這允許有效的多路 I/O 和與 Glib 的 mainloop 的集成。相反,dnotify 所使用的信號常常使程序員頭疼并且感覺不太優(yōu)雅。
inotify 通過提供一個更優(yōu)雅的 API 解決了這些問題,該 API 使用最少的文件描述符,并確保更細粒度的監(jiān)控。與 inotify 的通信是通過設備節(jié)點提供的。基于以上原因,對于監(jiān)控 Linux 2.6 平臺上的文件,inotify 是您最明智的選擇。
安裝 inotify
安裝 inotify 的第一步是確定您使用的 Linux 內(nèi)核是否支持它。檢查發(fā)行版的最簡單方法是,尋找是否存在 /dev/inotify 設備。如果存在該設備,您可以跳到
在簡單應用程序中使用 inotify
一節(jié)。
在撰寫本文時,inotify 包含在 Andrew Morton 的 Linux 2.6-mm 目錄樹中,而且一些 Linux 發(fā)行版正在提供支持 inotify 的內(nèi)核(包括 Gentoo 和 Ubuntu)或者具有提供支持的補充內(nèi)核包(例如 Fedora 和 SuSE)。因為 Andrew 可能會根據(jù)需要從目錄樹刪除對 inotify 的支持,并且 inotify 版本還處于頻繁的開發(fā)階段,所以強烈建議您從頭開始打補丁。
如果缺少該設備,您可能需要對內(nèi)核打補丁并創(chuàng)建該設備。
為 inotify 對內(nèi)核打補丁
可以從 Linux Kernel Archives 獲得 inotify 補丁(請參閱
參考資料
一節(jié)的鏈接)。
您應該為特定的內(nèi)核應用最高版本編號的補丁。每個發(fā)行版處理內(nèi)核的安裝都有所不同,但以下介紹的是一個通用指導。注意:從 Linux Kernel Archives 獲取發(fā)行版 2.6 Linux 內(nèi)核源文件,如果合適,請獲取最新的穩(wěn)定版本。
從進入內(nèi)核源文件目錄開始:
bash:~$ cd /usr/src
因為您早先安裝了內(nèi)核源文件,現(xiàn)在需要將它解壓縮:
bash:~$ sudo tar jxvf linux-source-2.6.8.1.tar.bz2
現(xiàn)在,使您的 symlink 指向新的源文件目錄樹:
bash:~$ sudo ln -sf linux-source-2.6.8.1 linux
改變當前目錄到剛才創(chuàng)建的內(nèi)核源文件目錄:
bash:~$ cd linux
拷貝 inotify 補。
bash:~$ sudo cp ~/inotify* /usr/src
將內(nèi)核打補丁:
bash:~$ sudo patch -p1
構(gòu)建內(nèi)核:
bash:~$ sudo make menuconfig
像平時一樣配置您的內(nèi)核,確保 inotify 工作正常。如果必要,請將新內(nèi)核添加到引導加載程序中,但是一定要記住維護舊內(nèi)核的映像和引導加載程序選項。這一步對于不同引導加載程序有所不同(請參閱
參考資料
了解關(guān)于特定引導加載程序的更多信息)。重新引導計算機并選擇啟用 inotify 的新內(nèi)核。在繼續(xù)往下操作前,測試您的新內(nèi)核以確保它工作正常。
創(chuàng)建 inotify 設備
接下來,您需要確保創(chuàng)建 /dev/inotify 設備。以下步驟帶領(lǐng)您完成這個過程。重要注意:次設備編號可能會發(fā)生改變,所以您需要多加注意以確保它隨時更新!如果 Linux 安裝支持 udev 功能,它將會自動保持更新。
在重新引導到新內(nèi)核后,您必須獲取次設備編號:
bash:~$ dmesg | grep ^inotify
返回結(jié)果示例如下:
inotify device minor=63
因為 inotify 是 misc 設備,所以主設備編號是 10。要創(chuàng)建設備節(jié)點作為根用戶,請執(zhí)行以下命令:
bash:~$ mknod /dev/inotify c 10 63
注意:如有必要,請使用合適的次設備編號替換“63”。
您可以隨意設置您想要的權(quán)限。一個示例權(quán)限設置如下所示:
bash:~$ chown root:root /dev/inotify
bash:~$ chmod 666 /dev/inotify
現(xiàn)在準備使用 inotify 設備進行文件系統(tǒng)監(jiān)控。
在簡單應用程序中使用 inotify
為演示 inotify 的使用,我將展示如何為文件系統(tǒng)事件構(gòu)造一個監(jiān)控任意目錄(或單個文件)的示例程序。我將站在一個較高的層次上來展示 inotify 使文件系統(tǒng)監(jiān)控變得多么容易。
Main 方法
這個簡單的示例向我們展示 inotify 在任意目錄上設置監(jiān)控是多么容易。稍后我們將看到主要的幫助器例程。您可以在本文的
下載
一節(jié)獲取這些例子中使用的示例代碼。
清單 1. 在目錄上設置監(jiān)控
/* 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 inotify device */
int inotify_fd;
/* First we open the inotify dev entry */
inotify_fd = open_inotify_dev();
if (inotify_fd
{
return 0;
}
/* We will need a place to enqueue 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 inotify use in your app.
*/
watch_dir (inotify_fd, argv[1], ALL_MASK);
process_inotify_events (q, inotify_fd);
/* Finish up by destroying the queue, closing the fd,
and returning a proper code
*/
queue_destroy (q);
close_inotify_dev (inotify_fd);
return 0;
}
重要的幫助器方法
以下是每個基于 inotify 的應用程序共同的最重要的幫助器例程:
Ø 為讀取而打開 inotify 設備。
Ø 對從該設備讀取的事件進行排隊。
Ø 允許應用程序?qū)κ录ㄖM行有用處理的實際的每事件處理器。
我不會深入鉆研事件排隊的細節(jié),因為我們能夠使用一些策略來避免排隊。提供的代碼中就展示了一個這樣的方法;更先進的多線程方法可以并且已經(jīng)在其他地方實現(xiàn)。在那些實現(xiàn)中,讀者線程簡單地在 inotify 設備上執(zhí)行 select(),然后將事件拷貝到一些線程共享的存儲空間(或者一些像 Glib 的異步消息隊列的東西),以后處理器線程會處理這里的事件。
清單 2. 打開 inotify 設備
/* This simply opens the inotify node in dev (read only) */
int open_inotify_dev ()
{
int fd;
fd = open("/dev/inotify", O_RDONLY);
if (fd
{
perror ("open(\"/dev/inotify\", O_RDONLY) = ");
}
return fd;
}
這對任何一個在 Linux 系統(tǒng)上進行過文件編程的人來說都應該是熟悉的。
清單 3. 實際的事件處理例程
/* This method does the dirty work of determining what happened,
then allows us to act appropriately
*/
void handle_event (struct 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 語句中,您可以隨意執(zhí)行任意已實現(xiàn)并且滿足需要的方法。
至于性能監(jiān)控,您可以確定哪些文件是最經(jīng)常被讀取的和它們打開的持續(xù)時間。這種監(jiān)控非常方便,因為在某些情況下,如果文件在短時間內(nèi)被應用程序重復地讀取,它會將文件緩存在內(nèi)存中而不用返回磁盤去讀取,從而提高性能。
很容易舉出一些執(zhí)行有趣操作的特定于事件的處理器的例子。比如,如果您是在為底層文件系統(tǒng)實現(xiàn)一個元數(shù)據(jù)存儲索引,您可能會尋找文件創(chuàng)建事件,不久還會在該文件上觸發(fā)一個元數(shù)據(jù)挖掘操作。在安全環(huán)境中,如果文件被寫入一個無人可以寫入的目錄,您會觸發(fā)某些形式的系統(tǒng)警報。
請注意,inotify 支持許多非常細粒度的事件 —— 例如 CLOSE 與 CLOSE_WRITE。
本文中的代碼所列舉的許多事件,可能您并不希望在每次代碼運行時都看到。實際上,只要可能,您可以并且應該只請求對您的應用程序有用的事件子集。出于測試目的,本文章提供的代碼通過嚴格使用完整掩碼(如可下載的示例代碼[請參閱
參考資料
] 中 main 方法的第 51 行附近或者上面的
清單 1
中的第 29 行所執(zhí)行的)展示了許多事件。應用程序員通常想要有更多選擇,而您則需要更特定的掩碼來滿足您的需要。這使您可以從上述的 handle_event() 方法中的 catch 語句刪除不感興趣的條目。
結(jié)束語
當應用于性能監(jiān)控、調(diào)試和自動化領(lǐng)域時,inotify 是一種用于監(jiān)控 Linux 文件系統(tǒng)的、強大且細粒度的機制。使用本文提供的代碼,您就可以編寫能夠以最低的性能開銷響應或記錄文件系統(tǒng)事件的應用程序。
下載
描述
名字
大小
下載方法
Sample code used in this article
inotify-sample.tar
30 KB
HTTP
參考資料
獲取 Robert Love 提供的最新
Inotify 補丁
。
從
Linux Kernel Archives
獲取當前 Linux 內(nèi)核源文件目錄樹。這里是獲取 Linux 內(nèi)核源代碼的正規(guī)地點。
Event Management Best Practices
(IBM 紅皮書,2004 年 6 月)聚焦于最佳實踐,深入廣泛地理解了事件管理,并考查了事件過濾、復制檢測、相關(guān)性、通知、逐步升級、同步、故障單整合、維護模式和自動化。
“
Use autonomic computing for problem determination
”(developerWorks,2004 年 6 月),描述如何在 IT 系統(tǒng)中使用自治系統(tǒng)來監(jiān)控事件和診斷錯誤條件,并提供校正操作。
“
商業(yè)服務網(wǎng)格,第 3 部分: 設置規(guī)則
”(developerWorks,2003 年 4 月),展示了如何為服務級別定義、服務選擇、安全、錯誤恢復、
事件監(jiān)控
和服務映射配置網(wǎng)格系統(tǒng)的策略文件。
在
developerWorks Linux 專區(qū)
可以找到更多為 Linux 開發(fā)者準備的參考資料。
通過參與
developerWorks blogs
加入 developerWorks 社區(qū)。
在 Developer Bookstore 的 Linux 區(qū)購買
打折出售的 Linux 書籍
。
訂購免費 SEK for Linux
,這套 DVD(兩張),包含了來自 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere® 的用于 Linux 的最新 IBM 試用軟件。
使用
IBM 試用軟件
改革您的下一個 Linux 開發(fā)項目,可直接從 developerWorks 下載。
本文來自ChinaUnix博客,如果查看原文請點:http://blog.chinaunix.net/u2/66039/showart_2036772.html |
|