前两天睡不着的时候想到有次面试

Q: Redis 最多支持多少连接?
A: 65535? (其实不对)
Q: 不是这个,巴拉巴拉
然后我开始走神了,后面想想他想说的是这个 select/epoll
心情难以言表,不过正好今天水一下

What is I/O

IO (Input/Output,输入/输出)即数据的读取(接收)或写入(发送)操作,通常用户进程中的一个完整IO分为两阶段:
用户进程空间<–>内核空间、内核空间<–>设备空间(磁盘、网络等)。
IO有内存IO、网络IO和磁盘IO三种

What is I/O Model

一般来说分为五种类型:

  • Blocking IO 阻塞IO
    在 I/O 操作完成之前,应用程序会一直阻塞等待。
  • NonBlocking IO 非阻塞IO
    应用程序不会阻塞等待 I/O 操作完成,可以继续执行其他操作。
  • IO Multiplexing IO复用
    一个线程可以同时监视多个 I/O 操作的状态,当有 I/O 操作完成时,内核会通知该线程。
  • Signal-Driven 信号驱动
    应用程序使用信号来处理 I/O 操作。当 I/O 操作完成时,操作系统会向应用程序发送信号。
  • Asynchornous IO 异步IO
    应用程序在发出 I/O 操作后,无需等待 I/O 操作完成,可以立即返回。当 I/O 操作完成后,操作系统会通知应用程序。

这里阻塞,非阻塞,同步/异步并不是一个概念:

  • 阻塞/非阻塞: 是针对 调用方 的概念,指的是调用方在调用某个函数或方法时,是否会一直等待结果返回。
  • 同步/异步:针对 操作 的概念,指的是操作是否会立即返回。
  • 关系:
    • 阻塞通常是同步的,因为调用方会一直等待结果返回,所以操作也无法立即返回
    • 非阻塞操作可以是同步的,也可以是异步的
      • 非阻塞同步操作会立即返回,但结果会在某个时间点返回
      • 非阻塞异步操作不会立即返回,结果会在操作完成后返回

两句话说不清的话就很难聊了

Conclusion

多路复用技术出现前,应用程序通常使用 阻塞式 I/O 模型来处理 I/O 操作。阻塞式 I/O 模型的缺点是:

  • 效率低下:当应用程序执行 I/O 操作时,会一直阻塞等待 I/O 操作完成,期间无法执行其他操作。
  • 无法处理并发:应用程序只能同时处理一个 I/O 操作,无法同时处理多个 I/O 操作。

为了解决阻塞式 I/O 模型的缺点,人们提出了 非阻塞式 I/O 模型。非阻塞式 I/O 模型允许应用程序在 I/O 操作未完成时继续执行其他操作。但是,非阻塞式 I/O 模型要求应用程序必须不断地轮询 I/O 操作的状态,以便在 I/O 操作完成时及时进行处理。这种轮询机制会造成 CPU 资源浪费。

多路复用 技术的出现,解决了非阻塞式 I/O 模型的 CPU 资源浪费问题。多路复用技术允许一个线程同时监视多个 I/O 操作的状态,当有 I/O 操作完成时,内核会通知该线程,该线程可以只处理就绪的 I/O 操作,从而避免了 CPU 资源浪费。

因此,多路复用技术是 I/O 模型发展的重要里程碑,它显著提高了应用程序的 I/O 处理效率。

select

select 是最早出现的 IO 多路复用机制,它可以通过轮询的方式来检查多个 fd 的状态变化。select 的缺点是:

  • 效率低下select 需要轮询所有 fd ,即使大部分 fd 都没有就绪,也会造成 CPU 资源浪费。
  • 连接数限制select 在 Linux 上对连接数有限制,通常是 1024 个。

poll

pollselect 的改进版本,它可以同时监听多个 fd 的状态变化,并且没有连接数限制。但是,poll 仍然使用轮询的方式来检查 fd 的状态变化,因此效率仍然不高。

epoll

epoll 相比 poll 主要有以下几个改进:

  • 效率更高epoll 使用事件驱动的方式来处理 IO 事件,而 poll 使用轮询的方式来检查 fd 的状态变化。因此, epoll 的效率要比 poll 高得多。
  • 可扩展性更好epoll 可以支持更大的并发连接数,而 poll 对连接数有限制。
  • 内存占用更少epoll 使用红黑树来存储 fd ,而 poll 使用链表来存储 fd 。因此,epoll 占用的内存要比 poll 少。

具体来说,epoll 的改进体现在以下几个方面:

  • 使用事件驱动: epoll 使用事件驱动的方式来处理 IO 事件,这意味着只有当 fd 有事件发生时,内核才会通知应用程序。而 poll 使用轮询的方式来检查 fd 的状态变化,这意味着即使 fd 没有事件发生,内核也会通知应用程序。
  • 使用红黑树: epoll 使用红黑树来存储 fd ,这意味着可以快速找到就绪的 fd 。而 poll 使用链表来存储 fd ,这意味着需要遍历整个链表才能找到就绪的 fd
  • 使用共享内存: epoll 使用共享内存来传递数据,这意味着应用程序和内核之间不需要进行额外的内存拷贝。而 poll 使用复制内存的方式来传递数据,这意味着应用程序和内核之间需要进行额外的内存拷贝。

因此,epoll 是 Linux 上推荐使用的 IO 多路复用机制。

Update

顺便看了一下 Redis,代码比我想的少很多。
Redis 使用优先级最高的是基于 Solaris 的 evport。
然后依次才是: epoll , kqueue , select