epoll
是一种高效的 I/O 多路复用机制,广泛用于 Linux 系统中,用于处理大量并发的文件描述符。它比传统的 select
和 poll
方法具有更好的性能,特别是在处理大量并发连接时。
1.epoll的设计思路
epoll是在select 出现 N 多年后才被发明的,是select 和 poll(poll 和 select 基本一样,有少量改进)的增强版本。epoll通过以下一些措施来改进效率:
-
措施一:功能分离
-
select 低效的原因之一是将“维护等待队列”和“阻塞进程”两个步骤合二为一。
如上图所示,每次调用select都需要这两步操作,然而大多数应用场景中,需要监视的socket相对固定,并不需要每次都修改。
epoll将这两个操作分开,先用epoll_ctl 维护等待队列,再调用epoll_wait 阻塞进程。显而易见,效率就能得到提升。
为方便理解后续的内容,我们先了解一下epoll的用法。如下的代码中,先用epoll_create 创建一个epoll对象 epfd,再通过epoll_ctl 将需要监视的socket添加到 epfd 中,最后调用epoll_wait 等待数据:
int s =socket(AF_INET, SOCK_STREAM, 0);
bind(s, ...)
listen(s, ...)
int epfd =epoll_create(...);
epoll_ctl(epfd, ...); //将所有需要监听的socket添加到epfd中
while(1){
int n =epoll_wait(...)
for(接收到数据的socket){
//处理
}
}
功能分离,使得epoll有了优化的可能。
措施二:就绪列表
select低效的另一个原因在于程序不知道哪些socket收到数据,只能一个个遍历。如果内核维护一个“就绪列表”,引用收到数据的socket,就能避免遍历。
如上图所示,计算机共有三个socket,收到数据的sock2和sock3 被就绪列表rdlist 所引用。当进程被唤醒后,只要获取rdlist 的内容,就能够知道哪些socket收到数据。