1

select poll epoll的区别

 2 years ago
source link: http://caiknife.github.io/blog/2019/02/17/select-poll-epollde-qu-bie/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

在 linux 没有实现 epoll 事件驱动机制之前,我们一般选择用 select 或者 poll 等 IO 多路复用的方法来实现并发服务程序。

在大数据、高并发、集群等一些名词唱的火热之年代,select 和 poll 的用武之地越来越有限了,风头已经被 epoll 占尽。

select 和 poll IO 多路复用模型

select 的缺点:

单个进程能够监视的文件描述符的数量存在最大限制,通常是 1024,当然可以更改数量,但由于 select 采用轮询的方式扫描文件描述符,文件描述符数量越多,性能越差;

内核/用户空间内存拷贝问题,select 需要复制大量的句柄数据结构,产生巨大的开销;

select 返回的是含有整个句柄的数组,应用程序需要遍历整个数组才能发现哪些句柄发生了事件;

select 的触发方式是水平触发,应用程序如果没有完成对一个已经就绪的文件描述符进行 IO,那么之后再次 select 调用还是会将这些文件描述符通知进程。

相比于 select 模型,poll 使用链表保存文件描述符,因此没有了监视文件数量的限制,但其他三个缺点依然存在。

拿 select 模型为例,假设我们的服务器需要支持 100 万的并发连接,则在 _FD_SETSIZE 为 1024 的情况下,则我们至少需要开辟 1K 个进程才能实现 100 万的并发连接。除了进程间上下文切换的时间消耗外,从内核/用户空间大量的无脑内存拷贝、数组轮询等,是系统难以承受的。因此,基于 select 模型的服务器程序,要达到 10 万级别的并发访问,是一个很难完成的任务。

epoll IO多路复用模型实现机制

由于 epoll 的实现机制与 select/poll 机制完全不同,上面所说的 select 的缺点在 epoll 上不复存在。

设想一下如下场景:有 100 万个客户端同时与一个服务器进程保持着 TCP 连接。而每一时刻,通常只有几百上千个 TCP 连接是活跃的。如何实现这样的高并发?

在 select/poll 时代,服务器进程每次都把这 100 万个连接告诉操作系统(从用户态复制句柄数据结构到内核态),让操作系统内核去查询这些套接字上是否有事件发生,轮询完后,再将句柄数据复制到用户态,让服务器应用程序轮询处理已发生的网络事件,这一过程资源消耗较大,因此,select/poll 一般只能处理几千的并发连接。

epoll 的设计和实现 select 完全不同。epoll 通过在 linux 内核中申请一个简易的文件系统(文件系统一般用什么数据结构实现?B+ 树)。把原先的 select/poll 调用分成了 3 个部分:

1)调用 epoll_create 建立一个 epoll 对象(在 epoll 文件系统中为这个句柄对象分配资源)

2)调用 epoll_ctl 向 epoll 对象中添加这 100 万个连接的套接字

3)调用 epoll_wait 收集发生的事件的连接

如此一来,要实现上面说的场景,只需要在进程启动时建立一个 epoll 对象,然后在需要的时候向这个 epoll 对象中添加或者删除连接。同时,epoll_wait 的效率也非常高,因为调用 epoll_wait 时,并没有一股脑的向操作系统复制这 100 万个连接的句柄数据,内核也不需要去遍历全部的连接。

上面的 3 个部分非常清晰,首先要调用 epoll_create 创建一个 epoll 对象。然后使用 epoll_ctl 可以操作上面建立的 epoll 对象,例如,将刚建立的 socket 加入到 epoll 中让其监控,或者把 epoll 正在监控的某个 socket 句柄移出 epoll,不再监控它等等。

epoll_wait 在调用时,在给定的 timeout 时间内,当在监控的所有句柄中有事件发生时,就返回用户态的进程。

从上面的调用方式就可以看到 epoll 比 select/poll 的优越之处:因为后者每次调用时都要传递你所要监控的所有 socket 给 select/poll 系统调用,这意味着需要将用户态的 socket 列表 copy 到内核态,如果以万计的句柄会导致每次都要 copy 几十几百 KB 的内存到内核态,非常低效。而我们调用 epoll_wait 时就相当于以往调用 select/poll,但是这时却不用传递 socket 句柄给内核,因为内核已经在 epoll_ctl 中拿到了要监控的句柄列表。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK