前言
网络通信作为互联网的底座,其网络服务质量直接影响着用户的上网体验。如微信这类级别的应用,拥有上亿级别的日活,是典型的高并发的场景,简单的堆硬件无法有效的解决该类问题,提高单台服务器的性能成为问题的焦点。随着各大互联网公司业务的扩展,越来越多的互联网公司青睐高性能、高并发技术的网络技术,通过性能的优化来提升系统的容量,进而可实现降本增效的目标。为解决网络高性能的问题,业界提供了硬件和软件两类解决方案,不少公司基于开源的DPDK框架和专用芯片来提升系统性能。
纯软件角度的技术方案主要有dpdk和ebpf两个大方向,这两类解决思路都需要对内核协议栈有较深刻的理解。通过对内核协议栈的梳理,进而了解内核协议栈要解决的问题域及相关问题场景,对理解dpdk的设计思想和整体架构会有很大帮助。
网络子系统是内核中非常复杂的子系统,其涉及到设备管理、驱动、中断、内存子系统、复杂的网络处理逻辑以及软件开发的工程经验,理解网络协议栈是对工程师知识面及知识深度的全方位挑战。网络子系统代码量庞大,知识点琐碎,本文以宏观概要的形式来展示内核协议栈,来建立内核协议栈的大局观,避免陷入琐碎的细节。
一、内核收包宏观流程
思考
-
内核协议栈内容繁杂,从哪些维度来梳理内核协议栈?
-
内核协议栈代码量庞大,使用了面对对象思想,充分应用了抽象工厂模式、命令模式等设计模式提高了代码的可维护性,通过什么方式来提高对内核代码的理解深度?如何便于记忆?
-
在内核协议栈中,都有哪些关键数据结构及核心操作?
-
CPU、内存、网卡、总线等硬件是如何交互的?各个软件子系统之间是如何交互的?
-
整个收包流程分为哪些阶段?
-
操作系统作为大管家,在网络子系统中是如何与用户交互的,提供了哪些可配置接口?
内核收包流程
-
0.加载驱动,驱动初始化;
-
1.数据包达到网卡 ;
-
2.网卡产生硬件中断;
-
3.CPU响应硬件中断;
-
4.CPU关闭硬件中断,产生软件中断,进入中断处理逻辑,调用相关回调函数;
-
5.激活ksoftirqd线程处理数据包;
-
6.数据包以skb的数据格式,进入netif_recv_core处理逻辑;
-
7.若开启RPS,则将报文分发给多核处理;
-
8.进入协议栈处理逻辑;
-
9.进入应用层处理逻辑;
备注:本章内容参考收包流程博客,缺少tcp/ip相关信息,未对netfilter/iptables/tc等相关安全组件进行介绍。
二、微观视角
概述
第一部分从宏观整体角度介绍内核收包,用于建立初步的直观印象,但内核收包流程过于复杂,仅有宏观视角还不足以深刻理解收包流程,这一部分以图的形式对收包流程中的几个关键点来进行展开。本文的目标是理解dpdk框架,未对tcp/ip部分进行展开介绍。
内核收包涉及到硬件与硬件、硬件与驱动、硬件与内核等维度的交互;在数据流转角度,涉及到如何将网卡内存中的数据拷贝到RAM中;聚焦到代码层面,主要有三个方面的逻辑:①初始化与销毁操作;②核心处理逻辑;③用户交互与配置逻辑。 接下来从四个维度来展开收包流程:
①初始化操作,工作线程及核心数据结构的创建; ②网卡收包,网卡、CPU、内存、驱动、中断是如何协调工作的; ③内核收包,驱动与内核协议栈是如何关联的; ④内核收包之RPS,将资源分发到不同CPU上。 备注:本章图片来自于图解内核协议栈,在个人理解的基础上,稍作修改。
初始化操作
作系统作为计算机系统的大管家,提供了设备接入系统的接口,设备驱动以内核模块的形式注入内核,将驱动的回调函数保存到napi的polllist中,当数据包到来时,根据软中断信息选择相应的函数。
内核收包前的准备工作主要有两点:①内核启动ksoftirqd线程响应中断;②通过系统调用将驱动挂载到系统中。
收包流程
张图介绍了收包过程中,CPU、网卡、内存、驱动之间交互的关系,在硬件交互过程中,中断起到了各组件之间通信的作用。
在计算机系统发展的初期,CPU与磁盘、网卡等外部设备的读写速度相差多个数量级,在外设准备好计算所需计算数据之前,快如"战斗机"的CPU不再等待慢如"老牛"的外设,先去处理其他已就绪的计算任务,当外设准备好所需数据后,以中断的方式通知CPU该任务已处于就绪状态,可以继续工作了。设备以异步的形式并行处理计算任务,充分释放了CPU的性能,也提升了系统的吞吐和资源的使用率。
随着社会的进步和互联网的普及,网络已成为普通大众不可或缺的基础设施,在当前形势下也促进了网络技术的发展,当前网卡设备逐渐演变成高速设备。在linux内核设计的初期,将网卡定位于低速设备,在当今大流量高并发场景下,该中断机制在一定程度上制约了计算机系统的性能。内核响应网卡中断,同时伴随着上下文的切换,浪费了系统资源,在这方面DPDK提供了相关的优化方案。
收包流程
该图介绍了内核响应中断,到驱动程序以及协议栈的相关流程。内核将中断号及对应回调函数保存在softirq_vec中,当数据包到达时,根据NET_RX_SOFTIRQ中断号进入net_rx_action处理逻辑。
为提升系统性能,内核提供了包合并功能,将小包合并成大包,来降低系统资源的开销。
收包流程
该图介绍介绍了进入协议栈的流程,内核若开启了RPS选项,会将数据包派发到不同的CPU上。
相关视频推荐
2024年,dpdk/spdk技术专家成长体系教程,c/c++程序员值得深耕的高薪就业方向,包含(dpdk/网络协议栈/vpp/OvS/DDos/SPDK..)https://www.bilibili.com/video/BV1Up42117Xq/
Linux C/C++开发(后端/音视频/游戏/嵌入式/高性能网络/存储/基础架构/安全)
需要C/C++ Linux服务器架构师学习资料加qun812855908获取(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享
三、DPDK收包流程
思考
-
操作系统是计算机系统的大管家,负责资源的管理,一般情况下通过系统调用来获取数据和计算资源,但DPDK是用户态开发框架,是如何突破内核限制,是如何将数据包拉取到用户态的?
-
网卡将数据包DMA到内存时,需要配置目的地址和长度,这些操作在DPDK中是如何实现的?
-
内核通过设置设备寄存器来访问外设,在DPDK中也许与外设交互,由于操作系统的资源限制,在用户态下如何与硬件进行交互?
DPDK收包流程
DPDK是用户态开源框架,网卡通过DMA将数据包拷贝到用户态内存,但DMA引擎需要知道目标地址所对应的物理地址,在用户态只存在虚拟地址,UIO内核驱动将虚拟地址映射为物理地址。
收包流程中需要与网卡进行交互,需要访问网卡的物理内存及寄存器,UIO将网卡的物理地址映射到虚拟地址空间,在用户态访问虚拟地址就可以访问网卡,通过向虚拟地址中写数据解可以完成对寄存器的控制。
本图在描述收包流程上并不准确,DMA将数据包拷贝到RAM的流程中,涉及到TDH/TDT寄存器、收包队列描述符等相关概念,在上面并没有明确标出,但这几组概念非常重要。
DPDK的收包流程可参考第一章,这里不再赘述。
理解UIO
UIO即用户态通过虚拟地址来访问外设。
在计算机系统中,CPU、内存、外设通过总线来进行通信,设备之间通信使用物理地址进行通信。操作系统在初始化时,根据DTS内容信息为外设配置物理地址,用物理地址来唯一标识外设的地址空间。在32位系统中,RAM和外设共用4GB的物理地址空间,RAM的物理地址是计算机系统物理地址空间的子集。 uio技术将设备的物理地址空间映射到虚拟地址空间,用户空间的应用程序可以通过虚拟地址,来访问外设的内存和寄存器。
驱动注册流程
DPDK可简单分为两类代码:驱动和lib库,向用户提供以rte_xxx开头的API程序。在管理驱动代码时,使用了工厂模式,将驱动对象注册到rte_pci_bus上,在rte_eal_init()中调用rte_bus_scan()完成相关驱动的挂载,这块的逻辑与内核的操作相似。
相关数据结构
上图为与本文密切相关的数据结构,以rte_eth_dev为核心进行展开,其中ixgbe_rx_queue是连接DMA与用户态程序的关键。