转载地址:https://zhuanlan.zhihu.com/p/63179839

从事服务端开发,少不了要接触网络编程。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的实现细节

九、结论

系列文章

如果这篇文章说不清epoll的本质,那就过来掐死我吧! (1)

如果这篇文章说不清epoll的本质,那就过来掐死我吧! (2)

Bestiario:如果这篇文章说不清epoll的本质,那就过来掐死我吧! (3)

公众号

希望大家多多关注,里面不定期发放干货

http://weixin.qq.com/r/fUOCmvbEh6v4rbL59xZv (二维码自动识别)

epoll原理_如果这篇文章说不清epoll的本质,那就过来掐死我吧! (1)相关推荐

  1. 基础技术 - 如果这篇文章说不清epoll的本质,那就过来掐死我吧!

    本文主体转自https://zhuanlan.zhihu.com/p/63179839,加上了自己的理解和批注 从事服务端开发,少不了要接触网络编程.epoll作为linux下高性能网络服务器的必备技 ...

  2. java黄油刀_ButterKnife原理解析看这篇文章就够了

    原标题:ButterKnife原理解析看这篇文章就够了 作者:SheHuan https://juejin.im/post/5acec2b46fb9a028c6761628 ButterKnife 算 ...

  3. 如果有人问你数据库的原理,看这篇文章。(超长预警)

    长文,相当的通透.以下为scrat进行总结后的删减版. 下面假定你已经知道时间复杂度 关系型数据库无处不在,而且种类繁多,从小巧实用的 SQLite 到强大的 Teradata .但很少有文章讲解数据 ...

  4. python装饰器原理-看完这篇文章还不懂Python装饰器?

    原标题:看完这篇文章还不懂Python装饰器? 1.必备 2.需求来了 初创公司有N个业务部门,1个基础平台部门,基础平台负责提供底层的功能,如:数据库操作.redis调用.监控API等功能.业务部门 ...

  5. 还没吃透内存缓存LruCache实现原理的看这篇文章,面试必会

    前言 这篇文章主要是分享今年上半年的面试心得,现已就职于某大厂有三个月了,近期有很多公司均已启动秋招,也祝大家在 2020 的下半年面试顺利,获得理想的offer! 之前找工作的那段时间感想颇多,总结 ...

  6. epoll原理_彻底搞懂epoll高效运行的原理

    前言 这篇文章读不懂的没关系,可以先收藏一下.笔者准备介绍完epoll和NIO等知识点,然后写一篇Java网络IO模型的介绍,这样可以使Java网络IO的知识体系更加地完整和严谨.初学者也可以等看完I ...

  7. websocketapp保活,还没吃透内存缓存LruCache实现原理的看这篇文章,看完直呼内行

    一.2021新的篇章 2021也该规划一下自己的职业生涯了:是选择继续从事Android(android的话已经火了好几年了,现在算是进入寒冬了,需要考虑清楚)?还是从事Java方面?还是改管理方面? ...

  8. Spring原理只要看这篇文章就够了

    Spring 的骨骼架构 Spring 总共有十几个组件,但是真正核心的组件只有几个,下面是 Spring 框架的总体架构图: 图 1 .Spring 框架的总体架构图 从上图中可以看出 Spring ...

  9. 还没吃透内存缓存LruCache实现原理的看这篇文章,跳槽薪资翻倍

    目前情况:10届某民办大学本科生,实际接触Android年限6年多了,工作年限五年半(注意,我说的是工作年限,不是工作经验),今年1月份裸辞后歇了大半年,经常一周也收不到几个offer,好不容易熬到H ...

最新文章

  1. sqlserver 2008 R2 删除重复数据
  2. 获取网络时间并刷新本地时间(源码2)
  3. Cloudflare的HTTP/2优化策略
  4. python语言语块句的标记_《自然语言处理理论与实战》
  5. cesium添加填充_Cesium中级教程1 - 空间数据可视化(一)
  6. 方格取数(信息学奥赛一本通-T1277)
  7. 必须要知道Java如何取得当前路径
  8. 封装成jar包_通用源码阅读指导mybatis源码详解:io包
  9. 监听独立于数据库服务器的配置,解决ORA-12520及ORA-12545错误
  10. 趣谈网络协议栈,以太网基础MAC和PHY
  11. 客户档案管理,批发零售进销存收银财务一体管理软件
  12. 我的2021 年终总结
  13. php远程登录linux,如何远程连接linux桌面
  14. 全球制造业“看上去很美”
  15. 手把手教你撸一个Web汇率计算器
  16. 特征级融合_自动驾驶系统入门(七)- 多传感器信息融合
  17. 51单片机仿真例程-双机串行通信
  18. 标梵互动讲解怎样深度进入学习web前端开发
  19. 5. VBA消息框(MsgBox)
  20. python将pyc转为py

热门文章

  1. apt 和 apt-get 区别
  2. 虚拟机里ubuntu扩容主分区/dev/sda1
  3. Android dalvik GC相关的属性详解
  4. Mac安装apktool/dex2jar/jd-gui逆向工具
  5. typescript之http请求
  6. android之Canvas绘制图片
  7. RAID0、1、5、6、10介绍
  8. windows查看器无法打开图片_Win7自带图片查看器异常
  9. Linux安装nextcloud教程,WSL下安装nextcloud
  10. python中使用什么来实现异常捕捉_python 异常捕捉