DPDK 入门

DPDK旁路原理

img

原来内核协议栈的方式数据是从 网卡–>驱动–>协议栈–>socket接口–>业务

而DPDK的方式是基于UIO(Userspace I/O)旁路数据。数据从 网卡–>DPDK轮询模式–>DPDK基础库–>业务

DPDK的基石UIO

为了让驱动运行在用户态,linux提供UIO机制,使用UIO可以通过read感知中断,通过mmap实现核网卡的通讯

UIO原理

img

要开发用户态驱动有几个步骤:

  1. 开发运行在内核的UIO模块,因为硬中断只能在内核处理
  2. 通过/dev/uioX读取中断
  3. 通过mmap和外设共享内存

DKDP核心优化

DPDK的UIO驱动屏蔽了硬件发出的中断,然后在用户态采用主动轮训的方式,这种模式被称为PMD(Poll Mode Driver)

UIO 旁路了内核,主动轮训去掉硬中断,DPDK从而可以在用户态做收发包处理。带来zero copy,无系统调用的好处,同步处理减少上下文切换带来的Cache MIss。

运行在PMD的CORE会处于CPU100%的状态。

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

interrupt DPDK:

img

DPDK的高性能代码实现

1. 采用HugePage减少TLB Miss

默认下Linux采用4KB为一页,页越小内存越大,页表的开销越大,页表的内存占用也越大。CPU有TLB(Translation Lookaside Buffer)成本高所以一般就只能存放几百到上千个页表项。如果进程要使用64G内存,则64G/4KB=16000000(一千六百万)页,每页在页表项中占用16000000 * 4B=62MB。如果用HugePage采用2MB作为一页,只需64G/2MB=2000,数量不在同个级别。

而DPDK采用HugePage,在x86-64下支持2MB、1GB的页大小,几何级的降低了页表项的大小,从而减少TLB-Miss。并提供了内存池(Mempool)、MBuf、无锁环(Ring)、Bitmap等基础库。根据我们的实践,在数据平面(Data Plane)频繁的内存分配释放,必须使用内存池,不能直接使用rte_malloc,DPDK的内存分配实现非常简陋,不如ptmalloc。

2. SNA(Shared-nothing Architecture)

软件架构去中心化,尽量避免全局共享,带来全局竞争,失去横向扩展的能力。NUMA体系下不跨Node远程使用内存。

3. SIMD(Single Instruction Multiple Data)

从最早的mmx/sse到最新的avx2,SIMD的能力一直在增强。DPDK采用批量同时处理多个包,再用向量编程,一个周期内对所有包进行处理。比如,memcpy就使用SIMD来提高速度。

SIMD在游戏后台比较常见,但是其他业务如果有类似批量处理的场景,要提高性能,也可看看能否满足。

4. 不使用慢速API

这里需要重新定义一下慢速API,比如说gettimeofday,虽然在64位下通过vDSO已经不需要陷入内核态,只是一个纯内存访问,每秒也能达到几千万的级别。但是,不要忘记了我们在10GE下,每秒的处理能力就要达到几千万。所以即使是gettimeofday也属于慢速API。DPDK提供Cycles接口,例如rte_get_tsc_cycles接口,基于HPET或TSC实现。

dpdk和linux内核中断区别,DPDK解析—–DPDK,PF_RING对比

https://blog.csdn.net/weixin_28865923/article/details/116609012

2.2 DPDK UIO机制

采用Linux提供UIO机制,可以旁路Kernel,将所有报文处理的工作在用户空间完成。

9b669f7c97ce

图片引自参考文档[1]

如上图,左边是传统的数据包获取方式,路径为:

网卡 -> Kernel驱动 -> Kernel TCP/IP协议栈 -> Socket接口 -> 业务

右边是DPDK的方式,基于UIO。数据路径为:

网卡 -> DPDK轮询模式-> DPDK基础库 -> 业务

2.3 DPDK 零拷贝(Zero Copy)

通过UIO+mmap 实现数据零拷贝(zero copy)是DPDK的特点之一。那么常规的Linux Kernel(libpcap)以及pf-ring处理数据包的流程要进行几次数据拷贝呢?

libpcap

libpcap采用的是传统的数据包获取方式,如上图左边路径。

网卡接收到数据包后,第一步需要把数据从网卡拷贝到主存(RX buffer)中。但是在这个拷贝的过程中使用了DMA(Direct Memory Access)技术。DMA 传输将数据从一个地址空间复制到另外一个地址空间。由CPU 初始化这个传输动作,传输动作本身是由 DMA 控制器来实行和完成,CPU是不需要参与,也不会产生CPU资源消耗。因此这个第一步的拷贝可以忽略。

第二步,Kernel会将网卡通过DMA传输进入主存(RX buffer)的内容拷贝一份进行处理,这里进行了一次数据拷贝。

第三步,由于内核空间和用户空间的内存是不共享的,Kernel会将数据包内容再进行一次拷贝用于用户空间使用。

综上,传统的数据包捕获方式(libpcap)会进行两次数据包拷贝。

pf-rfing

pf-ring采用mmap()将传统的两次拷贝减少至一次拷贝。

第一步,同样使用DMA技术,把数据从网卡拷贝到主存(RX buffer)中。

第二步,pf-ring会将网卡通过DMA传输进入主存(RX buffer)的内容拷贝一份放入环形缓冲区中(ring)中,这里进行了一次数据拷贝。

第三步,使用mmap()将环形缓冲区映射至用户空间,用户空间可以直接访问这个环形缓冲区中的数据。

pf-ring zc

pf-ring zc(zero copy)更是将pf-ring的一次拷贝也省去,达到了零拷贝的目的,具体的:

第一步,使用DMA技术,把数据从网卡拷贝到主存(RX buffer)中。

第二步,使用mmap()直接将RX buffer的数据映射到用户用户空间,使用户空间可以直接访问RX buffer的数据。

下图非常清晰地展示了pf-ring zc实现零拷贝的过程:

9b669f7c97ce

图片引自参考文档[3]

DPDK

DPDK实现零拷贝的方式与pf-ring zc类似,首先通过DMA将网卡数据拷贝至主存(RX buffer);随后使用mmap()直接将RX buffer的数据映射到用户用户空间,使用户空间可以直接访问RX buffer的数据,以此实现了零拷贝。

由上述可以看出,pf-ring zc和dpdk均可以实现数据包的零拷贝,两者均旁路了内核,但是实现原理略有不同。pf-ring zc通过zc驱动(也在应用层)接管数据包,dpdk基于UIO实现。

值得注意的是,无论是pf-ring zc还是dpdk,旁路内核就意味着应用程序完全接管网卡,这样一来,当通过UIO驱动或ZC驱动打开设备时,设备将无法用于标准网络。

3.PMD

DPDK的UIO驱动屏蔽了硬件发出中断,然后在用户态采用主动轮询的方式,这种模式被称为PMD(Poll Mode Driver)。

正常情况下,网卡收到数据包后,会发出中断信号,CPU接收到中断信号后,会停止正在运行的程序,保护断点地址和处理机当前状态,转入相应的中断服务程序并执行中断服务程序,完成后,继续执行中断前的程序。

DPDK采用轮询的方式,直接访问RX和TX描述符而没有任何中断,以便在用户的应用程序中快速接收,处理和传送数据包。这样可以减少CPU频繁中断,切换上下文带来的消耗。

在网卡大流量时,DPDK这种PMD的方式会带来较大的性能提升;但是在流量小的时候,轮询会导致CPU空转(不断轮询),从而导致CPU性能浪费和功耗问题。

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

与DPDK相比,pf-ring(no zc)使用的是NAPI polling和应用层polling,而pf-ring zc与DPDK类似,仅使用应用层polling。


DPDK 入门
https://dnsnat.gitee.io/NETWORK/DPDK 入门.html
作者
dnsnat
发布于
2022年5月16日
许可协议