【重识云原生】第四章云网络4.9.3.2节——DPDK原理详解

2022-09-08 12:07:06 浏览数 (1)

2 dpdk解决方案

        相对传统的基于内核的网络数据处理,dpdk 对从内核层到用户层的网络数据流程进行了重大突破,我们先看看传统的数据流程和 dpdk 中的网络流程有什么不同。

        传统 Linux 内核网络数据流程:

        dpdk 网络数据流程:

        DPDK拦截中断,不触发后续中断流程,并绕过协议栈,通过UIO技术将网卡收到的报文拷贝到应用层处理,报文不再经过内核协议栈。减少了中断,DPDK的包全部在用户控件使用内存池管理,内核控件与用户空间的内存交互不用进行拷贝,只做控制权转移,减少报文拷贝过程,提高报文的转发效率。

2.1 DPDK加速方案设计

        如果以CPU为核心来划分,提高报文处理的吞吐率,DPDK加速技术在顶层设计上大致涉及以下四个方面的内容:

1)消除IO延迟阻塞CPU:

        IO问题(包括隐式的自动取指和显示的内存读写指令)是拖垮CPU处理报文吞吐率的第一杀手。以Linux的IP栈为例,对报文的处理过程中,远超过90%以上CPU时钟周期是消耗在内存读写时的Cache行填充等待上,结果导致CPU核内的执行单元在绝大多数时间实际处于空闲等待状态。

2)提高CPU利用率:

        CPU利用率的问题就是如何减少CPU核的空闲等待时间,包括CPU核等待、以及核/超线程内部的执行单元的闲置等待两个方面。

3)提高CPU效率:

        CPU效率和利用率不是一回事,效率问题要解决的是减少无用功,例如减少上下文切换的开销。

4)CPU卸载任务给外设:

        CPU卸载就是把CPU核要干的部分活,分派给外设或其他单元去干,DPDK常见的是卸载给网卡去干。这样可以减轻CPU的负荷来提高CPU处理报文的吞吐率。

        消除IO延迟阻塞CPU,是重要的基础技术,下面简单解释几个这类技术在DPDK中的具体使用技巧:

  • 数据结构局部性:在设计数据结构时,预判分析在某个时段(如某个函数或依次调用的函数集)内,需要密集访问的数据域,集中在尽可能小的内存空间内,并且尽可能在一个Cache行内。例如:mbuf的控制结构,绝大多数报文在数据平面要使用的字段,集中在mbuf控制结构的第一个Cache内,第二个Cache行内的字段,在数据平面一般情况下都不会使用。报文buffer数据结构的精心设计,是提高报文处理性能的核心要害点,相对而言,Linux的报文buffer数据结构sk-buffer的设计非常糟糕,成为报文处理吞吐率的重要杀手。凭经验值估计,如果DPDK的报文buffer换成sk-buffer,吞吐率会降数倍。
  • 多报文批处理:从收发包,到对报文处理各种业务功能处理的函数,多数函数一次调用处理一批报文。而不是象Linux的IP栈一样单报文跑全程。根据“时间/空间局部性原理”可得知,多报文批处理可以显著提高Cache特别是L1-Cache的命中率(I-Cache和D-Cache命中率都会提高),分摊函数调用时堆栈/寄存器组上下文切换的开销(根据测试数据,函数调用上下文切换的开销均值达到20%左右)。同时,批处理模式还是其他报文处理加速技术的基础,例如:只有在批处理模式下,“读写延迟隐藏”相关技术的实现才有可能。
  • Cache行预取:对于IA架构来说,L1-Cache访问时延为4个时钟周期,L2为12个,L3需要26-31个时钟周期。如果三级Cache全部脱靶,一次内存访问需要100多个(约140个左右)的时钟周期,耗费的CPU核的时间是几百甚至上千条指令的损失!如新的haswell架构的IA-64核,每核每时钟周期是8 issue(发射),即每周期最多可以发射8条指令和retirement(引退)8条指令。为了隐藏内存访问无法容忍的延迟,避免因等待内存访问完成让核内的执行单元处于空闲等待的状态,提前将需要访问的内存内容预取到Cache中。
  • 交织:IA-64的haswell以后的架构,每周期8发射,其中4条RW和4条非RW如算术/逻辑/分支等类型指令;老的x86架构是4发射,类似是2条RW 2条非RW。因此,要让核内的流水线满负荷运行,RW指令和非RW指令交替编排,并且邻近指令间没有数据相关性,是让流水线满负荷且不断流的唯一模式。这是RW指令和非RW指令间的交织。另一个很重要的交织模式是:Cache行数据预取和数据处理的交织,例如,在多报文批处理中,预取下一个报文或者下一个小批次指文组的处理过程中要使用的数据结构、报文内容等,然后再处理当前报文或者当前小批次报文组。报文相关数据的Cache行预取和报文处理错位交替推进,是交织处理的典型模式,可以高效地隐藏Cache行填充的延迟。
  • 自动预取:CPU核内有自动预取单元,硬件系统会自动预取数据到Cache行。因此就像航海的人需要了解海洋洋流一样,软件工程师也需要彻透了解硬件自动预取逻辑和算法,顺着洋流而不是逆流而行。

        在DPDK中还使用了其他很多的报文处理加速的实现技巧,例如:DDIO、减少MMIO、提高PCIE传输效率、避免Cache行部分写、分支预测、大页内存、展开循环、展开函数、线程私有结构、核/CPU私有结构、无锁设计、无锁队列环ring、原子操作、RCU机制、读写锁、自旋锁、信息版本序号(分布式时戳)、用户态收发及UIO、报文零COPY、PMD收包、CPU亲和性、SIMD、调度模型:RTC及pipeline模式等等。在这里不再详述。在后续的分项专题中,每个技巧将会讲清楚:背景基础理论、运用要点、实例及分析。

        总的说来,DPDK是一个优秀的通信设备数据平面开发平台,提供数据平面开发工具集,为IA(Intel architecture)架构提供高效的用户态数据报文处理的库函数和驱动框架的支持。不同于Linux系统以通用性为设计目标,而是专注于数通设备或网络应用中数据包的高性能处理;DPDK数据平面工作在用户态,对需要转发的业务报文,旁路掉Linux中对数据报文进行处理的核心态IP栈。

        DPDK的顶层设计对硬件体系架构特性的利用很充分,也较为成功,但具体到二级模块级设计、详细设计后不尽完美,再到实现中的代码级,欠优化的地方更多。DPDK调度模型RTC和pipeline也较为粗犷,实际使用中需要精细调优。

        DPDK仅提供的是高性能处理报文的框架平台和库函数,而不是对报文的具体业务处理。对通信设备的项目工程实现来说,性能调优是一件具体而艰巨的任务,数据平面随便一行代码的处理失当,就可能会引起性能狂降,因此,应用好DPDK并不是一件轻松的事,深入理解DPDK加速技术的精髓是前提条件。

2.2 DPDK总体框架

        如上图所示dpkd总体框架图,底层通过UIO技术来实现用户态和内核态的数据包交互,然后将数据包的控制器交给应用层的应用程序处理,dpdk只是一个框架它提供各种功能库比如:MEMORY、RING、MBUF、PDM、MEMPOOL,这些接下来会分析。

        dpkd主要有以下核心技术:

2.2.1 UIO (用户空间的 I/O 技术)

        dpdk 能够绕过内核协议栈,本质上是得益于 UIO 技术,通过 UIO 能够拦截中断,并重设中断回调行为,从而绕过内核协议栈后续的处理流程。

        UIO 设备的实现机制其实是对用户空间暴露文件接口,比如当注册一个 UIO 设备 uioX,就会出现文件 /dev/uioX,对该文件的读写就是对设备内存的读写。除此之外,对设备的控制还可以通过 /sys/class/uio 下的各个文件的读写来完成。

2.2.2 内存池技术

        dpdk 在用户空间实现了一套精巧的内存池技术,内核空间和用户空间的内存交互不进行拷贝,只做控制权转移。这样,当收发数据包时,就减少了内存拷贝的开销。

2.2.3 大页内存管理

        Linux系统的内存管理依赖于存储器上,如下所示 Linux在内存管理中采用受保护的虚拟地址模式,在代码中地址分为3类:逻辑地址、线性地址、物理地址。程序使用具体内存简单说就是逻辑地址通过分段机制映射转化为线性地址,然后线性地址通过分页机制映射转化为物理地址的过程,而在实际使用中,仅将线性地址映射为物理地址的过程中,需要从内存中读取至少四次页目录表(Page Directory)和页表 (Page Table),为了加快内核读取速度,CPU在硬件上对页表做了缓存,就是TLB。 线性地址先从TLB获取高速缓存内存,如果不存在就从内存表获取,如果有直接的映射,直接从内存读取,没有则产生缺页中断,从新分配物理内存,或者从硬盘上将swap读取。具体图示如下:

        普通页大小是每个4K,如果是4K页的寻址如下,使用物理内存时需要多级查找才能找到对应的内存。

        4K的页表是linux针对一般情况得出的合适大小,然而对于特殊应用可以通过扩大页表面积提高内存使用效率。

        dpdk使用hupage的思想就是让程序尽量独占内存防止内存换出,扩大页表提高hash命中率,通过hugage技术扩大了该使用的页表大小,设定为更适合高频内存使用程序的状态,获得了以下几点优势:

  1. 无需交换,也就是不存在页面由于内存空间不足而存在换入换出的问题;
  2. 减少TLB(Translation Lookaside Buffer,即快表) Miss;
  3. 降低page table查询负载;

2.2.4 无锁环形队列

        dpdk 基于 Linux 内核的无锁环形缓冲 kfifo 实现了自己的一套无锁机制。支持单生产者入列/单消费者出列和多生产者入列/多消费者出列操作,在数据传输的时候,降低性能的同时还能保证数据的同步。

2.2.5 poll-mode网卡驱动

        DPDK网卡驱动完全抛弃中断模式,基于轮询方式收包,避免了中断开销。

2.2.6 NUMA

        为了解决单核带来的CPU性能不足,出现了SMP,但传统的SMP系统中,所有处理器共享系统总线,当处理器数目越来越多时,系统总线竞争加大,系统总线称为新的瓶颈。NUMA(非统一内存访问)技术解决了SMP系统可扩展性问题,已成为当今高性能服务器的主流体系结构之一。

        NUMA系统节点一般是由一组CPU和本地内存组成。NUMA调度器负责将进程在同一节点的CPU间调度,除非负载太高,才迁移到其它节点,但这会导致数据访问延时增大。下图是2颗CPU支持NUMA架构的示意图,每颗CPU物理上有4个核心。

        dpdk 内存分配上通过 proc 提供的内存信息,使 CPU 核心尽量使用靠近其所在节点的内存,避免了跨 NUMA 节点远程访问内存的性能问题。

 2.2.7 CPU 亲和性管理

        Affinity是进程的一个属性,这个属性指明了进程调度器能够把这个进程调度到哪些CPU上。在Linux中,我们可以利用CPU affinity 把一个或多个进程绑定到一个或多个CPU上。CPU Affinity分为2种,soft affinity和hard affinity。soft affinity仅是一个建议,如果不可避免,调度器还是会把进程调度到其它的CPU上。hard affinity是调度器必须遵守的规则。为什么需要CPU绑定?

  • 增加CPU缓存的命中率 CPU之间是不共享缓存的,如果进程频繁的在各个CPU间进行切换,需要不断的使旧CPU的cache失效。如果进程只在某个CPU上执行,则不会出现失效的情况。在多个线程操作的是相同的数据的情况下,如果把这些线程调度到一个处理器上,大大的增加了CPU缓存的命中率。但是可能会导致并发性能的降低。如果这些线程是串行的,则没有这个影响。
  • 适合time-sensitive应用在real-time或time-sensitive应用中,我们可以把系统进程绑定到某些CPU上,把应用进程绑定到剩余的CPU上。典型的设置是,把应用绑定到某个CPU上,把其它所有的进程绑定到其它的CPU上。

        dpdk 利用 CPU 的亲和性将一个线程或多个线程绑定到一个或多个 CPU 上,这样在线程执行过程中,就不会被随意调度,一方面减少了线程间的频繁切换带来的开销,另一方面避免了 CPU 缓存的局部失效性,增加了 CPU 缓存的命中率。

        DPDK将网卡接收队列分配给某个CPU核,该队列收到的报文都交给该核上的DPDK线程处理。存在两种方式将数据包发送到接收队列之上:

  • RSS(Receive Side Scaling,接收方扩展)机制:根据关键字,比如根据UDP的四元组进行哈希。
  • Flow Director机制:可设定根据数据包某些信息进行精确匹配,分配到指定的队列与CPU核。

线程绑定CPU

        当网络数据包(帧)被网卡接收后,DPDK网卡驱动将其存储在一个高效缓冲区中,并在MBUF缓存中创建MBUF对象与实际网络包相连,对网络包的分析和处理都会基于该MBUF,必要的时候才会访问缓冲区中的实际网络包。

2.2.8 多核调度框架

        dpdk启动时会分析系统的逻辑核属性,并建立映射表进行统一管理,每个核属性包括逻辑核id、硬核id、numa节点id。dpdk会根据系统默认状态生成一一绑定的映射表,用户可以根据需求更改映射表,后续dpdk框架会根据该映射表进行核绑定。

class core{

    lcore_id; //逻辑核id

    core_id; //硬核id

    socket_id; //NUMA节点id

}

class core coremap //所有逻辑核的映射表

        dpdk设计的多核架构一般会有主从核之分,主核负责完成各个模块的初始化,从核负责具体的业务处理:

  • 服务器启动时选取一个逻辑核做主核;
  • 然后启动其他核做从核;
  • 所有线程都根据映射表做核绑定;
  • 控制核主要完成pci、内存、日志等系统的初始化;
  • 从核启动后等待主核初始化完毕后挂载业务处理入口;
  • 从核运行业务代码;

2.2.9 环境抽象层设计

        DPDK的创造的环境抽象层(EAL, Environment Abstraction Layer)主要负责对计算机底层资源(如硬件和内存空间)的访问,并对提供给用户的接口实施了实现细节的封装。其初始化例程决定了如何分配这些资源(PCI设备、计时器、控制台等)。

        EAL提供的典型服务包括:

  • 加载和启动DPDK:DPDK及其应用程序会被链接为单一应用,因此需要通过某种方式进行加载DPDK。
  • 核关联/分配过程:EAL提供了将执行单元分配给特定核并创建执行实例的机制。
  • 预留系统内存:EAL为预留不同的内存区域提供便利,例如用于设备交互的物理内存区域。
  • 抽象PCI地址:EAL提供了访问PCI地址空间的接口。
  • 跟踪和调试功能:日志、堆栈转储等。
  • 实用的功能:libc中所没有提供的自旋锁和原子计数器。
  • CPU功能识别:在运行时确定CPU是否支持特定功能,确定当前CPU是否支持编译产生的二进制指令集。
  • 中断处理:向特定中断源注册/注销回调的接口。
  • 时钟功能:用于设置/删除在特定时间运行的回调函数接口。

2.2.10 PMD轮询模式驱动

        DPDK包括1Gb,10Gb,40Gb和半虚拟化抽象层的轮询模式驱动(PMD, Poll Mode Driver)。PMD由用户空间的特定的驱动程序提供的API组成,用于对设备和它们相应的队列进行设置。抛弃了基于中断的异步信号发送机制为该架构带来很大的开销节省。避免中断性能瓶颈是DPDK提升数据包处理速度的关键之一。

        DPDK环境为数据包处理应用考虑了两种模型:运行至完成(run-to-completion)模型和管道(pipeline)模型。在运行至完成模型中,一个API向某个特定端口的接收描述符环轮询以接收数据包。接着这个数据包在同一个核上被处理,之后被一个发送用API放到端口的传输描述符环上;在管道模型中,一个核心会通过API对一个或多个端口的接收描述符环进行轮询,数据包通过环被接收和传递给另一个核心,然后在这个核心上被处理,之后可能被发送用API放到端口的传输描述符环上。

        运行至完成是一个同步模型,每个指派给DPDK的逻辑核心执行如下所示的循环:

  1. 通过PMD接收用API来提取输出数据包
  2. 根据转发,一一处理收到的数据包
  3. 通过PMD发送用API发送输出数据包

        相反,管道模型是一个异步模型,有的逻辑核心只执行数据包提取,而有的只执行处理,收到的数据包在这些逻辑核心之间通过环来传递。提取核心执行如下的循环:

  1. 通过PMD接收用API来提取输出数据包
  2. 通过队列提供数据包给处理核心

        处理核心执行如下的循环:

  1. 从队列中提取数据包
  2. 根据重传(如果被转发)处理数据包

        为了避免不必要的中断性能瓶颈,执行环境禁止任何异步通知机制的使用。在任何需要或合适的时候,异步通信都应尽可能采用环的方式。

        在多核环境中避免锁竞争是一个重要的问题。为了处理这个问题,PMD被设计为可以尽可能地在单核私有资源下工作。例如,PMD为每个核心每个端口提供一个单独的队列。同样的,每个端口的接收队列只会被指派给唯一一个逻辑核心并接收它的轮询。

        网络空闲时CPU长期空转,会带来能耗问题。所以,DPDK推出Interrupt DPDK模式。

Interrupt DPDK:

        图片引自David Su/Yunhong Jiang/Wei Wang的文档《Towards Low Latency Interrupt Mode DPDK》

        它的原理和NAPI很像,就是没包可处理时进入睡眠,改为中断通知。并且可以和其他进程共享同个CPU Core,但是DPDK进程会有更高调度优先级。

2.2.11 其他技术突破点

        除了上述之外,dpdk 还有很多的技术突破,可以用下面这张图来概之。

3 dpdk核心组件

        核心组件指一系列的库,用于为高性能包处理程序提供所有必须的元素。核心组件及其之间的关系如下图所示:

dpdk核心组件示意图

(1)环形缓冲区管理(librte_ring)

        Ring数据结构提供了一个无锁的多生产者,多消费者的FIFO表处理接口。 他比无锁队列优异的地方在于它容易部署,适合大量的操作,而且更快。 Ring库在 Memory Pool Manager (librte_mempool)中使用到, 而且ring还用于不同核之间或是逻辑核上处理单元之间的通信。

(2)内存池管理(librte_mempool)

        内存池管理的主要职责就是在内存中分配指定数目对象的POOL。 每个POOL以名称来唯一标识,并且使用一个ring来存储空闲的对象节点。 它还提供了一些其他的服务如对象节点的每核备份缓存及自动对齐以保证元素能均衡的处于每核内存通道上。

(3) 网络报文缓冲区管理(librte_mbuf)

        报文缓存管理器提供了创建、释放报文缓存的能力,DPDK应用程序中可能使用这些报文缓存来存储消息。 而消息通常在程序开始时通过DPDK的MEMPOOL库创建并存储。 BUFF库提供了报文申请释放的API,通常消息buff用于缓存普通消息,报文buff用于缓存网络报文。

(4)定时器管理(librte_timer)

        这个库位DPDK执行单元提供了定时服务,为函数异步执行提供支持。 定时器可以设置周期调用或只调用一次。 使用EAL提供的接口获取高精度时钟,并且能在每个核上根据需要初始化。

(5)以太网轮询驱动架构

        DPDK的PMD驱动支持1G、10G、40G。 同时DPDK提供了虚拟的以太网控制器,被设计成非异步,基于中断的模式。

(6)报文转发算法支持

        DPDK提供了哈希(librte_hash)、最长前缀匹配的(librte_lpm)算法库用于支持包转发。

(7)网络协议库(librte_net)

        这个库提供了IP协议的一些定义,以及一些常用的宏。 这些定义都基于FreeBSD IP协议栈的代码,并且包含相关的协议号,IP相关宏定义,IPV4和IPV6头部结构等等。

参考链接

DPDK_百度百科

DPDK技术简介 - 简书

DPDK 全面分析 - bakari - 博客园

DPDK技术的原理是怎样的,它的作用是什么 - 今日头条 - 电子发烧友网

DPDK框架核心介绍 - 知乎

DPDK解析 - 知乎

DPDK加速技术深度剖析(一)—— 综述篇 | SDNLAB | 专注网络创新技术

DPDK 分析,原理以及学习路线 - 知乎

阿里云用DPDK如何解决千万级流量并发_哔哩哔哩_bilibili

阿里云用到的DPDK(分析原理)以及学习路线 - 知乎

DPDK系列之十二:基于virtio、vhost和OVS-DPDK的容器数据通道_cloudvtech的博客-CSDN博客_dpdk 容器

DPDK系列之六:qemu-kvm网络后端的加速技术_cloudvtech的博客-CSDN博客_kvm加速

DPDK系列之十五:Virtio技术分析之一,virtio基础架构_cloudvtech的博客-CSDN博客_virtio

从dpdk1811看virtio1.1 的实现—packed ring-lvyilong316-ChinaUnix博客

qemu-kvm中的virtio浅析 - 骑着蜗牛追太阳 - 博客园

Qemu模拟IO和半虚拟化Virtio的区别以及I/O半虚拟化驱动介绍_weixin_34051201的博客-CSDN博客

virtio blk原理 - 简书

DPDK系列之十一:容器云的数据通道加速方案概览_cloudvtech的博客-CSDN博客

DPDK系列之十二:基于virtio、vhost和OVS-DPDK的容器数据通道_cloudvtech的博客-CSDN博客_dpdk 容器

 《重识云原生系列》专题索引: 

  1. 第一章——不谋全局不足以谋一域
  2. 第二章计算第1节——计算虚拟化技术总述
  3. 第三章云存储第1节——分布式云存储总述
  4. 第四章云网络第一节——云网络技术发展简述
  5. 第四章云网络4.2节——相关基础知识准备
  6. 第四章云网络4.3节——重要网络协议
  7. 第四章云网络4.3.1节——路由技术简述
  8. 第四章云网络4.3.2节——VLAN技术
  9. 第四章云网络4.3.3节——RIP协议
  10. 第四章云网络4.3.4节——OSPF协议
  11. 第四章云网络4.3.5节——EIGRP协议
  12. 第四章云网络4.3.6节——IS-IS协议
  13. 第四章云网络4.3.7节——BGP协议
  14. 第四章云网络4.3.7.2节——BGP协议概述
  15. 第四章云网络4.3.7.3节——BGP协议实现原理
  16. 第四章云网络4.3.7.4节——高级特性
  17. 第四章云网络4.3.7.5节——实操
  18. 第四章云网络4.3.7.6节——MP-BGP协议
  19. 第四章云网络4.3.8节——策略路由
  20. 第四章云网络4.3.9节——Graceful Restart(平滑重启)技术
  21. 第四章云网络4.3.10节——VXLAN技术
  22. 第四章云网络4.3.10.2节——VXLAN Overlay网络方案设计
  23. 第四章云网络4.3.10.3节——VXLAN隧道机制
  24. 第四章云网络4.3.10.4节——VXLAN报文转发过程
  25. 第四章云网络4.3.10.5节——VXlan组网架构
  26. 第四章云网络4.3.10.6节——VXLAN应用部署方案
  27. 第四章云网络4.4节——Spine-Leaf网络架构
  28. 第四章云网络4.5节——大二层网络
  29. 第四章云网络4.6节——Underlay 和 Overlay概念
  30. 第四章云网络4.7.1节——网络虚拟化与卸载加速技术的演进简述
  31. 第四章云网络4.7.2节——virtio网络半虚拟化简介
  32. 第四章云网络4.7.3节——Vhost-net方案
  33. 第四章云网络4.7.4节vhost-user方案——virtio的DPDK卸载方案
  34. 第四章云网络4.7.5节vDPA方案——virtio的半硬件虚拟化实现
  35. 第四章云网络4.7.6节——virtio-blk存储虚拟化方案
  36. 第四章云网络4.7.8节——SR-IOV方案
  37. 第四章云网络4.7.9节——NFV
  38. 第四章云网络4.8.1节——SDN总述
  39. 第四章云网络4.8.2.1节——OpenFlow概述
  40. 第四章云网络4.8.2.2节——OpenFlow协议详解
  41. 第四章云网络4.8.2.3节——OpenFlow运行机制
  42. 第四章云网络4.8.3.1节——Open vSwitch简介
  43. 第四章云网络4.8.3.2节——Open vSwitch工作原理详解
  44. 第四章云网络4.8.4节——OpenStack与SDN的集成
  45. 第四章云网络4.8.5节——OpenDayLight
  46. 第四章云网络4.8.6节——Dragonflow
  47. 第四章云网络4.9.1节——网络卸载加速技术综述
  48. 第四章云网络4.9.2节——传统网络卸载技术
  49. 第四章云网络4.9.3.1节——DPDK技术综述
  50. 第四章云网络4.9.3.2节——DPDK原理详解
  51. 第四章云网络4.9.4.1节——智能网卡SmartNIC方案综述
  52. 第四章云网络4.9.4.2节——智能网卡实现

0 人点赞