how and why epoll better
前两天睡不着的时候想到有次面试
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
poll
是 select
的改进版本,它可以同时监听多个 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