1. 程式人生 > >linux 系統呼叫 inotify & epoll

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 
版權宣告:本文為博主原創文章,轉載請附上博文連結!