揭开 epoll 面纱:Nginx,Redis 等都在用的多路复用,到底是什么?
最近在看一些文章,出现在眼前频率比较高的一个单词就是 epoll。这个在众多优秀软件中都出现的内容,我看大家或多或少有接触过,例如在 Nginx 中使用 epoll 处理百万并发连接,Redis 使用 epoll 来处理高并发的客户端网络请求,Java 的 NIO 底层也是基于 epoll 的等等。
在 Redis 的面试八股文中,有一个最频繁提及的问题就是:Redis 为什么这么快?对于这个问题,我相信对于八股文的职位玩家而言,可以说是张口就来:Redis 是基于内存的,Redis 内部采用了多线程+epoll 多路复用模型,有优秀的数据结构......等等。
可以看出在上面的答案中提及到了 epoll 多路复用,其实我相信对于大多数没有编写过 C/C++的同学而言,你只会背,不会用吧,更没有见过它到底长什么样子?心血来潮,今天简单写一篇文章介绍下 epoll 的相关知识点,不会面面俱到每一个函数是如何定义的,接口是如何使用的,只希望让你了解到 epoll 到底是如何工作的。
一、epoll 要解决什么问题?
每个技术点都有其要解决的问题与应用场景,epoll 也一样,其设计的目的就在于:处理大批量的文件描述符(基本为网络 Socket 连接),提升系统处理网络高并发的能力。
另外,epoll 被称为"IO 多路复用",每个含义拆开来理解:
IO:epoll 的应用场景大多数在处理网络 IO 上。
多路复用:百度百科的解释是"多路复用技术能把多个信号组合起来在一条物理信道上进行传输",在编程中就对应着"多路复用技术能把多个网络连接组合起来在一条 epoll 上进行处理"。
1.1-五大 IO 模型
对于 IO 的处理上,有五大 IO 模型:
阻塞式 IO(blocking I/O)。
非阻塞式 IO(non-blocking I/O)。
IO 复用:实现上有 select、poll、epoll,本文介绍 epoll。
信号驱动式 IO。
异步 IO。
可以看到 epoll 是属于 IO 复用模型的,对于这五大 IO 模型,大家经常用来在一起进行比较,我认为"比较"这个动词不太适合,只能说每一种 IO 模型都有自己的适用场景,下面我简单介绍下其中提到比较多的一些 IO 模型的"特点"。
另外,本文在提及 IO 时,没有特殊说明,指的都是网络 IO。
阻塞式 IO:
对于阻塞式 IO,一般都是针对于单个网络而言的。
在软件层面的表现:当线程(or 进程)尝试从网络连接中读取数据,但是无数据可读时,线程阻塞。
在硬件层面的表现:线程产生阻塞之后,产生上下文切换,让出 CPU 给其他线程运行。
非阻塞式 IO:
对于非阻塞式 IO,也是针对于单个网络而言的。
在软件层面的表现:当线程(or 进程)尝试从网络连接中读取数据,但是无数据可读时,线程不阻塞,立即返回,可以处理其他逻辑。
在硬件层面的表现:线程不产生阻塞,CPU 按照给出的时间片时间进行处理。
IO 复用:
这个就是本文介绍的重点,其在 IO 处理方面,不拘束于一个 IO 连接,而是千级别/万级别的网络 IO 连接,这些连接都由 select/poll/epoll 来进行管理。
在软件层面的表现:select/poll/epoll 对管理的千级别/万级别的网络连接进行监听,当任何一个 IO 有消息/事件产生时,对这批 IO 连接进行处理。如果没有任何一个网络连接有消息产生,select/poll/epoll 本身也会产生阻塞。
在硬件层面的表现:根据软件层面的阻塞/非阻塞表现,硬件层面同理。
注:下图中是以 select 为例的,epoll 同理。
我相信到这里,可能还是有点蒙的,不妨接着看,后面伪代码部分看的时候可能就清晰些了。
1.2-epoll 解决了哪些问题?
回到标题,epoll 要解决什么问题?
我认为主要是下面两个方面。
1.2.1-软件层面:应对高并发连接
设想你编写的服务器,有千级别,万级别的客户端来连接,你如何管理这些 socket 连接哪?
每个 socket 去遍历一遍,看看是否有数据到来?
发数据的时候,找到特定的 socket,然后往里面发?
......
可以看到,与 socket 的交互,最核心的场景就是网络 IO 的处理,而这些操作 epoll 都可以协助你去完成,你新建立一个网络 socket 的时候,交管给 epoll 就可以了,当网络 IO 有数据到来时它会通知你,当你要往网络 IO 写数据时,epoll 也会自动帮你发送。
1.2.2-硬件层面:提升 CPU 使用率
想一想你是否写过这样的程序,while 循环等待网络 IO 的到来?
上面代码的缺点很明显:
消耗 CPU 的时间。while 循环会间歇性的获取 CPU 时间片运行,并尝试从网络 socket 上获取数据。当然,此过程也伴随着大量的上下文切换。
socket 过多你该如何处理?总不能每个 socket 都开一个 while 循环在哪里等待吧?
epoll 是怎么样处理的?见下面 epoll 伪代码的介绍。
二、epoll 的伪代码
epoll 的原型由 C 语言 POSIX C 提供,下面提供出了一段伪代码,方便你理解。
2.1-工作模型
通过上面的伪代码,可以大致理解 epoll 的工作模型:
针对于所有的 socket,交由 epoll 来进行管理。
在编码层面,也是不断的循环调用 epoll_wait,监听哪些 socket 已经准备好进行数据收发了。
获取到准备好的 socket,进行数据读写。
2.2-epoll 是如何提升 CPU 使用率的?
回到在 1.2.2 我们提到的,epoll 如何提升 CPU 的使用率?我看伪代码中也是使用 while 循环来调用 epoll_wait,这就不浪费 CPU 资源了么?
其实,大家可以这样进行理解:
epoll 管理的是千级别/万级别数量的 socket 连接,肯定是每个时刻都有 socket 处于有数据收发的情况,不可能你的代码中只有若干个 socket 连接,也使用 epoll 吧?
既然每个时刻都有 socket 进行数据收发,那么 while 循环占用的 CPU 时间是在进行合理的工作的,这不属于浪费 CPU 的资源。只有循环占用 CPU 时间,但是实际上没有进行任何工作的,才是浪费 CPU 资源。
三、epoll 的底层设计是什么样的?
epoll 管理着千级别/万级别的 IO 连接,又有着非常高的性能,下面简单从一些方面介绍下 epoll 的设计。
注:epoll 的涉及的内容非常多,这里挑出一些比较核心的概念进行讲解,让大家有这么回事就行了。
3.1-epoll 的数据结构
之前说过,所有的 socket 连接都是由 epoll 进行管理的,在伪代码中我们使用 epoll_create 创建 epoll 之后,其实就是定义下面这样的一个 struct:
成员如下:
rbr:被 epoll 所管理的所有 socket 都存储在红黑树中。
rdllist:存储着所有就绪的 socket(所谓"就绪",意思指有网络数据到来,或者有数据要发送出去。)
3.1.1-为什么使用红黑树?
来看看 epoll 都有哪些操作:
socket 的查找。
socket 的新增。
socket 的删除。
红黑树是"平衡搜索树",其各种操作的事件复杂度可以维持在 O(logn),因此 epoll 选择使用红黑树来管理这些 socket。
3.1.2-就绪列表的优点
就绪列表这一块其实才是 epoll 能够高效的核心,对于每一个就绪的 socket,会直接放入 rdllist 中,epoll_wait 获取到就绪的 socket 时,直接从 rdllist 中进行获取就可以了,无需再进行查找。
3.2-数据的收发
epoll 是基于事件回调机制地,当 epoll 所管理地 socket 有数据到来时,会出发事件回调函数,将就绪的 socket 放置在 rdllist 中,并且通知 epoll_wait 所在的进程来处理数据。
四、扩展阅读:select
在多路复用中,还有一个经常提起的就是 select,这也是一个经常被使用的多路复用函数。
4.1-select 是如何工作的?
select 并没有像 epoll 一样,将所有的 socket 存储在红黑树中,而是使用一个数组,存储了所有的 socket 连接。
当建立新的 socket 连接时,会根据 socket id,将对应的索引位置置为 1。
select 运行时,会遍历整个数组,然后判断每一个 socket 是否就绪,就绪的话就进行处理,没有就绪就忽略。
4.2-epoll 相比于 select 的优点
更快的 IO 处理:epoll 判断 socket 是否就绪,直接从 rdllist 中进行读取,事件复杂度为(1);而 select 需要遍历整个数组查找 socket 是否就绪,复杂度为 O(n)。
能够处理的 socket 数量有限:epoll 能够处理千级别/万级别的 socket 连接;但是 select 默认情况下最多只能处理 1024 个连接,因此这是其并不能用于高并发的主要原因。
五、小结
最后,简单总结下 epoll 中的一些亮点吧:
支持海量并发连接。
使用时间复杂度为 O(logn)的红黑树管理所有的 socket。
提高 CPU 的使用率,高效地使用 CPU 时间片处理所管理的 socket 连接。
基于事件回调机制处理消息,而不是主动轮询机制。
......
版权声明: 本文为 InfoQ 作者【董哥的黑板报】的原创文章。
原文链接:【http://xie.infoq.cn/article/6e0e4822754f224cd11f8ed4e】。文章转载请联系作者。
评论