简短的问题,但对我来说很难理解。
为什么ePoll比Poll更好地扩展?
答案 0 :(得分:16)
虽然Damon的理由对于你从不阻塞套接字的特殊情况是正确的,但在典型的真实世界程序中,原因却完全不同。典型的程序如下所示:
1)完成我们现在可以做的所有工作。
2)检查是否有任何网络连接需要服务,如果没有任何事情可以阻止。
3)为发现的任何网络连接提供服务。
4)转到第1步。
通常情况下,因为您刚刚完成了所有可以完成的工作,所以当您走到第2步时,您无需做任何工作。所以你必须等一下。现在,想象一下你感兴趣的是800个套接字。内核必须为这800个套接字中的每一个插入等待队列。而且,在数据到达800个套接字之一后的瞬间,内核必须将您从这800个等待队列中删除。将任务放在等待队列上需要创建“thunk”以将该任务链接到该等待队列。没有好的优化是可能的,因为内核不知道你将等待哪个800个套接字。
对于epoll
,epoll套接字本身具有等待队列,并且该进程仅放在该单个等待队列上。需要一个thunk来将800个连接中的每个连接链接到epoll等待队列,但该thunk是持久的。你可以通过向epoll集添加一个套接字来创建它,它会一直存在,直到你从集合中删除套接字。
当套接字上有活动时,内核会在检测活动的任务中处理它。当你等待时,内核已经知道是否有检测到的事件,并且内核只需要将你放在那个等待队列上。当你醒来时,它只需要从那个队列中删除你。
所以复制并不是select
或poll
的杀手,而是内核必须在每个阻塞操作上操纵大量的等待队列。
答案 1 :(得分:14)
poll系统调用需要每次都将文件描述符列表复制到内核。这种情况仅在epoll_ctl
发生一次,但不是每次调用epoll_wait
时都会发生。
此外,epoll_wait
对于观看 1 的描述符的数量是O(1)
,这意味着无论您是等待一个描述符还是等待5,000或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)
关于密钥长度。这在技术上是绝对正确的,并且可能存在这是一个问题的情况,然而,对于大多数人来说,这并不重要。