Answers:
对于极少数的套接字(当然,取决于您的硬件,它会有所不同,但是我们谈论的是10个或更少的数量级),select可以在内存使用率和运行速度上击败epoll。当然,对于这么少的套接字,这两种机制都非常快,以至于您在大多数情况下都不关心这种差异。
不过,需要澄清一下。选择和epoll均线性缩放。但是,最大的区别是面向用户空间的API具有基于不同事物的复杂性。select
调用的费用与传递给它的编号最高的文件描述符的值大致相同。如果选择一个单一的fd(100),则大约是选择一个单一的fd(50)的代价的两倍。在最高的fd之下添加更多的fd并不是完全免费的,因此实际上比这要复杂一些。对于大多数实现而言,它是一个很好的第一近似值。
epoll的成本接近实际具有事件的文件描述符的数量。如果您要监视200个文件描述符,但是其中只有100个文件事件发生,那么(非常粗略地)您只需为这100个活动文件描述符付费。这是epoll相对于select倾向于提供其主要优势之一的地方。如果您有一千个客户大部分都是闲置的,那么当您使用select时,您仍然需要为全部一千个客户付费。但是,使用epoll,就好像您只有几个-您只需要支付在任何给定时间处于活动状态的费用即可。
所有这些意味着epoll将导致大多数工作负载的CPU使用率降低。就内存使用而言,这有点麻烦。 select
确实以高度紧凑的方式(每个文件描述符一位)来表示所有必要的信息。FD_SETSIZE(通常为1024)限制了可以使用的文件描述符的数量,这select
意味着您可以使用的三个fd集中的每一个都不会花费超过128个字节select
(读,写,异常)。与最大384个字节相比,epoll有点像猪。每个文件描述符都由一个多字节结构表示。但是,绝对而言,它不会占用太多内存。您可以在几十个千字节中表示大量的文件描述符(我认为每1000个文件描述符大约20k)。而且,select
如果您只想监视一个文件描述符,但它的值恰好是1024,则必须花费所有384个字节,而使用epoll则仅花费20个字节。尽管如此,所有这些数字都很小,所以并没有太大的区别。
还有epoll的其他好处,也许您已经知道了,它不限于FD_SETSIZE文件描述符。您可以使用它来监视尽可能多的文件描述符。而且,如果您只有一个文件描述符,但其值大于FD_SETSIZE,则epoll也可以使用该文件描述符,但select
不会。
随机地,我最近还发现epoll
与select
或相比有一个轻微的缺点poll
。尽管这三个API均不支持普通文件(即文件系统上的文件),但由于将此类描述符报告为始终可读且始终可写,select
因此poll
存在这种缺乏支持。这使得它们不适合任何有意义的非阻塞文件系统I / O,使用select
或poll
碰巧从文件系统遇到文件描述符的程序至少会继续运行(或者,如果失败,那不是因为的select
或poll
),尽管它也许不是最佳的性能。
另一方面,当要求监视此类文件描述符时,epoll
它将快速失败并显示错误(EPERM
)。严格来说,这几乎是不正确的。它只是以明确的方式表明其缺乏支持。通常,我会为显式的故障情况表示赞赏,但是这种情况是无证的(据我所知),会导致应用程序完全崩溃,而不是仅仅以可能降低性能的方式运行。
在实践中,我唯一看到的地方是与stdio交互时。用户可以将stdin或stdout从/重定向到普通文件。以前的stdin和stdout可能是一个管道(由epoll支持就很好了),然后它变成了普通文件,epoll大声失败,从而破坏了应用程序。
man select
Linux内核没有施加固定的限制,但是glibc实现使fd_set为固定大小的类型,其中FD_SETSIZE定义为1024,而FD _ *()宏根据这个限制。要监视大于1023的文件描述符,请改用poll(2)。在CentOS 7上,我已经遇到了我自己的代码未能通过select()失败的问题,因为内核返回的文件句柄> 1023,而我目前正在查看一个闻起来好像是Twisted命中同一问题的问题。
在我公司的测试中,出现了epoll()的一个问题,因此与select相比,成本只有一个。
尝试超时读取网络时,创建epoll_fd(而不是FD_SET)并将fd添加到epoll_fd,比创建FD_SET(这是一个简单的malloc)要昂贵得多。
根据前面的答案,随着过程中FD数量的增加,select()的成本会增加,但是在我们的测试中,即使fd值为10,000,select仍然是赢家。在这些情况下,只有一个fd在等待线程,并且只是试图克服使用阻塞线程模型时网络读取和网络写入不会超时的事实。当然,与非阻塞反应堆系统相比,阻塞线程模型的性能较低,但是在某些情况下,需要与特定的传统代码库集成。
在高性能应用程序中这种用例很少见,因为反应堆模型不需要每次都创建一个新的epoll_fd。对于epoll_fd寿命长的模型---对于任何高性能服务器设计来说显然都是优选的--- epoll在各个方面都是明显的赢家。
select()
如果文件描述符的值在10k +范围内,您甚至无法使用-除非您重新编译一半的系统以更改FD_SETSIZE-所以我想知道这种策略是如何工作的。对于您所描述的场景,我可能会看看poll()
哪一个select()
比它看起来更像epoll()
-但删除了FD_SETSIZE限制。
FD_SETSIZE
是在编译C库时设置的编译时间常数。如果在构建应用程序时将其定义为不同的值,则您的应用程序和C库将不一致,并且情况会变差。如果您有引用声称可以重新定义是安全的,那么FD_SETSIZE
我很希望看到它们。
poll
表示完整性吗?