socket 中 select 与 epoll

selectpollepoll 是操作系统中用于监视多个文件描述符(如网络连接、文件等)的 I/O 多路复用机制。它们的主要区别在于性能、使用方式和实现机制。下面分别介绍它们的特点和差异。

1. select

  • 特点

    • select 是最早的 I/O 多路复用机制,几乎在所有的操作系统上都能使用。
    • 它通过一个固定大小的位图来表示文件描述符集合,最多只能监视 1024 个文件描述符(在一些系统中可调整,但有上限)。
    • 每次调用 select 都需要将全部文件描述符重新传入内核,并在内核和用户空间之间复制整个文件描述符集。
  • 实现机制

    • select 的底层实现采用轮询(polling)机制,通过遍历监控的文件描述符集来检查哪个文件描述符可读、可写或有错误。
    • 每次都要遍历整个文件描述符集合,效率较低,尤其在监控大量文件描述符时。
  • 缺点

    • 文件描述符集的最大数量有限。
    • 效率随着监控的文件描述符数量增加而降低。
    • 每次调用都需要重新传递文件描述符集合。
  • 适用场景

    • 简单的并发连接管理**:当需要处理的连接数较少时(少于 1024 个),使用 select 是一个简单而有效的选择。
    • 跨平台开发**:如果需要开发一个在不同操作系统上都能运行的程序,select 可能是最佳选择。

2. poll

  • 特点

    • poll 是对 select 的改进,不再使用固定大小的位图,而是使用一个链表来存储文件描述符集,因此没有最大文件描述符数量的限制。
    • select 一样,poll 每次调用也需要将文件描述符集传入内核。
  • 实现机制

    • poll 也采用轮询机制,通过遍历文件描述符链表来检查事件。
    • 内核依然需要遍历整个链表来检查状态,这使得 poll 在处理大量文件描述符时,性能依旧不理想。
  • 优点

    • 没有文件描述符数量的上限限制。
    • 结构更灵活,支持更多的事件类型。
  • 缺点

    • select 一样,每次调用都需要重新传递文件描述符集。
    • 随着监控的文件描述符数量增加,性能下降。
  • 适用场景

    • 需要监控大量文件描述符**:当文件描述符数量超过 select 的限制时,可以使用 poll 来处理。
    • 希望获得比 select 更好的灵活性**:poll 允许不同的事件类型和不同的文件描述符集,提供了更大的灵活性。

3. epoll

  • 特点

    • epoll 是 Linux 特有的 I/O 多路复用机制,专门为大规模的文件描述符监控设计,性能显著优于 selectpoll
    • 它将文件描述符和其对应的事件注册到内核,并在文件描述符状态发生变化时立即通知,而不是每次都遍历整个集合。
    • epoll 支持边缘触发(edge-triggered, ET)和水平触发(level-triggered, LT)两种模式。
  • 实现机制

    • epoll 使用一个 epoll 实例对象来管理文件描述符和事件。
    • 文件描述符和事件的注册只需要调用一次,之后的事件监控不再需要重新传递文件描述符集。
    • 事件发生时,内核将准备好的文件描述符列表传给用户空间,而不是用户空间去遍历整个集合。
  • 水平触发(Level-Triggered)

    • 工作方式:只要文件描述符处于可读/可写状态,epoll_wait 就会一直返回该事件。
    • 适用场景:适合处理简单的应用程序,因为不容易错过事件。
    • 优点:操作简单,不容易遗漏事件。
    • 缺点:可能会导致重复处理同一事件,增加不必要的开销。
  • 边缘触发(Edge-Triggered)

    • 工作方式:只有文件描述符从不可读/不可写状态变为可读/可写状态时,epoll_wait 才会返回该事件。
    • 适用场景:适用于高性能服务器等需要高效处理大量并发连接的场景。
    • 优点:减少重复处理,节省系统资源,提高性能。
    • 缺点:如果处理不当,容易遗漏事件,导致事件丢失。
  • 优点

    • 支持大规模的文件描述符监控,性能随文件描述符数量增加基本保持稳定。
    • 边缘触发模式下,事件通知效率高。
    • 文件描述符集不需要重复传入内核,减少了用户空间和内核空间之间的切换。
  • 缺点

    • 边缘触发模式下,使用不当可能导致事件丢失。
    • epoll 仅在 Linux 上可用,跨平台性较差。
  • 适用场景

    • 高并发服务器:在需要处理大量并发连接的服务器(如 Web 服务器、代理服务器)中,epoll 是最佳选择。
    • 需要高性能 I/O:在需要极高 I/O 性能的应用中,尤其是在 Linux 平台上,epoll 是最合适的选择。

总结

  • select:历史悠久,广泛支持,但性能有限,适合少量文件描述符监控。
  • poll:改进了 select 的文件描述符数量限制,但在大规模监控时性能仍然不理想。
  • epoll:适用于 Linux 环境下的大规模文件描述符监控,性能优越,适合高并发场景。

在实际应用中,如果需要在 Linux 上处理大量并发连接或 I/O 操作,epoll 通常是首选。