#include
#include
#include
#include
#include
#include
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保存触发事件的文件名。
size_t len = read(fd,buf,BUFLEN);
系统为每个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
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就可以进行进一步的操作了。