epoll模型添加inotify事件的代码实现

#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保存触发事件的文件名。

我们可以通过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就可以进行进一步的操作了。




你可能感兴趣的:(基础知识,c++,epoll,inotify)