基本知识
- 若要理解 select、 poll 、 epoll 多路复用器的工作原理,就需要首先了解什么是多路复用器。
- 而要了解什么是多路复用器,就需要先了解什么是 多进程 / 多线程连接处理模型。
多进程 / 多线程连接处理模型
- 在该模型下,一个用户连接请求会由一个内核进程处理,而一个内核进程会创建一个应用程序进程(App进程)来处理该连接请求。
- 应用程序进程(App进程)在调用 I/O 时,采用的是 BIO 通讯方式,即应用程序进程(App进程)在未获取到 I/O 响应之前是处于阻塞态的。
- 该模型的优点是,内核进程不存在对应用程序进程(App进程)的竞争,一个内核进程对应一个应用程序进程(App进程)。
- 但也正因如此,其弊端也就显而易见了。需要创建过多的 应用程序进程(App进程),而该创建过程十分的消耗系统资源。且一个系统的进程数量是有上限的,所以该模型不能处理高并发的情况。
多路复用连接处理模型
- 在该模型下,只有一个应用程序进程(App进程)来处理内核进程事务,且 应用程序进程(App进程)一次只能处理一个内核进程事务。
- 故这种模型对于内核进程来说, 存在对 应用程序进程(App进程)的竞争。
- 在前面的“多进程/多线程连接处理模型”中说过,应用程序进程(App进程)只要被创建了就会执行内核进程事务。
- 那么在该模型下,应用程序进程(App进程)应该执行哪个内核进程事务呢?
- 谁的准备就绪了, 应用程序进程(App进程)就执行哪个。但 应用程序进程(App进程)怎么知道哪个内核进程就绪了呢?
- 需要通过 多路复用器 来获取各个内核进程的状态信息。
- 那么多路复用器又是怎么获取到内核进程的状态信息的呢?
- 不同的多路复用器,其算法不同。常见的有三种:
- select、
- poll、
- epoll。
- 不同的多路复用器,其算法不同。常见的有三种:
- 那么多路复用器又是怎么获取到内核进程的状态信息的呢?
- 应用程序进程(App进程)在进行 I/O 时,采用的是 NIO 通讯方式,即该 应用程序进程(App进程)不会阻塞。
- 当一个 I/O 结果返回时, 应用程序进程(App进程)会暂停当前事务,将 I/O 结果返回给对应的内核进程。然后再继续执行暂停的线程。
- 该模型的优点很明显,无需创建很多的应用程序进程(App进程)去处理内核进程事务了,仅需一个即可。
多路复用器工作原理
select
- select 多路复用器是采用轮询的方式,一直在轮询所有的相关内核进程,查看它们的进程状态。
- 若已经就绪,则马上将该内核进程放入到就绪队列。
- 否则,继续查看下一个内核进程状态。
- 在处理内核进程事务之前,应用程序进程(App进程)首先会从内核空间中将用户连接请求相关数据复制到用户空间。
- 该多路复用器的缺陷有以下几点:
- 对所有内核进程采用轮询方式效率会很低。因为在大多数情况下,内核进程都不属于就绪状态,只有少部分才会是就绪态。所以这种轮询结果大多数都是无意义的。
- 由于就绪队列底层由数组实现,所以其所能处理的内核进程数量是有限制的,即其能够处理的最大并发连接数量是有限制的。
- 从内核空间到用户空间的复制,系统开销大。
poll
- poll 多路复用器的工作原理与 select 几乎相同,不同的是,由于其就绪队列由链表实现,所以,其对于要处理的内核进程数量理论上是没有限制的,即其能够处理的最大并发连接数量是没有限制的(当然,要受限于当前系统中进程可以打开的最大文件描述符数 ulimit,后面会讲到)。
epoll
- epoll 多路复用是对 select 与 poll 的增强与改进。不再采用轮询方式了,而是采用回调方式实现对内核进程状态的获取:
- 一旦内核进程就绪,其就会回调 epoll 多路复用器,进入到多路复用器的就绪队列(由链表实现)。所以 epoll 多路复用模型也称为 epoll 事件驱动模型。
- 另外,应用程序所使用的数据,也不再从内核空间复制到用户空间了,而是使用 mmap 零拷贝机制,大大降低了系统开销。
- 当内核进程就绪信息通知了 epoll 多路复用器后,多路复用器就会马上对其进行处理,将其马上存放到就绪队列吗?
- 不是的。根据处理方式的不同,可以分为两种处理模式: LT 模式与 ET 模式。
LT 模式
- LT,Level Triggered,水平触发模式。
- 即只要内核进程的就绪通知由于某种原因暂时没有被 epoll 处理,则该内核进程就会定时将其就绪信息通知 epoll。直到 epoll 将其写入到就绪队列,或由于某种原因该内核进程又不再就绪而不再通知。
- 其支持两种通讯方式: BIO 与NIO。
ET 模式
- ET,Edge Triggered,边缘触发模式。仅支持 NIO 的通讯方式。
- 当内核进程的就绪信息仅会通知一次 epoll,无论 epoll 是否处理该通知。明显该方式的效率要高于 LT 模式,但其有可能会出现就绪通知被忽视的情况,即连接请求丢失的情况。