Dav*_*rtz 16
虽然Damon的理由对于你从不阻塞套接字的特殊情况是正确的,但在典型的真实世界程序中,原因却完全不同.典型的程序如下所示:
1)做我们现在可以做的所有工作.
2)检查是否有任何网络连接需要服务,如果没有任何关系则阻止.
3)服务发现的任何网络连接.
4)转到步骤1.
通常情况下,因为您刚刚完成了所有可以完成的工作,所以当您进入第2步时,您无需做任何工作.所以你必须等一下.现在,想象一下你感兴趣的是800个套接字.内核必须为这800个套接字中的每一个插入等待队列.而且,在数据到达800个套接字之一后的瞬间,内核必须将您从这800个等待队列中删除.将任务放在等待队列上需要创建"thunk"以将该任务链接到该等待队列.没有好的优化是可能的,因为内核不知道你将等待哪800个套接字.
使用epoll,epoll套接字本身具有等待队列,并且该进程仅放在该单个等待队列上.需要一个thunk来将800个连接中的每个连接链接到epoll等待队列,但该thunk是持久的.您可以通过向epoll集添加套接字来创建它,并且它将保留在那里,直到您从集合中删除套接字.
当套接字上有活动时,内核会在检测活动的任务中处理它.当你等待时,内核已经知道是否有检测到的事件,并且内核只需要将你放在那个等待队列上.当你醒来时,它只需要从那个队列中删除你.
因此,这与其说是与杀手复制select或poll,它的内核具有操作上的每个阻塞操作等待队列数量庞大的事实.
Dam*_*mon 14
poll系统调用需要每次都将文件描述符列表复制到内核.这只会发生一次epoll_ctl,但不是每次都打电话epoll_wait.
此外,epoll_wait是O(1)在尊重看着描述符的数量的1,这意味着它不会不管你是否在等待一个描述符或在5000所或50,000描述.poll虽然效率更高select,但仍然必须每次遍历列表(即,它是O(N)关于描述符的数量).
最后,epoll可以在"边缘触发"模式下工作,除了"正常"模式,这意味着内核不需要跟踪您在信号准备就绪后读取了多少数据.这种模式更难掌握,但效率更高.
epoll_wait,当然仍然O(N)是关于发生的事件.任何界面都没有任何不同的方式.如果在观察的描述符上发生N个事件,则应用程序需要获得N个通知,并且需要执行N个 "事物"以便对正在发生的事情做出反应.M事件M <= N.在边缘触发模式下,当同一事件(例如POLLIN)多次发生时,您可能会收到更少的通知,可能只有一个通知.但是,对于大O符号而言,这并没有太大变化.
但是,epoll_wait无论观看的描述符的数量如何.假设它以预期的"正常"方式(即许多描述符,很少的事件)使用,这才是真正重要的,而且确实如此O(1).
作为类比,您可以考虑哈希表.哈希表访问其内容O(1),但有人可能会争辩说计算哈希实际上O(N)是关于密钥长度.这在技术上是绝对正确的,并且可能存在这是一个问题的情况,然而,对于大多数人来说,这无关紧要.