深入理解Linux内核IO复用--epoll

epoll是什么?

提到epoll,我们不得不先去了解IO复用下的另外两个重要的机制:selectpoll
先看看Linux内核中这三个函数的定义

select

int select(int n,fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

函数解释:
select 函数监视的文件描述符分别是 writefds、readfds、和 exceptfds。调用select 函数会阻塞,直到有描述符就绪(即有数据 可读、可写、或者有 异常),或者超时 (timeout 指定等待时间,如果立即返回设为 null 即可),函数返回。当 select 函数返回后, 可以 通过遍历 fdset,来找到就绪的描述符。
优点:select是最早提出单机上通过一个线程支持多个socket的机制,支持最广泛
缺点:可以通过select函数定义看到,select需要把所有关注事件描述符的集合传入给内核,内核需要遍历所有的描述符,而Linux中单个线程能监视的文件描述符FD上限是1024个,所以效率比较低

poll

int poll(struct pollfd *fds, unsigned int nfds, int timeout);

pollfd 结构包含了要监视的 event 和发生的 event,不再使用 select“参数-值”传递的方 式。同时,pollfd 监控的描述符并没有最大数量限制(但是数量过大后性能也是会下降)。 和 select 函数一样,poll 返回后,需要轮询 pollfd 来获取就绪的描述符。

epoll

int epoll_create(int size);
int epoll_ct(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

epoll可以说是select和poll的加强版,epoll对之前的流程做了分解,分别为:

  • epoll_create 创建一个epoll句柄,参数size表示内核监听的初始化值,创建成功后,会占用一个fd,因此在调用完epoll后,记得关闭epoll.
  • epoll_ct 对指定的fd执行操作(op),其中,op包括三个操作:添加,删除,修改对fd的监听事件。
  • epoll_wait 等待epfd的IO事件,最多返回maxevents个事件

三者的区别

  1. 支持单个线程打开的最大连接数(监视文件描述符)
机制描述
select由系统宏(FD_SETSIZE)参数限制,默认一般为1024
poll同select
epoll只受内存限制,因为是基于链表来实现的。
  1. fd数量过多的影响
机制描述
select每次调用都要线性遍历所有的连接,数量过多会导致效率下降,出现性能问题
poll同select
epoll采用回调机制,只有活跃的socket会回调,不会出现线性下降的性能问题, 但是如果活跃的socket过多的话,也会导致性能下降
  1. 消息传递方式
机制描述
select需要将消息从内核态传递到用户态,即内核copy动作,涉及到内核态到用户态的切换
poll同select
epoll内核和用户空间共享

总结

表面来看,epoll的性能最优,但是也要分场景,在连接数少并且连接都活跃的情况下,select和poll的表现要优于epoll,因为epoll需要很多的函数回调,而select, poll没有这个环节,所以epoll在连接数多的场景性能优势会非常明显。


版权声明:本文为beyond_0818原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。