1. 程式人生 > >inotify機制監控文件系統事件原理及使用

inotify機制監控文件系統事件原理及使用

direct esc his 存儲 cname smo gin related oca

1.基本描述

inotify提供了一種監控文件系統事件的機制,可以用來監控單個的文件以及目錄。當一個目錄被監控,inotify會返回該目錄以及該目錄下面文件的事件。

2.原理以及使用

2.1內核原理

inotify機制借用了內核裏面的notify通知鏈技術,針對文件系統裏面的使用主要是在inode結構體裏面加入了相關的字段(內核版本3.10.0-327):

struct inode {
。。。
#ifdef CONFIG_FSNOTIFY
__u32 i_fsnotify_mask; /* all events this inode cares about */ 具體可能監控的事件,事件基本上是一些位段
struct hlist_head i_fsnotify_marks; /* 具體的鏈表,鏈表上可以掛入多個mask結構(事件) */
#endif


??。。。
}

struct fsnotify_mark {
__u32 mask; /* mask this mark is for */
atomic_t refcnt; /* active things looking at this mark */
struct fsnotify_group *group; /* group this mark is for */
struct list_head g_list; /* list of marks by group->i_fsnotify_marks */
spinlock_t lock; /* protect group and inode */
union {
struct fsnotify_inode_mark i;


struct fsnotify_vfsmount_mark m;
};
__u32 ignored_mask; /* events types to ignore */
unsigned int flags; /* vfsmount or inode mark? */
struct list_head destroy_list;
void (*free_mark)(struct fsnotify_mark *mark); /* called on final put+free */
};

具體的關系結構圖如圖所示:

技術分享圖片

圖1 inotify機制內核下面各結構體關系圖

2.2用戶態接口原理

用戶態接口的基本思路是初始化一個具體的inotify實例,並設置實例以及具體監控哪些事件。當有具體的事件以後可以讀取對應的

結構體來解析。事件結構體為:

struct inotify_event {
int wd; /* Watch descriptor */ 監控描述符
uint32_t mask; /* Mask of events */ 具體的事件(文件創建、刪除、屬性修改等)
uint32_t cookie; /* Unique cookie associating related
events (for rename(2)) */
uint32_t len; /* Size of name field */
char name[]; /* Optional null-terminated name */ 具體的文件名
};

具體可以監控的事件主要有:(註釋比較清晰了)

IN_ACCESS File was accessed (read) (*).
IN_ATTRIB Metadata changed, e.g., permissions, timestamps, extended attributes, link count (since Linux 2.6.25), UID, GID, etc. (*).
IN_CLOSE_WRITE File opened for writing was closed (*).
IN_CLOSE_NOWRITE File not opened for writing was closed (*).
IN_CREATE File/directory created in watched directory (*).
IN_DELETE File/directory deleted from watched directory (*).
IN_DELETE_SELF Watched file/directory was itself deleted.
IN_MODIFY File was modified (*).
IN_MOVE_SELF Watched file/directory was itself moved.
IN_MOVED_FROM Generated for the directory containing the old filename when a file is renamed (*).
IN_MOVED_TO Generated for the directory containing the new filename when a file is renamed (*).
IN_OPEN File was opened (*).

主要的用戶態接口函數:

int inotify_init(void);
int inotify_init1(int flags);

初始化一個inotify實例並且返回一個文件描述符,inotify實例和一個evnet隊列掛鉤,失敗返回-1。

int inotify_add_watch(int fd, const char *pathname, uint32_t mask);

添加一個watch對象,fd為具體的inotify實例描述符,pathname為監控的目錄或者文件,mask為具體的事件,成功返回非負整數,失敗返

回-1.

int inotify_rm_watch(int fd, int wd);

刪除一個watch,fd為inotify實例描述符,wd為watch描述符。成功返回0,失敗返回-1.

3.測試程序

#include <sys/inotify.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/select.h>

#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>

/*
* struct inotify_event {
* int wd; // Watch descriptor
* uint32_t mask; // Mask of events
* uint32_t cookie; // Unique cookie associating related events (for rename(2))
* uint32_t len; // Size of name field
* char name[]; // Optional null-terminated name
* };
*
**/

int giNotifyFd;
int giaWds[20];
int giCount;

int watch_inotify_events(int fd)
{
char event_buf[512];
int ret;
int event_pos = 0;
int event_size = 0;
struct inotify_event *event;
time_t tNow;
struct tm *pTimeNow;

/* 讀事件是否發生,沒有發生就會阻塞 */
ret = read(fd, event_buf, sizeof(event_buf));

/* 如果read的返回值,小於inotify_event大小出現錯誤 */
if (ret < (int)sizeof(struct inotify_event)){
printf("counld not get event!\n");
return -1;
}
/* 因為read的返回值存在一個或者多個inotify_event對象,需要一個一個取出來處理 */
while (ret >= (int)sizeof(struct inotify_event)) {
event = (struct inotify_event*)(event_buf + event_pos);
if (event->len) {
// time(&tNow); /* 這三行可以註釋掉,之前出現過加了這三行執行出現core dump的問題。 */
// pTimeNow = localtime(&tNow);
// printf("Local time is:%s", asctime(pTimeNow));

if(event->mask & IN_CREATE){
printf("watch is %d, create file: %s\n", event->wd, event->name);
} else {
printf("watch is %d, delete file: %s\n", event->wd, event->name);
}
if (event->mask & IN_ATTRIB) {
printf("watch is %d, modify file attribute: %s\n", event->wd, event->name);
}
}
/* event_size就是一個事件的真正大小 */
event_size = sizeof(struct inotify_event) + event->len;
ret -= event_size;
event_pos += event_size;
}
return 0;
}

/* 遞歸處理目錄 */

void init_all_iwds(char *pcName)
{
int iWd;
struct stat tStat;
DIR *pDir;
struct dirent *ptDirent;
char caNametmp[100]; // 存儲目錄名字

iWd = inotify_add_watch(giNotifyFd, pcName, IN_CREATE|IN_DELETE|IN_ATTRIB|IN_MODIFY);
giaWds[giCount] = iWd;
giCount++;

if (-1 == stat(pcName, &tStat)) {
printf("stat %s error\n", pcName);
return;
}
if (!S_ISDIR(tStat.st_mode))
return;
/* now the child dir */
pDir = opendir(pcName);
if (NULL == pDir) {
printf("opendir %s error\n", pcName);
return;
}

// 循環讀目錄下面的子項
while (NULL != (ptDirent = readdir(pDir))) {
if ((0 == strcmp(ptDirent->d_name, ".")) || (0 == strcmp(ptDirent->d_name, "..")))
continue; // 跳過當前目錄和上一級父目錄
// printf("sub name is %s, d_type is 0x%x\n", ptDirent->d_name, ptDirent->d_type);
sprintf(caNametmp, "%s/%s", pcName, ptDirent->d_name); //獲取子目錄或文件名字
if (-1 == stat(caNametmp, &tStat)) {
printf("stat error:%s\n", caNametmp); // 獲取統計數據
return;
}
if (!S_ISDIR(tStat.st_mode)) //看是否是子目錄,原則只處理目錄
continue;

printf("sub absoulte dir name is %s\n", caNametmp);
// iWd = inotify_add_watch(giNotifyFd, caNametmp, IN_CREATE|IN_DELETE|IN_ATTRIB|IN_MODIFY);
init_all_iwds(caNametmp); //處理子目錄
}

// 關閉
closedir(pDir);
}

int main(int argc, char** argv)
{
int iNotifyRet;
fd_set fds;

int iaWd[10];

int icount = 0;

if (argc != 2) {
printf("Usage: %s <dir>\n", argv[0]);
return -1;
}

/* inotify初始化 */
iNotifyFd = inotify_init();
if (iNotifyFd == -1) {
printf("inotify_init error!\n");
return -1;
}

/* 遞歸處理具體的目錄,添加watch對象 */
init_all_iwds(argv[1]);

/* 處理事件 */
while (1) {
FD_ZERO(&fds);
FD_SET(iNotifyFd, &fds);

if (select(iNotifyFd+1, &fds, NULL, NULL, NULL) > 0) {
iNotifyRet = watch_inotify_events(iNotifyFd);
if (-1 == iNotifyRet)
break;
}
}

/* 刪除inotify的watch對象 */

for (icount = 0; icount < giCount; icount++) {

if (inotify_rm_watch(iNotifyFd, giaWds[icount ]) == -1) {
printf("notify_rm_watch %d error!\n", giaWds[icount]);
return -1;
}

}

/* 關閉inotify描述符 */
close(iNotifyFd);

return 0;
}

文件命名為:inotify.c

編譯: gcc -o inotify inotify.c 生成可執行文件

執行: ./inotify輸出 Usage: ./inotify <dir> 提示需要輸入具體監控的目錄或者文件。

執行: ./inotify /home/work/0604_inotify/ &

創建 aaa 文件,打印出 watch is 1, create file: aaa

修改aaa文件屬性,打印出 watch is 1, modify file attribute: aaa

inotify機制監控文件系統事件原理及使用