epoll模型新增inotify事件的程式碼實現
#include <sys/inotify.h> #include <sys/epoll.h> #include <stdlib.h> #include <stdio.h> #include <sys/ioctl.h> #include <string.h> struct inotify_data_type{ int fd; char self_type[16]; }; int main(int argc,char* argv[]){ if(argc < 2) { return 1; } int inotify_fd = inotify_init(); if(inotify_fd < 0){ printf("Create inotify descriptor failed."); return 1; } int *wd = malloc(sizeof(int)*(argc-1)); int i; for(i = 1; i < argc; i++){ wd[i-1] = inotify_add_watch(inotify_fd,argv[i],IN_CLOSE_WRITE); if(wd[i-1] < 0){ printf("Could not watch dirctory : %s"); return 1; } } int nb = 1; ioctl(inotify_fd,FIONBIO,&nb); int epoll_fd = epoll_create(1024); if(epoll_fd < 0){ printf("Create epoll descriptor failed"); return 1; } struct inotify_data_type inotify_data; inotify_data.fd = inotify_fd; strcpy(inotify_data.self_type,"inotify"); struct epoll_event inotify_event; int option = EPOLL_CTL_ADD; inotify_event.events = EPOLLIN|EPOLLET; inotify_event.data.ptr = &inotify_data; int result = epoll_ctl(epoll_fd, option, inotify_fd, &inotify_event); if(result < 0){ printf("Could not add Inotify event in EPOLL"); return 1; } int running = 1; struct epoll_event event_list[10]; while(running){ int events_num = epoll_wait(epoll_fd, event_list,10,0); if(events_num < 0){ printf("Epoll_wait failed!"); return 1; } if(events_num > 0){ // int i = 0; for(i = 0; i < events_num; i++){ struct inotify_data_type *inotify_data_backup = event_list[i].data.ptr; if(strcmp(inotify_data_backup->self_type,"inotify") == 0){ int revents = event_list[i].events; if(revents & (EPOLLERR|EPOLLHUP)){ continue; } if(revents & EPOLLIN){ char inotify_event_buf[1024]; bzero(inotify_event_buf,1024); int length = read(inotify_data_backup->fd,inotify_event_buf,1024); //理論上,下面的程式碼不需要使用迴圈 char *tmp; int tmp_len; for(tmp = inotify_event_buf,tmp_len = 0; (tmp-inotify_event_buf) < length; tmp += tmp_ len){ struct inotify_event *iev = (struct inotify_event*)tmp; int j = 0; for(j = 0; j < argc-1; j++){ if(wd[j] == iev->wd){ if(iev->mask & IN_CLOSE_WRITE){ printf("The inotify event referred to File=%s, whose length=%d\n",iev- >name,iev->len);//顯示觸發事件的檔名 } } } tmp_len = sizeof(struct inotify_event)+iev->len; } } } } } } close(epoll_fd); }
1. inotify 簡介
inotify是linux提供的一款監視檔案系統的工具,其允許監控程式開啟一個獨立檔案描述符,針對事件集監控一個或者多個檔案,例如開啟、關閉、移動/重新命名、刪除、建立或者改變屬性。由於其實用檔案描述符,因此可以和select和epoll函式等結合使用,上面的程式碼就是一個例子。
2.介面說明
int inotify_init();
建立一個inotify例項的系統呼叫,並返回一個指向該例項的檔案描述符。
int inotify_add_watch(int fd , char *path, uint32_t mask);
增加對檔案或者目錄的監控。檔案系統的變化一個叫watches的物件管理,每個watch是一個二元組(目標,事件掩碼)。
fd:inotify 檔案描述符
path:要監控的檔案或者目錄的路徑
mask:事件掩碼,具體的操作根據掩碼不同而不同,讓我們看一下
/* events suitable for MASK parameter of INOTIFY_ADD_WATCH. */ #define IN_ACCESS 0x00000001 /* File was accessed. */ #define IN_MODIFY 0x00000002 /* File was modified. */ #define IN_ATTRIB 0x00000004 /* Metadata changed. */ #define IN_CLOSE_WRITE 0x00000008 /* Writtable file was closed. */ #define IN_CLOSE_NOWRITE 0x00000010 /* Unwrittable file closed. */ #define IN_CLOSE (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE) /* Close. */ #define IN_OPEN 0x00000020 /* File was opened. */ #define IN_MOVED_FROM 0x00000040 /* File was moved from X. */ #define IN_MOVED_TO 0x00000080 /* File was moved to Y. */ #define IN_MOVE (IN_MOVED_FROM | IN_MOVED_TO) /* Moves. */ #define IN_CREATE 0x00000100 /* Subfile was created. */ #define IN_DELETE 0x00000200 /* Subfile was deleted. */ #define IN_DELETE_SELF 0x00000400 /* Self was deleted. */ #define IN_MOVE_SELF 0x00000800 /* Self was moved. */ /* Events sent by the kernel. */ #define IN_UNMOUNT 0x00002000 /* Backing fs was unmounted. */ #define IN_Q_OVERFLOW 0x00004000 /* Event queued overflowed. */ #define IN_IGNORED 0x00008000 /* File was ignored. */ /* Helper events. */ #define IN_CLOSE (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE) /* Close. */ #define IN_MOVE (IN_MOVED_FROM | IN_MOVED_TO) /* Moves. */ /* Special flags. */ #define IN_ONLYDIR 0x01000000 /* Only watch the path if it is a directory. */ #define IN_DONT_FOLLOW 0x02000000 /* Do not follow a sym link. */ #define IN_MASK_ADD 0x20000000 /* Add to the mask of an already existing watch. */ #define IN_ISDIR 0x40000000 /* Event occurred against dir. */ #define IN_ONESHOT 0x80000000 /* Only send event once. */ /* All events which a program can wait on. */ #define IN_ALL_EVENTS (IN_ACCESS | IN_MODIFY | IN_ATTRIB | IN_CLOSE_WRITE \ | IN_CLOSE_NOWRITE | IN_OPEN | IN_MOVED_FROM \ | IN_MOVED_TO | IN_CREATE | IN_DELETE \ | IN_DELETE_SELF | IN_MOVE_SELF)
int inotify_rm_watch(fd,wd);
刪除一個watch
fd:inotify_init的返回
wd:inotify_add_watch的返回
3. inotify的事件結構
struct inotify_event{
int wd; //watch描述符
uint32_t mask; //watch掩碼
uint32_t cookie; //用於同步兩個事件的cookie
unit32_t len; //name的長度
char name[0] //name指標
};
wd 可用於判斷事件是否屬於我們所監聽的檔案系統物件,mask返回事件型別。當監控物件是目錄,且事件與目錄內的檔案有關時才會使用到name儲存觸發事件的檔名。我們可以通過read一次獲得多個事件,具體返回事件多少要看提供多達的buf
size_t len = read(fd,buf,BUFLEN);
4.核心實現原理(轉)
系統為每個inotify例項分配一個inotify_device結構
struct inotify_device {
wait_queue_head_t wq; /* wait queue for i/o */
struct idr idr; /* idr mapping wd -> watch */
struct semaphore sem; /* protects this bad boy */
struct list_head events; /* list of queued events */
struct list_head watches; /* list of watches */
atomic_t count; /* reference count */
struct user_struct *user; /* user who opened this dev */
unsigned int queue_size; /* size of the queue (bytes) */
unsigned int event_count; /* number of pending events */
unsigned int max_events; /* maximum number of events */
u32 last_wd; /* the last wd allocated */
};
該結構在使用者呼叫inotify_init時建立,當關閉對應的描述符時被釋放。
同時,系統為每一個watch例項分配一個inotify_watch結構,其在呼叫inotify_add_watch時建立,並在呼叫inotify_rm_watch或者close(fd)時銷燬:
struct inotify_watch {
struct list_head d_list; /* entry in inotify_device's list */
struct list_head i_list; /* entry in inode's list */
atomic_t count; /* reference count */
struct inotify_device *dev; /* associated device */
struct inode *inode; /* associated inode */
s32 wd; /* watch descriptor */
u32 mask; /* event mask for this watch */
};
d_list 指向所有inotify_device組成的列表
i_list 指向所有被監控的inode組成的列表
count 引用計數
dev 指向對應的inotify_device例項
inode 標明要監控的inode
wd 分配給watch的描述符
mask事件掩碼
系統為了支援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 列表的訪問。當檔案系統發生第一部分提到的事件之一時,相應的檔案系統程式碼將顯式的呼叫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_unmount_inodes,它會在檔案系統被 umount 時呼叫來通知 umount 事件給 inotify 系統。上面提到的函式最後都會呼叫inotify_inode_queue_event:
A. 判斷inode是否被監控
B. 遍歷inotify_watch列表,看是否是某個watch所關係的事件,如果是 inotify_dev_queue_event,不是則返回
而 inotify_dev_queue_event主要做一下工作:
A. 是否重複事件,是的話丟棄,不是繼續
B. 判斷inotify例項即inotify_device的事件佇列是否溢位,溢位則返回一個當前的檔案操作事件,繼續
C. 由kernel_event函式構建inotify_kernel_event結構,然後把結構插入對應inotify_device的events事件列表,喚醒wq指向的等待佇列。
整個inotify的核心工作流程就到這裡了,後面我們通過read方式讀取相應的inotify_event就可以進行進一步的操作了。