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