从事服务端开发,少不了要接触网络编程。epoll 作为 Linux 下高性能网络服务器的必备技术至关重要,nginx、Redis、Skynet 和大部分游戏服务器都使用到这一多路复用技术。
epoll 很重要,但是 epoll 与 select 的区别是什么呢?epoll 高效的原因是什么?
网上虽然也有不少讲解 epoll 的文章,但要么是过于浅显,或者陷入源码解析,很少能有通俗易懂的。笔者于是决定编写此文,让缺乏专业背景知识的读者也能够明白 epoll 的原理。
文章核心思想是:要让读者清晰明白 epoll 为什么性能好。
本文会从网卡接收数据的流程讲起,串联起 CPU 中断、操作系统进程调度等知识;再一步步分析阻塞接收数据、select 到 epoll 的进化过程;最后探究 epoll 的实现细节。
一、从网卡接收数据说起
下边是一个典型的计算机结构图,计算机由 CPU、存储器(内存)与网络接口等部件组成,了解 epoll 本质的第一步,要从硬件的角度看计算机怎样接收网络数据。
计算机结构图(图片来源:Linux内核完全注释之微型计算机组成结构)
下图展示了网卡接收数据的过程。
- 在 ① 阶段,网卡收到网线传来的数据;
- 经过 ② 阶段的硬件电路的传输;
- 最终 ③ 阶段将数据写入到内存中的某个地址上。
这个过程涉及到 DMA 传输、IO 通路选择等硬件有关的知识,但我们只需知道:网卡会把接收到的数据写入内存。
网卡接收数据的过程
通过硬件传输,网卡接收的数据存放到内存中,操作系统就可以去读取它们。
二、如何知道接收了数据?
了解 epoll 本质的第二步,要从 CPU 的角度来看数据接收。理解这个问题,要先了解一个概念——中断。
计算机执行程序时,会有优先级的需求。比如,当计算机收到断电信号时,它应立即去保存数据,保存数据的程序具有较高的优先级(电容可以保存少许电量,供 CPU 运行很短的一小段时间)。
一般而言,由硬件产生的信号需要 CPU 立马做出回应,不然数据可能就丢失了,所以它的优先级很高。CPU 理应中断掉正在执行的程序,去做出响应;当 CPU 完成对硬件的响应后,再重新执行用户程序。中断的过程如下图,它和函数调用差不多,只不过函数调用是事先定好位置,而中断的位置由“信号”决定。
中断程序调用
以键盘为例,当用户按下键盘某个按键时,键盘会给 CPU 的中断引脚发出一个高电平,CPU 能够捕获这个信号,然后执行键盘中断程序。下图展示了各种硬件通过中断与 CPU 交互的过程。
CPU 中断(图片来源:net.pku.edu.cn)
现在可以回答“如何知道接收了数据?”这个问题了:当网卡把数据写入到内存后,网卡向 CPU 发出一个中断信号,操作系统便能得知有新数据到来,再通过网卡中断程序去处理数据。
三、进程阻塞为什么不占用 CPU 资源?
了解 epoll 本质的第三步,要从操作系统进程调度的角度来看数据接收。阻塞是进程调度的关键一环