linux 系统调用 inotify & epoll

一、inotify
作用: 监控一个目录下文件的增加、删除事件

1.重要的数据结构
// 发生的event结构
struct inotify_event {
    __s32       wd;         /* watch descriptor */
    __u32       mask;       /* 表明add /remove 事件  IN_CREATE, IN_DELETE, IN_OPEN, IN_CLOSE*/                      
    __u32       cookie;     /* cookie to synchronize two events */
    __u32       len;        /* name长度 */
    char        name[0];    /* 指针,指向 add/remove 的文件名字*/
};

2.主要函数
// 1. 初始化
 mINotifyFd = inotify_init();

----------


// 2. 设置监听目录 
 inotify_add_watch(mINotifyFd, "/work/test/tmp", IN_DELETE | IN_CREATE);//监听xxx目录下的 delete、create事件

----------


// 3. 通过读取fd,读出  inotify_event 事件(阻塞),并处理.(读出的数据按照inotify_event 结构排布)

  read(mINotifyFd,buf,MAXCOUNT);

3.demo 检测一个目录下文件的 add/remove
#include <assert.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <memory.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/inotify.h>

#define MAXCOUNT 500

// 3. read & process
int process_inotifyevent(int fd)
{
                int count = 0;
                struct inotify_event *event;
                struct inotify_event  * cur ;
                char buf[MAXCOUNT];


                count =read(fd,buf,MAXCOUNT);

                // 读取出来的数据量小于一个event事件,error
                if(count < sizeof(*event))
                {
                                printf("error event\n");
                                return -1;
                }

                cur = (struct inotify_event  *) buf;

                while(count >= sizeof(*event))
                {

                                if(cur->len > 0)  // name 长度
                                {
                                                printf("have event\n");
                                                if(cur->mask & IN_CREATE)

                                                                printf("create file,file name = %s\n",cur->name);

                                                if(cur->mask & IN_DELETE)

                                                                printf("delete file,file name = %s\n",cur->name);
                                }

                                else
                                                printf("no  event\n");

                                count -=  sizeof(*event);
                                cur += 1;
                }

                return 0;
}


int main(int argc, char** argv)
{
                int mINotifyFd=0;

                // 1
                mINotifyFd = inotify_init();


                // 2
                inotify_add_watch(mINotifyFd, "/work/test/tmp", IN_DELETE | IN_CREATE);//监听xxx目录下的 delete、create事件


                while(1)
                {
                    process_inotifyevent(mINotifyFd);
                }

                return 0;


}


二、epoll
作用:检测一个或多个文件的可读、可写属性变化

1.数据结构


 // 感兴趣的事件和被触发的事件  
struct epoll_event {  
    __uint32_t events; /* Epoll 事件 :  EPOLLIN、EPOLLOUT等感兴趣的事件 */  
    epoll_data_t data; /* ☆下面的联合体结构:一般用作保存事件的fd或者其他,根据需求赋值  */  
};  

(1)events
    events可以是以下几个宏的集合:
    EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
    EPOLLOUT:表示对应的文件描述符可以写;
    EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
    EPOLLERR:表示对应的文件描述符发生错误;
    EPOLLHUP:表示对应的文件描述符被挂断;
    EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
    EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里


(2)data 
    //保存触发事件的某个文件描述符相关的数据(与具体使用方式有关,加入检测前赋值!!)  

    typedef union epoll_data {  
        void *ptr;  
        int fd;      // 保存文件描述符
        __uint32_t u32;  
        __uint64_t u64;  
    } epoll_data_t;  


2. 函数

// 1. 创建一个epoll的句柄。自从linux2.6.8之后,size参数是被忽略的(实际测试0不可以)。 
  int epoll_create(int size);

----------


// 2. 添加监听文件fd
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

 /*
第一个参数是epoll_create()的返回值。

第二个参数表示动作,用三个宏来表示:
    EPOLL_CTL_ADD:注册新的fd到epfd中;      ☆添加一个新的监听
    EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
    EPOLL_CTL_DEL:从epfd中删除一个fd;

第三个参数是需要监听的fd。

第四个参数是告诉内核需要监听什么事,struct epoll_event结构如下:
一般先将epoll_event->events赋值为EPOLLIN,
 epoll_event->data->xx  是一个联合体根据需求赋值,赋值fd或者其他
*/


----------


// 3.收集在epoll监控的事件中发生的事件

 int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

 /*
 参数1 events是分配好的epoll_event结构体数组,epoll将会把发生的事件赋值到events数组中
 (events不可以是空指针,内核只负责把数据复制到这个events数组中,不会去帮助我们在用户态中分配内存)

 参数2 maxevents告之内核这个events有多大,这个 maxevents的值不能大于创建epoll_create()时的size

 参数3 timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。

 返回值:如果函数调用成功,返回对应I/O上已准备好的文件描述符数目,如返回0表示已超时。
*/

3. demo 检测多个文件是否可读
#include <sys/epoll.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>

#define MAXCOUNT 500

struct epoll_event mPendingEventItems[10];
char buf[MAXCOUNT];

int main(int argc ,char ** argv)
{
                int mEpollFd;
                int fd;
                int i;
                struct epoll_event eventItem;

                if (argc<2)
                {
                                printf("usage: %s <filename1 filename2>\n",argv[0]);
                                return -1;
                }


                // 1.init
                mEpollFd = epoll_create(10); 


                // 2. open file & 监听 fd
                for( i=1; i<argc; i++)
                {
                                fd = open(argv[i],O_RDWR);
                                eventItem.events = EPOLLIN;
                                eventItem.data.fd = fd;
                                epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem);
                }

                // 3. wait & process
                while(1)
                {

                                int pollResult = epoll_wait(mEpollFd, mPendingEventItems, 10, -1);

                                for (i=0; i<pollResult; i++)
                                {
                                                if(mPendingEventItems[i].events == EPOLLIN) // 确保可读事件发生
                                                {
                                                                memset(buf,0,MAXCOUNT);
                                                                int count = read(mPendingEventItems[i].data.fd,buf,MAXCOUNT);
                                                                buf[count] = '\0';
                                                                printf("have data:%s\n",buf);
                                                }
                                }
                }
}


三、inotify & epoll 混合使用 监测一个目录下文件的增减及新增文件的可读性变化
(EventHub.cpp就是这么一个套路)

#include <assert.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <memory.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/inotify.h>
#include <sys/epoll.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>


#define MAX_FILES 1000
#define EPOLL_COUNT 20
#define MAXCOUNT 500
static char *epoll_files[MAX_FILES];

static struct epoll_event mPendingEventItems[EPOLL_COUNT];

int mINotifyFd,mEpollFd,i;

char inotifyBuf[MAXCOUNT];

char epollBuf[MAXCOUNT];

typedef struct t_name_fd {
    int fd;
    char name[30];

}  T_name_fd;


T_name_fd  t_name_fd[100];
int count_name_fd;

int getfdFromName(char* name)
{
    int i;
    for(i=0; i<MAX_FILES; i++)
    {
        if (!epoll_files[i])
            continue;

        if(0 == strcmp(name,epoll_files[i]))
        {
            return i;

        }
    }

    return -1;
}

int main(int argc, char** argv)
{

                struct epoll_event eventItem;

                struct inotify_event  inotifyEvent;

                struct inotify_event*  curInotifyEvent;
                char name[30];
                int readCount = 0;
                int fd;

                // 1. init inotify &  epoll
                mINotifyFd = inotify_init();
                mEpollFd = epoll_create(10);


                // 2.add inotify watch dir
                inotify_add_watch(mINotifyFd, "/work/test/tmp", IN_DELETE | IN_CREATE);//监听xxx目录下的 delete、create事件

                // 3. add inotify fd to epoll
                eventItem.events = EPOLLIN;
                eventItem.data.fd = mINotifyFd;
                epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);


                while(1)
                {


                                // 4.epoll检测文件的可读变化
                                int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_COUNT, -1);
                                printf("pollResult = %d\n",pollResult);


                                for(i=0; i <pollResult; i++)
                                {
                                                // 5. inotify 监听的fd发生变化
                                                if(mPendingEventItems[i].data.fd == mINotifyFd)
                                                {

                                                                printf("dir changer------------------\n");

                                                                /* 读取inotify事件,查看是add 文件还是remove文件,
                                                                add 需要将其添加到epoll中去,
                                                                remove 需要从epoll中移除
                                                                */
                                                                readCount  = 0;
                                                                readCount = read(mINotifyFd,inotifyBuf,MAXCOUNT);

                                                                if (readCount <  sizeof(inotifyEvent))
                                                                {
                                                                                printf("eorr inofity event\n");
                                                                                return -1;
                                                                }

                                                                // cur 指针赋值
                                                                curInotifyEvent = (struct inotify_event*)inotifyBuf;

                                                                while(readCount >= sizeof(inotifyEvent))
                                                                {
                                                                    if (curInotifyEvent->len > 0)
                                                                        {
                                                                                if(curInotifyEvent->mask & IN_CREATE)
                                                                                {
                                                                                                printf("add file :%s\n",curInotifyEvent->name);

                                                                                                sprintf(name,"/work/test/tmp/%s",curInotifyEvent->name);
                                                                                                // 打开文文件并将其添加到epoll
                                                                                                 fd=open(name,O_RDWR);
                                                                                                eventItem.events = EPOLLIN;
                                                                                                eventItem.data.fd = fd;
                                                                                                epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem);

                                                                                                // 关联name fd
                                                                                                epoll_files[fd] = name;

                                                                                               printf("file name ====== :%s\n",name);


                                                                                }
                                                                                else if(curInotifyEvent->mask & IN_DELETE)
                                                                                {
                                                                                               sprintf(name,"/work/test/tmp/%s",curInotifyEvent->name);

                                                                                                fd = getfdFromName(name);
                                                                                               printf("remove file :%s,fd = %d\n",name,fd);

                                                                                                if (fd >=0)
                                                                                                    {
                                                                                                        epoll_ctl(mEpollFd, EPOLL_CTL_DEL, fd, NULL);
                                                                                                        epoll_files[fd] = NULL;
                                                                                                    }       

                                                                                }
                                                                        }
                                                                     curInotifyEvent --;
                                                                     readCount -= sizeof(inotifyEvent);
                                                                }

                                                }


                                                //6. 其他原有的fd发生变化
                                                else
                                                {
                                                                printf("file changer------------------\n");


                                                                readCount = 0;
                                                                readCount = read(mPendingEventItems[i].data.fd,epollBuf,MAXCOUNT);
                                                                if(readCount > 0)
                                                                {
                                                                                epollBuf[readCount] = '\0';
                                                                                printf("file can read, fd: %d, countent:%s",mPendingEventItems[i].data.fd,epollBuf);
                                                                }
                                                }


                                }
                }

                return 0;


}


--------------------- 
作者:这个ID洒家要了 
来源:CSDN 
原文:https://blog.csdn.net/u012719256/article/details/52944001 
版权声明:本文为博主原创文章,转载请附上博文链接!

猜你喜欢

转载自blog.csdn.net/lucky_liuxiang/article/details/83579812