相比于select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。因为在内核中的select实现中,它是采用轮询来处理的,轮询的fd数目越多,自然耗时越多。
这是一张epoll工作原理的汇总图。
epoll 的接口
epoll_create 创建一个 epoll 对象
int epoll_create(int size);
- epoll_create返回一个句柄,之后 epoll的使用都将依靠这个句柄来标识;
- size参数只是应用程序向操作系统提的建议,操作系统并不一定生成size大小的epoll例程;
- 当创建好epoll句柄后,它就是会占用一个fd值,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。
- 成功返回epoll文件描述符,失败返回-1;
epoll_ctl 向 epoll 对象中添加要管理的连接
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
- epoll_ctl向 epoll对象中添加、修改或者删除感兴趣的事件,返回0表示成功,否则返回–1
- epfd:epoll_create返回的句柄;
- op :
- EPOLL_CTL_ADD:注册新的fd到epfd中;
- EPOLL_CTL_DEL:从epfd中删除一个fd;
- EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
- fd:需要监听的socket句柄fd;
- event:告诉内核需要监听什么事的结构体。
epoll_wait等待其管理的连接上的IO事件
int epoll_wait(int epfd,struct epoll_event* events,int maxevents,int timeout);
- 收集在 epoll监控的事件中已经发生的事件,如果 epoll中没有任何一个事件发生,则最多等待timeout毫秒后返回;
- epoll_wait的返回值表示当前发生的事件个数,如果返回0,则表示本次调用中没有事件发生,如果返回–1,则表示出现错误;
- epfd:epoll的描述符;
- events:分配好的 epoll_event结构体数组;
- maxevents:表示本次可以返回的最大事件数目,通常 maxevents参数与预分配的events数组的大小是相等的;
- timeout:表示在没有检测到事件发生时最多等待的时间(单位为毫秒),如果 timeout为0,则表示 epoll_wait在 rdllist链表中为空,立刻返回,不会等待。
epoll 的两种工作模式
- LT(水平触发)模式
- 只要引起epoll_wait返回的事件还存在,那么再次调用epoll_wait时,这个事件还会被注册。
- ET(边缘出发)模式
- 每个事件在刚发生的时候注册一次,之后就不会再被注册,除非又有新的事件发生。
epoll 的使用方式
以下为epoll应用的伪代码
#include <sys/epoll.h>
int main(){
int fd = socket(AF_INET, SOCK_STREAM, 0);
bind(fd, ...)
listen(fd, ...)
int epfd = epoll_create(...);
epoll_ctl(epfd, ...); //将所有需要监听的socket添加到epfd中
while(1){
int n = epoll_wait(...)
for(接收到数据的socket){
//处理
}
}
}
如果想要深入理解epoll底层原理,可以读一下这篇文章:linux网络编程之带你了解epoll的本质(内部实现原理) - 知乎