对于非常少量的套接字(当然,取决于您的硬件,但我们谈论的是大约 10 个或更少的套接字),select 可以在内存使用和运行速度方面击败 epoll。当然,对于如此少量的套接字,这两种机制都非常快,以至于在绝大多数情况下您并不真正关心这种差异。
不过,有一个澄清。 select 和 epoll 都是线性缩放的。然而,一个很大的不同是,面向用户空间的 API 具有基于不同事物的复杂性。 select 调用的成本大致与您传递给它的编号最高的文件描述符的值有关。如果您在单个 fd 上选择 100,那么这大约是在单个 fd 上选择 50 的两倍。在最高值以下添加更多 fd 并不是完全免费的,因此在实践中它比这更复杂一些,但是这个对于大多数实现来说是一个很好的初步近似值。
epoll 的开销更接近于实际在其上具有事件的文件描述符的数量。如果您正在监视 200 个文件描述符,但其中只有 100 个有事件,那么您(非常粗略地)只需为这 100 个活动文件描述符付费。这就是 epoll 倾向于提供其优于 select 的主要优势之一的地方。如果您有一千个大部分空闲的客户,那么当您使用 select 时,您仍然需要为全部一千个客户付费。但是,使用 epoll,就好像您只有几个 - 您只需为在任何给定时间处于活动状态的那些付费。
所有这一切都意味着 epoll 将减少大多数工作负载的 CPU 使用率。就内存使用而言,这有点折腾。 select 确实设法以高度紧凑的方式(每个文件描述符一位)表示所有必要的信息。 FD_SETSIZE(通常为 1024)限制您可以与 select 一起使用多少个文件描述符,这意味着您永远不会为您可以与 select 一起使用的三个 fd 集(读、写、例外)。与最大 384 字节相比,epoll 有点像猪。每个文件描述符都由一个多字节结构表示。但是,绝对而言,它仍然不会使用太多内存。您可以在几十 KB 中表示大量文件描述符(我认为每 1000 个文件描述符大约 20k)。如果您只想监视一个文件描述符但它的值恰好是 1024,那么您还可以考虑这样一个事实,即您必须将所有 384 个字节用于select,而使用 epoll 您只需花费 20 个字节。尽管如此,所有这些数字都非常小,所以没有太大区别。
还有 epoll 的其他好处,也许您已经知道,它不限于 FD_SETSIZE 文件描述符。您可以使用它来监控尽可能多的文件描述符。如果你只有一个文件描述符,但它的值大于 FD_SETSIZE,epoll 也可以使用,但select 不行。
随机地,我最近还发现了 epoll 与 select 或 poll 相比的一个小缺点。虽然这三个 API 都不支持普通文件(即文件系统上的文件),但select 和poll 表示缺乏支持,因为报告此类描述符始终可读且始终可写。这使得它们不适合任何有意义的非阻塞文件系统 I/O,使用 select 或 poll 并且碰巧遇到来自文件系统的文件描述符的程序至少会继续运行(或者如果它失败,不会是因为select 或poll),尽管它可能不是最好的性能。
另一方面,epoll 在被要求监视此类文件描述符时会快速失败并出现错误(显然是EPERM)。严格来说,这几乎是不正确的。它只是以明确的方式表明它缺乏支持。通常我会为明确的失败条件鼓掌,但这种情况没有记录(据我所知)并导致应用程序完全崩溃,而不是仅仅以潜在的性能下降运行。
在实践中,我见过的唯一出现这种情况的地方是与 stdio 交互时。用户可能会将标准输入或标准输出从/重定向到普通文件。而以前的标准输入和标准输出本来是一个管道——epoll 支持得很好——然后它变成了一个普通文件,epoll 大声失败,破坏了应用程序。