[导读]通过本文你将会了解到以下内容:IO事件和IO复用、线程模型和事件驱动模型的架构、基于事件驱动的Reactor模式详解、同步IO和异步IO简介。
1. 落寞的小黑
上周北京很冷,周五晚上大白下班奔地铁站,收到了好基友小黑的微信:
于是大白掉头扫了个单车奔五道口了,小黑靠谱地选了个不错的位置。
大白: 就咱这觉悟,心里有工作,哪里都是办公桌,不要拘泥于形式嘛。
明显能感觉得到小黑哥最近好像比较累,之前眼里bulingbuling闪的光是看不到了。
小黑: 是一家做自动驾驶的创业公司,网站是看团队介绍还不错,就去看看了,这次没咋准备,很多问题其实都熟悉,但是回答的不到位。
大白: 哦,明白了,那就是当时理解的不到位,稀里糊涂过去了,现在忽然问起来,想不起重点。
小黑: 差不多吧,问我都做过哪些高性能的网络框架模型,也就是IO和事件驱动那一套。
话说完,小黑喝了一大口啤酒,大白看出了小黑心里有一些落寞。
毕竟在帝都这个地方竞争和工作压力,以及生活琐事都一直围绕着我们,但是金钱和好运都巧妙地避开了自己...
想到这里,大白也深深喝了一大口,我命由我不由天,开整!
大白:黑哥,你说这个问题确实不好回答,全是术语和略带歧义的东西,我觉得我们抓住本质去阐述就好。
大白决定和小黑好好聊聊,Linux开发中常用的高性能网络框架中的一些事儿,火锅的映衬下让夜色和天气都不那么寒冷了。
2. IO事件和IO复用
2.1 什么是IO事件
IO指的是输入Input/输出Output,但是从汉语角度来说,出和入是相对的,所以我们需要个参照物。
这里我们的参照物选择为程序运行时的主存储空间,外部通常包括网卡、磁盘等。
有了上述的设定理解起来就方便多了,我们来一起看下:
IO的本质是数据的流动,数据可以从网卡到程序内存,也可以从程序内存写到网卡,磁盘操作也是如此。
事件可以理解为一种状态或者动作,也就是状态的迁移会触发一种相应的动作。
理解可读可写事件是非常有必要的,一般来说一个socket大部分时候是可写的,但是并不是都可读。
可读一般代表是一个新连接或者原有连接有新数据交互,对于服务端程序来说也是重点关注的事件。
2.2 什么是IO复用
设想假如有几万个IO事件,那么应用程序该如何管理呢?这就要提到IO复用了。
IO复用从本质上来说就是应用程序借助于IO复用函数向内核注册很多类型的IO事件,当这些注册的IO事件发生变化时内核就通过IO复用函数来通知应用程序。
从图中可以看到,IO复用中复用的就是一个负责监听管理这些IO事件的线程。
之所以可以实现一个线程管理成百上千个IO事件,是因为大部分时间里某个时刻只有少量IO事件被触发。
大概就像这样:
草原上的一只大狗可以看管几十只绵羊,因为大部分时候只有个别绵羊不守规矩乱跑,其他的都是乖乖吃草。
3. 网络框架设计要素
要理解网络框架有哪些,必须要清楚网络框架完成了哪些事情。
-
远端的机器A发送了一个HTTP请求到服务器B,此时服务器B网卡接收到数据并产生一个IO可读事件;
-
我们以同步IO为例,此时内核将该可读事件通知到应用程序的Listen线程;
-
Listen线程将任务甩给Handler线程,由Handler将数据从内核读缓冲区拷贝到用户空间读缓冲区;
-
请求数据包在应用程序内部进行计算和处理并封装响应包;
-
-
当这个连接可写时将数据从用户态写缓冲区拷贝到内核缓冲区,并通过网卡发送出去;
备注:上述例子是以同步IO为例,并且将线程中的角色分为Listen线程、Handler线程、Worker线程,分别完成不同的工作,后续会详细展开。
所以我们可以知道,要完成一个数据交互,涉及了几大块内容:
大白认为,这三大块内容,不论什么形式的框架都绕不开,也是理解网络架构的关键所在。
4. 高性能网络框架实践
4.1 基于线程模型
在早期并发数不多的场景中,有一种One Request One Thread的架构模式。
该模式下每次接收一个新请求就创建一个处理线程,线程虽然消耗资源并不多,但是成千上万请求打过来,性能也是扛不住的。
这是一种比较原始的架构,思路也非常清晰,创建多个线程来提供处理能力,但在高并发生产环境中几乎没有应用,本文不再展开。
4.2 基于事件驱动模型
当前流行的是基于事件驱动的IO复用模型,相比多线程模型优势很明显。
在此我们先理解一下什么是事件驱动Event-Drive-Model。
事件驱动编程是一种编程范式,程序的执行流由外部事件来决定,它的特点是包含一个事件循环,当外部事件发生时使用回调机制来触发相应的处理。
通俗来说就是:
有一个循环装置在一直等待各种事件的到来,并将到达的事件放到队列中,再由一个分拣装置来调用对应的处理装置来响应。
4.3 Reactor反应堆模式
第一次听到这个模式的时候很困惑,究竟反应堆是个啥?
研究了一下发现,反应堆是个核物理的概念,大致是这个样子的:
核反应堆是核电站的心脏 ,它的工作原理是这样的:原子由原子核与核外电子组成,原子核由质子与中子组成。
当铀235的原子核受到外来中子轰击时,一个原子核会吸收一个中子分裂成两个质量较小的原子核,同时放出2-3个中子。
这裂变产生的中子又去轰击另外的铀235原子核,引起新的裂变,
如此持续进行就是裂变的链式反应。
结合这种核裂变的图,好像
是一个请求打过来,服务器内部瞬间延伸出很多分支来完成响应,一变二,二变四,甚至更多,确实有种反应堆的感觉。
接下来我们看看究竟反应堆模式是如何构建高性能网络框架的。
5.反应堆模式详解
5.1 反应堆模式的本质是什么
从本质上理解,无论什么网络框架都要完成两部分操作:
所以上述这些问题
由谁来做以及多少线程来做,就衍生出了很多形式,所以不要被表面现象迷惑,出现必有原因,追溯之后我们才能真正掌握它。
反应堆模式根据处理IO环节和处理数据环节的数量差异分为如下几种:
我们来看看这三种常见模式的特点、原理、优缺点、应用场景等。
5.2 单Reactor线程模式
这种模式最为简洁,一个线程完成了连接的监听、接收新连接、处理连接、读取数据、写入数据全套工作。
由于只使用了一个线程,对于多核利用率偏低,但是编程简单。
是不是觉得这个种单线程的模式没有市场?那可未必,不信你看Redis。
在这种模式种IO操作和CPU操作是没有分开的,都是由1个线程来完成的,显然如果在Handler处理某个请求超时了将会阻塞客户端的正常连接。
在Redis中由于都是内存操作,速度很快,这种瓶颈虽然存在但是不够明显。
5.3 单Reactor线程和线程池模式
为了解决IO操作和CPU操作的不匹配,也就是IO操作和CPU操作是在一个线程内部串行执行的,这样就拉低了CPU操作效率。
一种解决方法就是将IO操作和CPU操作分别由单独的线程来完成,各玩各的互不影响。
单Reactor线程完成IO操作、复用工作线程池来完成CPU操作就是一种解决思路。
在这种模式种由Reactor线程完成连接的管理和数据读取&写回,完全掌管IO操作。
工作线程池处理来自上游分发的任务,对其中的数据进行解码、计算、编码再返回给Reactor线程和客户端完成交互。
这种模式有效利用了多核,但是单Reactor线程来完成IO操作在高并发场景中仍然会出现瓶颈。
换句话说,连接实在太多了,一个Reactor线程忙不过来建立新连接和响应旧连接这些事情,因此Reactor线程也需要几个帮手。
5.4 多Reactor线程和线程池模式
我们将Reactor线程进行扩展,一个Reactor线程负责处理新连接,多个Reactor线程负责处理连接成功的IO数据读写。
也就是进一步将监听&创建连接 和 处理连接 分别由两个及以上的线程来完成,进一步提高了IO操作部分的效率。
这种模式算是比较高配的版本了,在实际生产环境也有使用。
5.5 拓展:同步IO和异步IO
我们可以轻易区分什么是阻塞IO和非阻塞IO,那么什么是同步IO和异步IO呢?
前面提到Reactor模式其中非常重要的一环就是调用read/write函数来完成数据拷贝,这部分是应用程序自己完成的,内核只负责通知监控的事件到来了,所以本质上Reactor模式属于非阻塞同步IO。
还有一种Preactor模式,借助于系统本身的异步IO特性,由操作系统进行数据拷贝,在完成之后来通知应用程序来取就可以,效率更高一些,但是底层需要借助于内核的异步IO机制来实现。
底层的异步IO机制可能借助于DMA和Zero-Copy技术来实现,理论上性能更高。
当前Windows系统通过IOCP实现了真正的异步I/O,而在Linux 系统的异步I/O还不完善,比如Linux中的boost.asio模块就是异步IO的支持,但是目前Linux系统还是以基于Reactor模式的非阻塞同步IO为主。
6. 小结
本文从IO事件和IO复用出发,阐述了网络架构最底层的组成。
继续展开了基于线程模型和基于事件驱动模型的网络框架特点及其设计要素。
之后重点描述了反应堆模式的核心本质,以及生产环境中的多种形式。
最后简单介绍了同步IO和异步IO的区别,以及Preactor模式的优势。
希望读者朋友可以摒弃专业术语和表述,抓住问题的本质和重点,找到一个适合自己思维方法去理解和掌握高性能网络架构的设计之道。
最后依然感谢各位的倾情阅读,如果有问题,欢迎添加大白微信进行讨论交流:
往期精彩文章
图解|为什么HTTP3.0使用UDP协议
图解|通用搜索引擎背后的技术点
图解什么是一致性哈希算法
图解|什么是缺页错误Page Fault
图解|什么是缓存系统三座大山
深入理解快速排序和STL的sort算法
Linux服务端最大并发数是多少?
免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!
本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
当我们谈论RISC-V的设计特色,讨论得最多的往往是它开源开放的属性、模块化的指令集设计,还有灵活的特权级架构。但很少有人会深入拆解它的启动流程,去品味这套架构在启动阶段设计里藏着的巧思。我自己在做RISC-V嵌入式开发...
关键字:
RISC-V
Linux
在操作系统学习和后端开发面试中,“线程间共享哪些资源、又隔离哪些资源”是一个高频问题,很多人对此一知半解:只记得“线程共享进程地址空间”,但具体到哪些内存区域、哪些资源是共享的,哪些是线程私有,总是说不清楚。实际上,理解...
关键字:
Linux
线程
在Linux系统的C语言开发中,字节对齐是一个绕不开的基础话题。很多开发者都遇到过这样的困惑:明明计算结构体大小时把每个成员的字节数加起来,结果实际大小却比计算值大上好几倍,甚至修改结构体成员的排列顺序,总大小还会发生变...
关键字:
Linux
CPU
中国北京(2026年6月23日)—— 业界领先的半导体器件供应商兆易创新GigaDevice(股票代码:603986.SH;3986.HK)宣布与全球领先的软件设计、开发与质量解决方案提供商Qt Group正式达成合作,...
关键字:
嵌入式
GUI
智能家居
在嵌入式开发、工业控制、电力电子等领域,PWM(脉冲宽度调制,Pulse Width Modulation)是应用最广泛的模拟量控制技术之一。小到智能家电的电机调速、LED亮度调节,大到新能源汽车的电机驱动、光伏并网逆变...
关键字:
PWM
嵌入式
在电子工程、芯片调试、嵌入式开发等领域,JTAG是绕不开的核心技术术语。很多从业者刚接触时总会疑惑:这个常被挂在嘴边的JTAG到底是什么?实际使用中又该怎么判断一套JTAG接口或设备是否正常?要理清这些问题,得从它的诞生...
关键字:
JTAG
嵌入式
把推理放到本地,并不自动等于隐私安全;很多泄露发生在日志、特征和升级包边界。嵌入式AI如果只保护原始数据,不保护模型和中间结果,攻击面仍然很宽。
关键字:
嵌入式
AI
密钥
实验室准确率不低,现场却频繁误触,往往不是模型突然失效,而是决策层没有给噪声和不确定样本留出口。嵌入式AI如果只输出最高分标签,边界样本会被硬塞进错误动作。
关键字:
嵌入式
AI
误触
长时间满负载跑模型时,板子最先拒绝的可能不是算法,而是电源和散热余量。嵌入式AI若把峰值算力当持续能力,延迟会在温升、限流和降频之间突然拉长。
关键字:
嵌入式
AI
热设计
模型能在开发板上跑起来,不等于能在控制周期里稳定跑完。嵌入式AI最先暴露的常不是算力峰值不够,而是算子拆分和内存搬运把推理时间切成了不可预测的碎片。
关键字:
嵌入式
AI
算子
模型升级不像替换一份普通资源,因为它同时改动推理图、预处理和判定阈值。嵌入式AI若没有把版本依赖和回滚状态写清,一次在线更新就可能让设备保持可启动却不可用。
关键字:
嵌入式
AI
模型
摄像头、麦克风和执行器都能按时工作,并不代表推理结果活在正确时刻。嵌入式AI一旦把流水线排队和时间戳混在一起,闭环就会拿过去的画面控制未来的动作。
关键字:
嵌入式
AI
时间戳
精度在桌面验证良好,移到板端却掉点,常说明量化边界没有被真实数据喂饱。嵌入式AI的 INT8 部署如果只追求模型变小,误差会先从分布尾部进入决策。
关键字:
嵌入式
AI
校准
一个模型独占开发板时延迟很好,和通信、控制、存储一起跑却超时,说明冲突发生在系统资源而不是网络结构本身。嵌入式AI多任务部署要先回答谁能等、谁不能等。
关键字:
嵌入式
AI
批处理
在嵌入式Linux/RTOS设备联调中,常需一个轻量网络调试助手——既能发RAW TCP/UDP包、又能做简单HTTP查看,以验证PHY、交换机或云端连通性。lwIP(lightweight IP stack) 以其极小...
关键字:
lwIP
嵌入式
在智能硬件遍地开花的今天,物联网、嵌入式、单片机这三个高频出现的技术名词,常常让不少入门学习者混淆边界。很多人会简单把它们等同起来,觉得“做物联网就是写单片机代码”,但实际深入行业就会发现,三者是一套从底层硬件到上层应用...
关键字:
物联网
嵌入式
随着消费电子、可穿戴设备、微型物联网终端及高密度嵌入式系统向小型化、集成化迭代,空间受限设计已成为硬件研发的主流场景。这类设计的核心矛盾集中在有限物理体积与高集成、高性能、低功耗需求的冲突,传统功率控制方案依赖大体积散热...
关键字:
物联网
嵌入式
电源模块
在嵌入式开发的日常工作里,几乎每个工程师都曾和串口通信打过无数交道。当系统需要频繁输出传感器数据、调试日志或者控制指令时,大家第一反应往往是把阻塞式的查询发送换成DMA传输——毕竟所有人都知道,DMA能把CPU从逐字节搬...
关键字:
嵌入式
串口
在嵌入式产品开发中,兼容性问题是最容易被忽视却影响深远的“隐形陷阱”:同一套软件在首批芯片上运行正常,更换批次就出现不定期死机;在开发板上调试完美,换到量产PCB就功能异常;用A编译器编译运行稳定,升级编译器版本就出现启...
关键字:
嵌入式
兼容性
在嵌入式Linux开发中,管脚配置与GPIO控制是驱动开发的核心基础工作。从嵌入式单板到桌面级服务器,几乎所有硬件交互都离不开对芯片管脚的配置与管理。早期Linux内核没有统一的管脚管理框架,各个厂商的BSP代码各自为政...
关键字:
Linux
GPIO