一、epoll系统调用
epoll是Linux特有的I/O复用函数。它的实现和使用上与select、poll有很大的差异。注意epoll是使用一组函数来完成任务的,而不是单个函数。其次,epoll把用户关心的文件描述符上的事件放在内核的一个事件表里面,从而无需像select和poll那样每次调用都要重复传入文件描述符或事件集。
二、内核事件表
#include
int epoll_create(int size);
#include
int epoll_ctl(int epfd,int op,int fd,struct epoll_event *event)
EPOLL_CTL_ADD //往事件表上注册fd的事件
EPOLL_CTL_MOD //修改fd上的注册事件
EPOLL_CTL_DEL //删除fd上的注册事件
struct epoll_event
{
_uint32_t events; //epoll事件
epolla_data_t data; //用户数据
}
其中event成员描述事件类型。epoll支持的事件类型和poll基本相同。表示epoll事件类型的宏是在poll对应的宏前面加上"E",比如epoll的数据可读事件是EPOLLIN。但epoll有两个额外的事件类型----EPOLLET和EPOLLONESHOT。data成员用于存储用户数据。
typedef union epoll_data
{
void* ptr;
int fd;
uint32_t u32;
unit64_t u64;
} epoll_data_t;
epoll_data_t是一个联合体,四个成员中用的最多的是fd,它指定事件所从属的目标的文件描述符。ptr成员可用来指定与fd相关的用户数据。但由于epoll_data_t是一个联合体,所以不能同时使用其ptr成员和fd成员,因此,如果要将文件描述符和用户数据关联起来,实现快速的数据访问,只能使用其他的手段,比如放弃epoll_data_t的fd成员,而在ptr指向的用户数据中包含fd。
epoll_ctl成功时返回0,失败返回-1并设置error。
三、epoll_wait函数
epoll系列系统调用的主要接口是epoll_wait函数。它在一段超时时间内等待一组文件描述符上的事件,其原型如下:
#include
int epoll_wait(int epfd,struct epoll_event* events,int maxevents,int timeout);
该函数成功时返回就绪的文件描述符的个数,失败时返回-1并设置error。
//如何索引poll返回的就绪文件描述符
int ret=poll(fds,MAX_EVENT_NUMBER,-1);
//必须遍历所有已注册文件描述符并找到其中的就绪者
for(int i=0;i<MAX_EVENT_NUMBET;++i)
{
if(fds[i].revents &POLLIN)
{
int sockfd=fds[i].fd;
}
}
//如何索引epoll返回的就绪文件描述符
int ret=epoll_wait(epollfd,events,MAX_EVENT_NUMBER,-1);
//仅遍历就绪的ret个文件描述符
for(int i=0;i<ret;++i)
{
int sockfd=events[i].data.fd;
//sockfd肯定就绪,直接处理。
}
四、LT和ET模式
epoll对文件描述符的操作有两种模式:LT(水平触发)和 ET(边沿触发)模式。LT是默认的工作模式,这种模式下epoll相当于一个效率较高的poll。当往epoll内核事件表中注册一个文件描述符上的EPOLLET事件时,epoll将以ET模式来操作该文件描述符。
可见,ET模式在很大程度上降低了同一个epoll事件呗重复触发的次数,因此效率要比LT模式高。
下面代码可以清除的体现LT和ET正在工作方式上面的差异:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAX_EVENT_NUMBER 1024
#define BUFFER_SIZE 10
//将文件描述符设置为非阻塞的
int setnonblocking(int fd)
{
int old_option=fcntl(fd,F_GETFL);
int new_option=old_option | O_NONBLOCK;
fcntl(fd,F_SETFL,new_option);
return old_option;
}
//将文件描述符fd上的EPOLLIN注册到epollfd指示的epoll内核事件表中,
//参数enable_et指定是否对fd启用ET模式
void addfd(int epollfd,int fd,bool enable_et)
{
epoll_event event;
event.data.fd=fd;
event.events=