为什么ePoll比Poll更好地扩展?

Fil*_*tos 12 epoll

简短的问题但对我来说很难理解.

为什么ePoll比Poll更好地扩展?

Dav*_*rtz 16

虽然Damon的理由对于你从不阻塞套接字的特殊情况是正确的,但在典型的真实世界程序中,原因却完全不同.典型的程序如下所示:

1)做我们现在可以做的所有工作.

2)检查是否有任何网络连接需要服务,如果没有任何关系则阻止.

3)服务发现的任何网络连接.

4)转到步骤1.

通常情况下,因为您刚刚完成了所有可以完成的工作,所以当您进入第2步时,您无需做任何工作.所以你必须等一下.现在,想象一下你感兴趣的是800个套接字.内核必须为这800个套接字中的每一个插入等待队列.而且,在数据到达800个套接字之一后的瞬间,内核必须将您从这800个等待队列中删除.将任务放在等待队列上需要创建"thunk"以将该任务链接到该等待队列.没有好的优化是可能的,因为内核不知道你将等待哪800个套接字.

使用epoll,epoll套接字本身具有等待队列,并且该进程仅放在该单个等待队列上.需要一个thunk来将800个连接中的每个连接链接到epoll等待队列,但该thunk是持久的.您可以通过向epoll集添加套接字来创建它,并且它将保留在那里,直到您从集合中删除套接字.

当套接字上有活动时,内核会在检测活动的任务中处理它.当你等待时,内核已经知道是否有检测到的事件,并且内核只需要将你放在那个等待队列上.当你醒来时,它只需要从那个队列中删除你.

因此,这与其说是与杀手复制selectpoll,它的内核具有操作上的每个阻塞操作等待队列数量庞大的事实.


Dam*_*mon 14

poll系统调用需要每次都将文件描述符列表复制到内核.这只会发生一次epoll_ctl,但不是每次都打电话epoll_wait.

此外,epoll_waitO(1)在尊重看着描述符的数量的1,这意味着它不会不管你是否在等待一个描述符或在5000所或50,000描述.poll虽然效率更高select,但仍然必须每次遍历列表(即,它是O(N)关于描述符的数量).

最后,epoll可以在"边缘触发"模式下工作,除了"正常"模式,这意味着内核不需要跟踪您在信号准备就绪后读取了多少数据.这种模式更难掌握,但效率更高.


1正如David Schwartz正确指出的那样epoll_wait,当然仍然O(N)是关于发生事件.任何界面都没有任何不同的方式.如果在观察的描述符上发生N个事件,则应用程序需要获得N个通知,并且需要执行N个 "事物"以便对正在发生的事情做出反应.
这在边缘触发模式中再次略有不同,但并没有根本的不同,在这种情况下你实际上可以获得M事件M <= N.在边缘触发模式下,当同一事件(例如POLLIN)多次发生时,您可能会收到更少的通知,可能只有一个通知.但是,对于大O符号而言,这并没有太大变化.

但是,epoll_wait无论观看的描述符数量如何.假设它以预期的"正常"方式(即许多描述符,很少的事件)使用,这才是真正重要的,而且确实如此O(1).

作为类比,您可以考虑哈希表.哈希表访问其内容O(1),但有人可能会争辩说计算哈希实际上O(N)是关于密钥长度.这在技术上是绝对正确的,并且可能存在这是一个问题的情况,然而,对于大多数人来说,这无关紧要.

  • 您可以使用epoll_ctl在集合中添加/删除/更改描述符.这会根据您的请求更改内核结构(例如,它将eventpoll结构添加到文件描述符的等待队列中).当你调用epoll_wait时,它只是阻塞.当文件描述符上发生某些事情时,它会唤醒该描述符的等待队列(实际上,它比那更复杂,在eventpoll结构中有另一个队列,所以几个线程可以阻塞同一个fd,但基本上就是这样).实际上它实际上是"目标通知"而不是"搜索轮询",这就是为什么它也是O(1). (2认同)