一、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;
}