相对传统的基于内核的网络数据处理,dpdk 对从内核层到用户层的网络数据流程进行了重大突破,我们先看看传统的数据流程和 dpdk 中的网络流程有什么不同。
传统 Linux 内核网络数据流程:
dpdk 网络数据流程:
DPDK拦截中断,不触发后续中断流程,并绕过协议栈,通过UIO技术将网卡收到的报文拷贝到应用层处理,报文不再经过内核协议栈。减少了中断,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中的具体使用技巧:
在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加速技术的精髓是前提条件。
如上图所示dpkd总体框架图,底层通过UIO技术来实现用户态和内核态的数据包交互,然后将数据包的控制器交给应用层的应用程序处理,dpdk只是一个框架它提供各种功能库比如:MEMORY、RING、MBUF、PDM、MEMPOOL,这些接下来会分析。
dpkd主要有以下核心技术:
dpdk 能够绕过内核协议栈,本质上是得益于 UIO 技术,通过 UIO 能够拦截中断,并重设中断回调行为,从而绕过内核协议栈后续的处理流程。
UIO 设备的实现机制其实是对用户空间暴露文件接口,比如当注册一个 UIO 设备 uioX,就会出现文件 /dev/uioX,对该文件的读写就是对设备内存的读写。除此之外,对设备的控制还可以通过 /sys/class/uio 下的各个文件的读写来完成。
dpdk 在用户空间实现了一套精巧的内存池技术,内核空间和用户空间的内存交互不进行拷贝,只做控制权转移。这样,当收发数据包时,就减少了内存拷贝的开销。
Linux系统的内存管理依赖于存储器上,如下所示 Linux在内存管理中采用受保护的虚拟地址模式,在代码中地址分为3类:逻辑地址、线性地址、物理地址。程序使用具体内存简单说就是逻辑地址通过分段机制映射转化为线性地址,然后线性地址通过分页机制映射转化为物理地址的过程,而在实际使用中,仅将线性地址映射为物理地址的过程中,需要从内存中读取至少四次页目录表(Page Directory)和页表 (Page Table),为了加快内核读取速度,CPU在硬件上对页表做了缓存,就是TLB。 线性地址先从TLB获取高速缓存内存,如果不存在就从内存表获取,如果有直接的映射,直接从内存读取,没有则产生缺页中断,从新分配物理内存,或者从硬盘上将swap读取。具体图示如下:
普通页大小是每个4K,如果是4K页的寻址如下,使用物理内存时需要多级查找才能找到对应的内存。
4K的页表是linux针对一般情况得出的合适大小,然而对于特殊应用可以通过扩大页表面积提高内存使用效率。
dpdk使用hupage的思想就是让程序尽量独占内存防止内存换出,扩大页表提高hash命中率,通过hugage技术扩大了该使用的页表大小,设定为更适合高频内存使用程序的状态,获得了以下几点优势:
dpdk 基于 Linux 内核的无锁环形缓冲 kfifo 实现了自己的一套无锁机制。支持单生产者入列/单消费者出列和多生产者入列/多消费者出列操作,在数据传输的时候,降低性能的同时还能保证数据的同步。
DPDK网卡驱动完全抛弃中断模式,基于轮询方式收包,避免了中断开销。
为了解决单核带来的CPU性能不足,出现了SMP,但传统的SMP系统中,所有处理器共享系统总线,当处理器数目越来越多时,系统总线竞争加大,系统总线称为新的瓶颈。NUMA(非统一内存访问)技术解决了SMP系统可扩展性问题,已成为当今高性能服务器的主流体系结构之一。
NUMA系统节点一般是由一组CPU和本地内存组成。NUMA调度器负责将进程在同一节点的CPU间调度,除非负载太高,才迁移到其它节点,但这会导致数据访问延时增大。下图是2颗CPU支持NUMA架构的示意图,每颗CPU物理上有4个核心。
dpdk 内存分配上通过 proc 提供的内存信息,使 CPU 核心尽量使用靠近其所在节点的内存,避免了跨 NUMA 节点远程访问内存的性能问题。
Affinity是进程的一个属性,这个属性指明了进程调度器能够把这个进程调度到哪些CPU上。在Linux中,我们可以利用CPU affinity 把一个或多个进程绑定到一个或多个CPU上。CPU Affinity分为2种,soft affinity和hard affinity。soft affinity仅是一个建议,如果不可避免,调度器还是会把进程调度到其它的CPU上。hard affinity是调度器必须遵守的规则。为什么需要CPU绑定?
dpdk 利用 CPU 的亲和性将一个线程或多个线程绑定到一个或多个 CPU 上,这样在线程执行过程中,就不会被随意调度,一方面减少了线程间的频繁切换带来的开销,另一方面避免了 CPU 缓存的局部失效性,增加了 CPU 缓存的命中率。
DPDK将网卡接收队列分配给某个CPU核,该队列收到的报文都交给该核上的DPDK线程处理。存在两种方式将数据包发送到接收队列之上:
线程绑定CPU
当网络数据包(帧)被网卡接收后,DPDK网卡驱动将其存储在一个高效缓冲区中,并在MBUF缓存中创建MBUF对象与实际网络包相连,对网络包的分析和处理都会基于该MBUF,必要的时候才会访问缓冲区中的实际网络包。
dpdk启动时会分析系统的逻辑核属性,并建立映射表进行统一管理,每个核属性包括逻辑核id、硬核id、numa节点id。dpdk会根据系统默认状态生成一一绑定的映射表,用户可以根据需求更改映射表,后续dpdk框架会根据该映射表进行核绑定。
class core{
lcore_id; //逻辑核id
core_id; //硬核id
socket_id; //NUMA节点id
}
class core coremap[ ] //所有逻辑核的映射表
dpdk设计的多核架构一般会有主从核之分,主核负责完成各个模块的初始化,从核负责具体的业务处理:
DPDK的创造的环境抽象层(EAL, Environment Abstraction Layer)主要负责对计算机底层资源(如硬件和内存空间)的访问,并对提供给用户的接口实施了实现细节的封装。其初始化例程决定了如何分配这些资源(PCI设备、计时器、控制台等)。
EAL提供的典型服务包括:
DPDK包括1Gb,10Gb,40Gb和半虚拟化抽象层的轮询模式驱动(PMD, Poll Mode Driver)。PMD由用户空间的特定的驱动程序提供的API组成,用于对设备和它们相应的队列进行设置。抛弃了基于中断的异步信号发送机制为该架构带来很大的开销节省。避免中断性能瓶颈是DPDK提升数据包处理速度的关键之一。
DPDK环境为数据包处理应用考虑了两种模型:运行至完成(run-to-completion)模型和管道(pipeline)模型。在运行至完成模型中,一个API向某个特定端口的接收描述符环轮询以接收数据包。接着这个数据包在同一个核上被处理,之后被一个发送用API放到端口的传输描述符环上;在管道模型中,一个核心会通过API对一个或多个端口的接收描述符环进行轮询,数据包通过环被接收和传递给另一个核心,然后在这个核心上被处理,之后可能被发送用API放到端口的传输描述符环上。
运行至完成是一个同步模型,每个指派给DPDK的逻辑核心执行如下所示的循环:
相反,管道模型是一个异步模型,有的逻辑核心只执行数据包提取,而有的只执行处理,收到的数据包在这些逻辑核心之间通过环来传递。提取核心执行如下的循环:
处理核心执行如下的循环:
为了避免不必要的中断性能瓶颈,执行环境禁止任何异步通知机制的使用。在任何需要或合适的时候,异步通信都应尽可能采用环的方式。
在多核环境中避免锁竞争是一个重要的问题。为了处理这个问题,PMD被设计为可以尽可能地在单核私有资源下工作。例如,PMD为每个核心每个端口提供一个单独的队列。同样的,每个端口的接收队列只会被指派给唯一一个逻辑核心并接收它的轮询。
网络空闲时CPU长期空转,会带来能耗问题。所以,DPDK推出Interrupt DPDK模式。
Interrupt DPDK:
图片引自David Su/Yunhong Jiang/Wei Wang的文档《Towards Low Latency Interrupt Mode DPDK》
它的原理和NAPI很像,就是没包可处理时进入睡眠,改为中断通知。并且可以和其他进程共享同个CPU Core,但是DPDK进程会有更高调度优先级。
除了上述之外,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 容器
《重识云原生系列》专题索引:
第四章云网络4.9.1节——网络卸载加速技术综述
第四章云网络4.9.2节——传统网络卸载技术
第四章云网络4.9.3.1节——DPDK技术综述
第四章云网络4.9.3.2节——DPDK原理详解
第四章云网络4.9.4.1节——智能网卡SmartNIC方案综述
第四章云网络4.9.4.2节——智能网卡实现