关于网络:epoll的本质

56次阅读

共计 2736 个字符,预计需要花费 7 分钟才能阅读完成。

从事服务端开发,少不了要接触网络编程。epoll 作为 linux 下高性能网络服务器的必备技术至关重要,nginx、redis、skynet 和大部分游戏服务器都应用到这一多路复用技术。

/ 罗培羽

因为 epoll 的重要性,不少游戏公司(如某某九九)在招聘服务端同学时,可能会问及 epoll 相干的问题。比方 epoll 和 select 的区别是什么?epoll 高效率的起因是什么?如果只靠背诵,显然不能算上粗浅的了解。

网上尽管也有不少解说 epoll 的文章,但要不是过于通俗,就是陷入源码解析,很少能有通俗易懂的。于是决定编写此文,让不足业余背景常识的读者也可能明确 epoll 的原理。文章核心思想是:

要让读者清晰明确 EPOLL 为什么性能好。

本文会从网卡接收数据的流程讲起,串联起 CPU 中断、操作系统过程调度等常识;再一步步剖析阻塞接收数据、select 到 epoll 的进化过程;最初探索 epoll 的实现细节。目录:

一、从网卡接收数据说起
二、如何晓得接管了数据?
三、过程阻塞为什么不占用 cpu 资源?
四、内核接管网络数据全过程
五、同时监督多个 socket 的简略办法
六、epoll 的设计思路
七、epoll 的原理和流程
八、epoll 的实现细节
九、论断

一、从网卡接收数据说起

下图是一个典型的计算机结构图,计算机由 CPU、存储器(内存)、网络接口等部件组成。理解 epoll 实质的 第一步 ,要从 硬件 的角度看计算机怎么接管网络数据。

计算机结构图(图片起源:linux 内核齐全正文之微型计算机组成构造)

下图展现了网卡接收数据的过程。在①阶段,网卡收到网线传来的数据;通过②阶段的硬件电路的传输;最终将数据写入到内存中的某个地址上(③阶段)。这个过程波及到 DMA 传输、IO 通路抉择等硬件无关的常识,但咱们只需晓得:网卡会把接管到的数据写入内存。

网卡接收数据的过程

通过硬件传输,网卡接管的数据寄存到内存中。操作系统就能够去读取它们。

二、如何晓得接管了数据?

理解 epoll 实质的 第二步 ,要从CPU 的角度来看数据接管。要了解这个问题,要先理解一个概念——中断。

计算机执行程序时,会有优先级的需要。比方,当计算机收到断电信号时(电容能够保留少许电量,供 CPU 运行很短的一小段时间),它应立即去保留数据,保留数据的程序具备较高的优先级。

一般而言,由硬件产生的信号须要 cpu 立马做出回应(不然数据可能就失落),所以它的优先级很高。cpu 理当中断掉正在执行的程序,去做出响应;当 cpu 实现对硬件的响应后,再从新执行用户程序。中断的过程如下图,和函数调用差不多。只不过函数调用是当时定好地位,而中断的地位由“信号”决定。

中断程序调用

以键盘为例,当用户按下键盘某个按键时,键盘会给 cpu 的中断引脚收回一个高电平。cpu 可能捕捉这个信号,而后执行键盘中断程序。下图展现了各种硬件通过中断与 cpu 交互。

cpu 中断(图片起源:net.pku.edu.cn)

当初能够答复本节提出的问题了:当网卡把数据写入到内存后,网卡向 cpu 收回一个中断信号,操作系统便能得悉有新数据到来 ,再通过网卡 中断程序 去解决数据。

三、过程阻塞为什么不占用 cpu 资源?

理解 epoll 实质的 第三步 ,要从 操作系统过程调度 的角度来看数据接管。阻塞是过程调度的要害一环,指的是过程在期待某事件(如接管到网络数据)产生之前的期待状态,recv、select 和 epoll 都是阻塞办法。理解“过程阻塞为什么不占用 cpu 资源?”,也就可能理解这一步

为简略起见,咱们从一般的 recv 接管开始剖析,先看看上面代码:

// 创立 socket
int s = socket(AF_INET, SOCK_STREAM, 0);   
// 绑定
bind(s, ...)
// 监听
listen(s, ...)
// 承受客户端连贯
int c = accept(s, ...)
// 接管客户端数据
recv(c, ...);
// 将数据打印进去
printf(...)

这是一段最根底的网络编程代码,先新建 socket 对象,顺次调用 bind、listen、accept,最初调用 recv 接收数据。recv 是个阻塞办法,当程序运行到 recv 时,它会始终期待,直到接管到数据才往下执行。

插入:如果您还不太熟悉网络编程,欢送浏览我编写的《Unity3D 网络游戏实战(第 2 版)》,会有具体的介绍。

那么阻塞的原理是什么?

工作队列

操作系统为了反对多任务,实现了过程调度的性能,会把过程分为“运行”和“期待”等几种状态。运行状态是过程取得 cpu 使用权,正在执行代码的状态;期待状态是阻塞状态,比方上述程序运行到 recv 时,程序会从运行状态变为期待状态,接管到数据后又变回运行状态。操作系统会分时执行各个运行状态的过程,因为速度很快,看上去就像是同时执行多个工作。

下图中的计算机中运行着 A、B、C 三个过程,其中过程 A 执行着上述根底网络程序,一开始,这 3 个过程都被操作系统的工作队列所援用,处于运行状态,会分时执行。

工作队列中有 A、B 和 C 三个过程

期待队列

当过程 A 执行到创立 socket 的语句时,操作系统会创立一个由文件系统治理的 socket 对象(如下图)。这个 socket 对象蕴含了发送缓冲区、接收缓冲区、期待队列等成员。期待队列是个十分重要的构造,它指向所有须要期待该 socket 事件的过程。

创立 socket

当程序执行到 recv 时,操作系统会将过程 A 从工作队列挪动到该 socket 的期待队列中(如下图)。因为工作队列只剩下了过程 B 和 C,根据过程调度,cpu 会轮流执行这两个过程的程序,不会执行过程 A 的程序。所以过程 A 被阻塞,不会往下执行代码,也不会占用 cpu 资源

socket 的期待队列

ps:操作系统增加期待队列只是增加了对这个“期待中”过程的援用,以便在接管到数据时获取过程对象、将其唤醒,而非间接将过程治理纳入本人之下。上图为了不便阐明,间接将过程挂到期待队列之下。

唤醒过程

当 socket 接管到数据后,操作系统将该 socket 期待队列上的过程从新放回到工作队列,该过程变成运行状态,继续执行代码。也因为 socket 的接收缓冲区曾经有了数据,recv 能够返回接管到的数据。

以下内容待续

四、内核接管网络数据全过程

五、同时监督多个 socket 的简略办法

六、epoll 的设计思路

七、epoll 的原理和流程

八、epoll 的实现细节

九、论断

既然说到网络编程,笔者的 《Unity3D 网络游戏实战(第 2 版)》 是一本专门介绍如何开发 多人网络游戏 的书籍,用实例介绍开发游戏的全过程,十分实用。书中对网络编程有具体的解说,全书用一个大例子贯通,真正的“实战”教程。

致谢:本文力求具体阐明 epoll 的原理,特别感谢

@陆俊壕

@AllenKong12

雄爷、堂叔 等共事审阅了文章并给予修改意见。

正文完
 0