使用epoll进行多路传输

1 epoll高级文件读取模式的简介
为了解决poll和select两种传输方式的瓶颈,在linux2.6的版本中,就引入了epoll多路传输I/O的方式[内核在每个文件上加入了回调函数,当有事件发生时回调函数就会被调用,这些方式可以避免轮询,大大的减轻系统负载],称之为epoll。
每次调用poll 和 select 时,两者都需要向系统监控程序传递一个满链表的文件描述符集合。其中每一个文件描述符都必须被系统调用,假如有成千上万个文件描述符需要被监控,这两种操作模式都会成为系统的瓶颈,内核需要花费大量的时间哪个文件描述符需要被应用程序检查。
如果我们采用epoll模式的话,应用程序通过调用系统内核所提供的文件描述符列表,然后使用另外一个不同的系统调用监控这些文件描述符。当监控列表被生成后,内核会根据应用程序所关心的事情不断的监控文件描述符,当有事件发生时,内核将发出通知,告诉应用程序需要注意的事情刚刚发生。当应用程序需要询问哪一个文件描述符发生变化时,此时内核只需要通过列表查询出刚刚发生变化的文件描述符,而不用去遍历和检查所有的文件描述符,采用这种通知的方式,大大的减轻了系统遍历所消耗的时间。
2 poll select epoll三者之间比较
为了满足epoll这种通知模式,需要编写更复杂的系统调用接口。
poll: 采用pollfd结构组成的数组来存储文件描述符集合;
select: 采用三个fd_set来存储文件描述符;
epoll:将文件描述符集合移动至内核中,而不是在程序的地址空间中对文件进行描述。每一个文件集合都可通过一个epoll描述符的引用,epoll描述符是一个只为epoll系统调用使用的文件描述符。
3 epoll 高级文件操作方式实现
3.1 需引入头文件
#include < sys/epoll.h>
3.2 函数原型:
int epoll_create(int numDescriptors); //epoll开始启用 向内核申请epoll描述符数量
int close(epfd); //epoll模式使用完毕,内核释放epfd所占用的内存
int epoll_ctl(int epfd,int op,int fd,struct epoll_event *event);
int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout);//epoll所使用的系统调用,有一个或更多的正在被监控的文件描述符有数据可以读取或准备写入之前,会一直阻塞,知道timeout都耗尽,如果超时,则返回0

3.3 函数参数说明:
epoll_event:
struct epoll_event{
int events;
union{
void *ptr;
int fd;
unsigned int u32;
unsigned long long u64;
}data;
};
该结构主要指定了哪些事件会被监控,那类事件发生了,允许一个单独的数据元素与文件描述符相联系。
epoll_event.events 事件说明:
3.3.1 EPOLLIN: read操作不会阻塞,或者需要读取的数据已经准备好了,或者说没有更多的数据可以读取。
3.3.2 EPOLLOUT:相关文件已经准备好,可以被写入。
3.3.3 EPOLLPRI:epoll_event中data.fd数据已经准备好可以进行读取。
3.3.4 EPOLL_CTL_ADD:,把文件描述符fd和事件集合events增加到文件描述符集合中。如果需要添加的文件描述符已经存在,则返回EEXIST。(多线程程序访问时,可能会多次向文件集合中增加相同的文件描述符,加入此参数 可以避免相同的文件描述符被加入)。
3.3.5 EPOLL_CTL_DEL:文件描述符fd从正在被监控的文件描述符集合中删除。
3.3.6 EPOLL_CTL_MOD:对fd所对应的epoll_event中的events所指向的信息进行更新。这种更新允许更新正在被监控的事件集合。
3.3.7 EPOLLERR:文件发生了一个未解决的错误;如果应用程序不从socket读取或写入时,就会有错误发生在socket上。
3.3.8 EPOLLHUP:文件描述符发生一个挂起操作

timeout:超时时间设定(以秒为单位)

函数源码使用

:#include < fcntl.h>
#include < stdio.h>
#include < sys/epoll.h>
#include < unistd.h>

void addEvent(int epfd,char *fileName){
int fd;
struct epoll_event event;

if((fd = open(fileName,O_RDONLY|O_NONBLOCK)) <0){ printf("open filename:%s is error1\n",fileName); exit(0); } event.events = EPOLLIN; event.data.fd = fd; if(epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&event)){ printf("epoll_ctl(add) is error!\n"); exit(1); } } int main(void){ char buf[4096]; int i,rc; int epfd; struct epoll_event events[2]; int num; int numFds; epfd = epoll_create(2); if(epfd<0){ printf("epoll_create \n"); return 1; } addEvent(epfd,"p1"); addEvent(epfd,"p2"); numFds =2; while(numFds){ if((num = epoll_wait(epfd,events,sizeof(events)/sizeof(*events),-1)) <=0){ printf("epoll_wait error!\n"); return 1; } for(i=0;ipoll 文件读取模式
select文件读取模式
poll 同select 对比