Linux下inotify的基本使用及註意事項
最近在寫一個雲備份的項目,其中有一個模塊是要監控計算機本地的文件,於是我翻了翻linux/unix系統編程手冊發現了inotify這個用於文件監控的框架
1.概述
1)inotify機制可用於監控文件或目錄。當監控目錄時,與該目錄自身以及該目錄下面的文件都會被監控,其上有事件發生時都會通知給應用程序
2)inotify監控機制為非遞歸,若應用程序有意監控整個目錄子樹內的事件,則需對該樹中的每個目錄發起inotify_add_watch()調用
3)可使用select(),poll(),epoll()以及由信號驅動的I/O來監控inotify文件描述符
2.inotify API
1)inotify_init()系統調用可創建一新的inotify實例
- #include<sys/inotify.h>
- int inotify_init(void);
該函數的返回值為一個文件描述符,我們可以簡單的理解為該文件描述符所指代的文件中將會保存類似所監控的目錄所發生的事件集
2)針對fd所指的inotify實例的監控列表,系統調用inotify_add_watch()可以追加新的監控項
[cpp] view plain copy- #include<sys/inotify.h>
- int inotify_add_watch(int fd,const char *pathname,uint32_t mask);
參數pathname為想要創建的監控項所對應的文件,特別註意調用該接口必須要對該文件有讀權限,該函數只對文件做一次檢查,如果在監控時修改了所監控的文件讀權限,則不會影響繼續監控此文件
參數mask為一位掩碼,針對pathname定義了想要監控的事件,此函數的返回值為一個用於唯一指代此監控項的描述符
3.inotify事件
IN_ACCESS 文件被訪問
IN_ATTRIB 文件元數據改變
IN_CLOSE_WRITE 關閉為了寫入而打開的文件
IN_CREATE 在受監控目錄下創建了文件或目錄
IN_DELETE 在受監控目錄內刪除了文件或目錄
IN_DELETE_SELF 刪除了受監控目錄/文件本身
IN_MODIFY 文件被修改
IN_MODIFY_SELF 移動受監控目錄或文件本身
IN_MOVED_FROM 文件移除受監控目錄
IN_MOVED_TO 將文件移到受監控目錄
IN_OPEN 文件被打開
IN_ALL_EVENTS 以上所有輸出事件的統稱
IN_MOVE IN_MOVED_FROM | IN_MOVED_TO事件的統稱
IN_ONESHOT 只監控pathname的一個事件
IN_ONLYDIR pathname不為目錄時會失敗
對於上述一些事件的說明:
1)當文件的元數據(比如,權限,所有權,鏈接計數,擴展屬性,用戶ID,或組ID等)改變時,會發生IN_ATTRIB事件
2)刪除受監控對象時會發生IN_DELETE_SELF
3)重命名對象時會發生IN_MORE_SELF事件
4)ONE_SHOT允許只監控pathname的一個事件,事件發生後,監控項會自動從監控列表消失
4.讀取inotify事件
將監控項項在監控列表中登記後,應運程序可用read()從inotify的文件描述符中讀取事件,以判定發生了那些事件。若讀取之時還沒有發生任何事件,則read()會阻塞,直至有事件產生,事件發生後,每次調用read()會返回一個緩存區,內含一個或多個如下類型的結構體
[cpp] view plain copy- struct inotify_event
- {
- <span style="white-space:pre"> </span>int wd;
- <span style="white-space:pre"> </span>uint32_t mask;
- <span style="white-space:pre"> </span>uint32_t cookie;
- <span style="white-space:pre"> </span>uint32_t len;
- <span style="white-space:pre"> </span>char name[];
- }
.字段wd指名發生事件的是哪個監控描述符,該字段值由之前對inotify_add_watch()的調用返回。因為用read()讀到的是inotify文件中的所有事件,可是當inotify同時監控多個目錄或文件時我們應該如何區分讀到的內容呢,這是wd就派上了用場,我們可以用wd來做區分
.mask字段返回了描述該事件的位掩碼
.cookie字段將相關事件聯系到一起,目前只有對重命名時才會用到
.len字段表示實際分配給name字段的字節數
.name字段則是標示該文件
5.簡單程序事例
[cpp] view plain copy- #include<stdio.h>
- #include<assert.h>
- #include<unistd.h>
- #include<stdlib.h>
- #include<errno.h>
- #include<string.h>
- #include<sys/types.h>
- #include<sys/inotify.h>
- #include<limits.h>
- #include<fcntl.h>
- #define BUF_LEN 1000
- void displayInotifyEvent(struct inotify_event *i)
- {
- printf(" wd = %2d; ",i->wd);
- if(i->cookie > 0)
- {
- printf("cokkie = %4d; ",i->cookie);
- }
- printf("mask = ");
- if(i->mask & IN_ACCESS) printf("IN_ACCESS\n");
- if(i->mask & IN_DELETE_SELF) printf("IN_DELETE_SELF\n");
- if(i->mask & IN_MODIFY) printf("IN_MODIFY\n");
- if(i->mask & IN_OPEN) printf("IN_OPEN\n");
- if(len > 0)
- {
- printf("name = %s\n",i->name);
- }
- }
- int main(int argc,char **argv)
- {
- int inotifyFd,wd,j;
- char buf[BUF_LEN];
- ssize_t numRead;
- char *p;
- struct inotify_event *event;
- int flags;
- if(argc < 2 )
- {
- printf("error\n");
- }
- inotifyFd = inotify_init();
- if(inotifyFd == -1)
- {
- printf("初始化失敗");
- }
- wd = inotify_add_watch(inotifyFd,argv[1],IN_ALL_EVENTS);
- if(wd == -1)
- {
- printf("error\n");
- }
- printf("Watching %s using wd %d\n",argv[1],wd);
- while(1)
- {
- numRead = read(inotifyFd,buf,BUF_LEN);
- if(numRead == -1)
- {
- printf("read error\n");
- }
- printf("Read %ldbytes from inotify fd\n",(long)numRead);
- for(p=buf;p < buf+numRead;)
- {
- event = (struct inotify_event *)p;
- displayInotifyEvent(event);
- p+=sizeof(struct inotify_event) + event->len;
- }
- }
- return 0;
- }
Linux下inotify的基本使用及註意事項