I/O复用之epoll
操作函数
int epoll_create(int size);
参数size
可以是任何一个大于0的数字
创建一个epoll实例,返回这个实例的文件描述符
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
对epoll实例进行增删改操作
-
epfd
epoll实例对应的文件描述符 -
op
对应操作
EPOLL_CTL_ADD:add,往epoll中添加结点
EPOLL_CTL_MOD:modify,修改 epoll 中的结点
EPOLL_CTL_DEL:delete,删除 epoll 中的结点 -
fd
结点中的文件描述符 -
event
是个结构体,内部包含 events和 data-
events
要检测的事件类型,类型间用 "|" 连接表示同时检测多种事件类型
EPOLLIN:读事件,表示这个文件描述符可以读
EPOLLOUT:写事件,表示这个文件描述符可以写
EPOLLET:将 epoll 设置为边沿触发模式 ET
EPOLLRDHUP:对端关闭socket,即关闭连接时会触发
EPOLLONESHOT:仅监听此fd上的一次事件,若要再次监听这个fd,需要使用epoll_ctl()
再次注册 -
data
:
通常储存第三个参数,即结点中的文件描述符。所以是以struct epoll_event
形式往 epoll 中添加结点
-
-
返回值
失败返回-1,成功返回0
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
检测 epoll 中有无结点触发了事件,即读就绪/写就绪。如果没有就绪结点,函数会阻塞在这里
-
epfd
:同上 -
events
:是 epoll_event 类型的数组,储存了检测到就绪的结点 -
maxevents
:修饰第二个参数,规定了数组的最大容量 -
timeout
: 函数阻塞的时长 -
返回值:返回就绪的描述符数目。0表示已超时。-1表示有错误发生,需要检查 errno错误码
LT & ET
LT 水平触发
检测到事件触发通知应用程序,程序可以不立即处理。每次调用epoll_wait时都会向应用程序报告,直至程序处理。
ET 边沿触发
检测到事件触发通知应用程序,程序必须立即处理。
EPOLLONESHOT
我们设想这样一个情况,客户端发来信息,触发epoll上的读就绪事件,分配一个线程去处理报文,但此时客户端发来的报文不完整,线程就阻塞在这等待,下一次客户端发来信息再次触发读事件,可能被分配给另一个线程处理,这样就导致一份报文被两个线程处理,这不是我们所希望的。
所以我们可以把事件注册成 EPOLLONESHOT 类型,即使出现上述报文不完整的情况,客户端再次发来信息,仍不会触发事件,也就不会指派线程处理,就保证了当一个线程正在处理某个连接时,处理完成前别的线程没有机会操作。
每个socket被创建时,会被分配两个缓冲区:读缓冲区、写缓冲区。
当有信息发送来的时候,会先存放在读缓冲区中,会触发 epoll 上的文件描述符的读就绪事件。对于写就绪事件同理