快捷搜索:  汽车  科技

dpdk 常见工具(架构之美DPDK技术介绍)

dpdk 常见工具(架构之美DPDK技术介绍)右边是DPDK的方式,基于UIO(Userspace I/O)旁路数据。数据从 网卡 -> DPDK轮询模式-> DPDK基础库 -> 业务左边是原来的方式数据从 网卡 -> 驱动 -> 协议栈 -> Socket接口 -> 业务那么,我们来看看发展了十几年的DPDK,从Intel主导开发,到华为、思科、AWS等大厂商的加入,核心玩家都在该圈子里,拥有完善的社区,生态形成闭环。早期,主要是传统电信领域3层以下的应用,如华为、中国电信、中国移动都是其早期使用者,交换机、路由器、网关是主要应用场景。但是,随着上层业务的需求以及DPDK的完善,在更高的应用也在逐步出现。DPDK旁路原理:图片引自Jingjing Wu的文档《Flow Bifurcation on Intel® Ethernet Controller X710/XL710》

DPDK是Intel公司开发的一款高性能的网络驱动组件,旨在为数据面应用程序提供一个简单方便的,完整的,快速的数据包处理解决方案,主要技术有用户态、轮询取代中断、零拷贝、网卡RSS、访存DirectIO等。

一、网络IO的处境和趋势

从我们用户的使用就可以感受到网速一直在提升,而网络技术的发展也从1GE/10GE/25GE/40GE/100GE的演变,从中可以得出单机的网络IO能力必须跟上时代的发展。

  1. 传统的电信领域
    IP层及以下,例如路由器、交换机、防火墙、基站等设备都是采用硬件解决方案。基于专用网络处理器(NP),有基于FPGA,更有基于ASIC的。但是基于硬件的劣势非常明显,发生Bug不易修复,不易调试维护,并且网络技术一直在发展,例如2G/3G/4G/5G等移动技术的革新,这些属于业务的逻辑基于硬件实现太痛苦,不能快速迭代。传统领域面临的挑战是急需一套软件架构的高性能网络IO开发框架。
  2. 云的发展
    私有云的出现通过网络功能虚拟化(NFV)共享硬件成为趋势,NFV的定义是通过标准的服务器、标准交换机实现各种传统的或新的网络功能。急需一套基于常用系统和标准服务器的高性能网络IO开发框架。
  3. 单机性能的飙升
    网卡从1G到100G的发展,cpu从单核到多核到多CPU的发展,服务器的单机能力通过横行扩展达到新的高点。但是软件开发却无法跟上节奏,单机处理能力没能和硬件门当户对,如何开发出与时并进高吞吐量的服务,单机百万千万并发能力。即使有业务对QPS要求不高,主要是CPU密集型,但是现在大数据分析、人工智能等应用都需要在分布式服务器之间传输大量数据完成作业。这点应该是我们互联网后台开发最应关注,也最关联的。
二、linux x86网络IO瓶颈

在数年前曾经写过《网卡工作原理及高并发下的调优》一文,描述了Linux的收发报文流程。根据经验,在C1(8核)上跑应用每1W包处理需要消耗1%软中断CPU,这意味着单机的上限是100万PPS(Packet Per Second)。从TGW(netfilter版)的性能100万PPS,AliLVS优化了也只到150万PPS,并且他们使用的服务器的配置还是比较好的。假设,我们要跑满10GE网卡,每个包64字节,这就需要2000万PPS(注:以太网万兆网卡速度上限是1488万PPS,因为最小帧大小为84B《Bandwidth Packets Per Second and Other Network Performance Metrics》),100G是2亿PPS,即每个包的处理耗时不能超过50纳秒。而一次Cache Miss,不管是TLB、数据cache、指令Cache发生Miss,回内存读取大约65纳秒,NUMA体系下跨Node通讯大约40纳秒。所以,即使不加上业务逻辑,即使纯收发包都如此艰难。我们要控制Cache的命中率,我们要了解计算机体系结构,不能发生跨Node通讯。
从这些数据,我希望可以直接感受一下这里的挑战有多大,理想和现实,我们需要从中平衡。问题都有这些

  • 传统的收发报文方式都必须采用硬中断来做通讯,每次硬中断大约消耗100微秒,这还不算因为终止上下文所带来的Cache Miss。
  • 数据必须从内核态用户态之间切换拷贝带来大量CPU消耗,全局锁竞争。
  • 收发包都有系统调用的开销。
  • 内核工作在多核上,为可全局一致,即使采用Lock Free,也避免不了锁总线、内存屏障带来的性能损耗。
  • 从网卡到业务进程,经过的路径太长,有些其实未必要的,例如netfilter框架,这些都带来一定的消耗,而且容易Cache Miss。
三、DPDK的基本原理

从前面的分析可以得知IO实现的方式、内核的瓶颈,以及数据流过内核存在不可控因素,这些都是在内核中实现,内核是导致瓶颈的原因所在,要解决问题需要绕过内核。所以主流解决方案都是旁路网卡IO,绕过内核直接在用户态收发包来解决内核的瓶颈。
Linux社区也提供了旁路机制Netmap,官方数据10G网卡1400万PPS,但是Netmap没广泛使用。其原因有几个:

  • Netmap需要驱动的支持,即需要网卡厂商认可这个方案。
  • Netmap仍然依赖中断通知机制,没完全解决瓶颈。
  • Netmap更像是几个系统调用,实现用户态直接收发包,功能太过原始,没形成依赖的网络开发框架,社区不完善。

那么,我们来看看发展了十几年的DPDK,从Intel主导开发,到华为、思科、AWS等大厂商的加入,核心玩家都在该圈子里,拥有完善的社区,生态形成闭环。早期,主要是传统电信领域3层以下的应用,如华为、中国电信、中国移动都是其早期使用者,交换机、路由器、网关是主要应用场景。但是,随着上层业务的需求以及DPDK的完善,在更高的应用也在逐步出现。

DPDK旁路原理

dpdk 常见工具(架构之美DPDK技术介绍)(1)


图片引自Jingjing Wu的文档《Flow Bifurcation on Intel® Ethernet Controller X710/XL710》

左边是原来的方式数据从 网卡 -> 驱动 -> 协议栈 -> Socket接口 -> 业务

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

用户态的好处是易用开发和维护,灵活性好。并且Crash也不影响内核运行,鲁棒性强。

DPDK支持的CPU体系架构:x86、ARM、PowerPC(PPC)

DPDK支持的网卡列表:http://core.dpdk.org/supported/,我们主流使用Intel 82599(光口)、Intel x540(电口)

四、DPDK的基石UIO

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

UIO原理

dpdk 常见工具(架构之美DPDK技术介绍)(2)


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

  • 开发运行在内核的UIO模块,因为硬中断只能在内核处理
  • 通过/dev/uioX读取中断
  • 通过mmap和外设共享内存
五、DPDK核心优化:PMD

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

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

运行在PMD的Core会处于用户态CPU100%的状态

dpdk 常见工具(架构之美DPDK技术介绍)(3)


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

Interrupt DPDK

dpdk 常见工具(架构之美DPDK技术介绍)(4)


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

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

六、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实现。

在x86-64下使用RDTSC指令,直接从寄存器读取,需要输入2个参数,比较常见的实现:

static inline uint64_t rte_rdtsc(void) { uint32_t lo hi; __asm__ __volatile__ ( "rdtsc" : "=a"(lo) "=d"(hi) ); return ((unsigned long long)lo) | (((unsigned long long)hi) << 32); }

这么写逻辑没错,但是还不够极致,还涉及到2次位运算才能得到结果,我们看看DPDK是怎么实现:

static inline uint64_t rte_rdtsc(void) { union { uint64_t tsc_64; struct { uint32_t lo_32; uint32_t hi_32; }; } tsc; asm volatile("rdtsc" : "=a" (tsc.lo_32) "=d" (tsc.hi_32)); return tsc.tsc_64; }

巧妙的利用C的union共享内存,直接赋值,减少了不必要的运算。但是使用tsc有些问题需要面对和解决

  • CPU亲和性,解决多核跳动不精确的问题
  • 内存屏障,解决乱序执行不精确的问题
  • 禁止降频和禁止Intel Turbo Boost,固定CPU频率,解决频率变化带来的失准问题

5. 编译执行优化

分支预测

现代CPU通过pipeline、superscalar提高并行处理能力,为了进一步发挥并行能力会做分支预测,提升CPU的并行能力。遇到分支时判断可能进入哪个分支,提前处理该分支的代码,预先做指令读取编码读取寄存器等,预测失败则预处理全部丢弃。我们开发业务有时候会非常清楚这个分支是true还是False,那就可以通过人工干预生成更紧凑的代码提示CPU分支预测成功率。

#pragma once #if !__GLIBC_PREREQ(2 3) # if !define __builtin_expect # define __builtin_expect(x expected_value) (x) # endif #endif #if !defined(likely) #define likely(x) (__builtin_expect(!!(x) 1)) #endif #if !defined(unlikely) #define unlikely(x) (__builtin_expect(!!(x) 0)) #endif

CPU Cache预取

Cache Miss的代价非常高,回内存读需要65纳秒,可以将即将访问的数据主动推送的CPU Cache进行优化。比较典型的场景是链表的遍历,链表的下一节点都是随机内存地址,所以CPU肯定是无法自动预加载的。但是我们在处理本节点时,可以通过CPU指令将下一个节点推送到Cache里。
API文档:http://doc.dpdk.org/api/rte__pr…

static inline void rte_prefetch0(const volatile void *p) { asm volatile ("prefetcht0 %[p]" : : [p] "m" (*(const volatile char *)p)); } #if !defined(prefetch) #define prefetch(x) __builtin_prefetch(x) #endif

…等等

内存对齐
内存对齐有2个好处:

l 避免结构体成员跨Cache Line,需2次读取才能合并到寄存器中,降低性能。结构体成员需从大到小排序和以及强制对齐。参考《Data alignment: Straighten up and fly right》

#define __rte_packed __attribute__((__packed__))

l 多线程场景下写产生False sharing,造成Cache Miss,结构体按Cache Line对齐

#ifndef CACHE_LINE_SIZE #define CACHE_LINE_SIZE 64 #endif #ifndef aligined #define aligined(a) __attribute__((__aligned__(a))) #endif

常量优化

常量相关的运算的编译阶段完成。比如C 11引入了constexp,比如可以使用GCC的__builtin_constant_p来判断值是否常量,然后对常量进行编译时得出结果。举例网络序主机序转换

#define rte_bswap32(x) ((uint32_t)(__builtin_constant_p(x) ? \ rte_constant_bswap32(x) : \ rte_arch_bswap32(x)))

其中rte_constant_bswap32的实现

#define RTE_STATIC_BSWAP32(v) \ ((((uint32_t)(v) & UINT32_C(0x000000ff)) << 24) | \ (((uint32_t)(v) & UINT32_C(0x0000ff00)) << 8) | \ (((uint32_t)(v) & UINT32_C(0x00ff0000)) >> 8) | \ (((uint32_t)(v) & UINT32_C(0xff000000)) >> 24))

使用CPU指令

现代CPU提供很多指令可直接完成常见功能,比如大小端转换,x86有bswap指令直接支持了。

static inline uint64_t rte_arch_bswap64(uint64_t _x) { register uint64_t x = _x; asm volatile ("bswap %[x]" : [x] " r" (x) ); return x; }

这个实现,也是GLIBC的实现,先常量优化、CPU指令优化、最后才用裸代码实现。毕竟都是顶端程序员,对语言、编译器,对实现的追求不一样,所以造轮子前一定要先了解好轮子。

Google开源的cpu_features可以获取当前CPU支持什么特性,从而对特定CPU进行执行优化。高性能编程永无止境,对硬件、内核、编译器、开发语言的理解要深入且与时俱进。

七、DPDK生态

对我们互联网后台开发来说DPDK框架本身提供的能力还是比较裸的,比如要使用DPDK就必须实现ARP、IP层这些基础功能,有一定上手难度。如果要更高层的业务使用,还需要用户态的传输协议支持。不建议直接使用DPDK。

目前生态完善,社区强大(一线大厂支持)的应用层开发项目是http://FD.io(The Fast Data Project),有思科开源支持的VPP,比较完善的协议支持,ARP、VLAN、Multipath、IPv4/v6、MPLS等。用户态传输协议UDP/TCP有TLDK。从项目定位到社区支持力度算比较靠谱的框架。

腾讯云开源的F-Stack也值得关注一下,开发更简单,直接提供了POSIX接口。

Seastar也很强大和灵活,内核态和DPDK都随意切换,也有自己的传输协议Seastar Native TCP/IP Stack支持,但是目前还未看到有大型项目在使用Seastar,可能需要填的坑比较多。

我们GBN Gateway项目需要支持L3/IP层接入做Wan网关,单机20GE,基于DPDK开发。

一、主要特点1、UIO(Linux Userspace I/O)

提供应用空间下驱动程序的支持,也就是说网卡驱动是运行在用户空间的,减下了报文在用户空间和应用空间的多次拷贝。如图:DPDK绕过了Linux内核的网络驱动模块,直接从网络硬件到达用户空间,不需要进行频繁的内存拷贝和系统调用。根据官方给出的数据,DPDK裸包反弹每个包需要80个时钟周期,而传统Linux内核协议栈每包需要2k~4k个时钟周期。DPDK能显著提升虚拟化网络设备的数据采集效率。

dpdk 常见工具(架构之美DPDK技术介绍)(5)

不使用与使用DPDK的Linux内核对比

dpdk 常见工具(架构之美DPDK技术介绍)(6)

UIO技术的工作原理

UIO技术将设备驱动分为用户空间驱动和内核空间驱动两部分,内核空间驱动主要负责设备资源分配、UIO设备注册以及小部分中断响应函数,驱动的大部分工作在用户空间的驱动程序下完成。通过UIO框架提供的API接口将UIO的驱动注册到内核,注册完成后将生成存有设备物理地址等信息的map文件,用户态进程访问该文件将设备对应的内存空间地址映射到用户空间,即可直接操作设备的内存空间,UIO技术使得应用程序可以通过用户空间驱动直接操作设备的内存空间,避免了数据在内核缓冲区和应用程序缓冲区的多次拷贝,提供数据处理效率。

简单地说,DPDK使高速数据包网络应用程序的开发变得更快,这意味着它允许构建能够更快地处理数据包的应用程序,这多亏了内核的绕过。实际上,它使用了快速路径,而不是正常的网络层路径和上下文切换路径。包被直接传递到用户空间(作为原始包)。如下图为linux内核包处理和dpdk包处理的区别。

dpdk 常见工具(架构之美DPDK技术介绍)(7)

linux内核处理包

dpdk 常见工具(架构之美DPDK技术介绍)(8)

dpdk处理包

dpdk 常见工具(架构之美DPDK技术介绍)(9)

slow路径和fast路径比较

2、用户空间轮询模式(PMD)

传统中断模式: 传统Linux系统中,当网络设备检测到数据帧过来的时候,会使用DMA(直接内存访问)将帧发送到预先分配好的内核缓冲区里面,然后更新相应的接收描述符环,之后产生中断通知有数据帧过来。Linux系统会进行相应的响应,然后更新相应的描述符环,再将接收到的数据帧交给内核中的网络堆栈进行处理,网络堆栈处理完之后会将相应的数据拷贝到相应的套接字,从而数据就被复制到了用户空间,应用程序就可以使用这些数据了,数据帧的接收过程如图:

dpdk 常见工具(架构之美DPDK技术介绍)(10)

数据帧的接收过程


在发送的时候,一旦用户程序处理完了数据,会通过一个系统调用将数据写入到套接字,将数据从用户空间拷贝到内核空间的缓冲区,交由网络堆栈进行处理,网络堆栈根据需要对数据进行封装并调用网卡设备的驱动程序,网卡设备驱动程序会更新传输描述符环,然后向网卡设备告知有数据帧需要传输。网卡设备会将数据帧从内核中的缓冲区拷贝到自己的缓冲区中并发送到网络链路上,传送到链路上之后,网卡设备会通过一个中断告知成功发送,然后内核会释放相应的缓冲区。数据的发送如图:

dpdk 常见工具(架构之美DPDK技术介绍)(11)

数据帧的发送过程


由于linux系统是通过中断的方式告知CPU有数据包过来的,当网络的流量越来越大,linux系统会浪费越来越多的时间去处理中断,当流量速率达到10G的时候,linux系统可能会被中断淹没,浪费很多CPU资源。

DPDK用户空间的轮询模式驱动:用户空间驱动使得应用程序不需要经过linux内核就可以访问网络设备卡。网卡设备可以通过DMA方式将数据包传输到事先分配好的缓冲区,这个缓冲区位于用户空间,应用程序通过不断轮询的方式可以读取数据包并在原地址上直接处理,不需要中断,而且也省去了内核到应用层的数据包拷贝过程。

因此相对于linux系统传统中断方式,Intel DPDK避免了中断处理、上下文切换、系统调用、数据复制带来的性能上的消耗,大大提升了数据包的处理性能。同时由于Intel DPDK在用户空间就可以开发驱动,与传统的在内核中开发驱动相比,安全系数大大降低。因为内核层权限比较高,操作相对比较危险,可能因为小的代码bug就会导致系统崩溃,需要仔细的开发和广泛的测试。而在应用层则相反,比较安全,且在应用层调试代码要方便的多。

3、大页内存

Linux操作系统通过查找TLB来实现快速的虚拟地址到物理地址的转化。由于TLB是一块高速缓冲cache,容量比较小,容易发生没有命中。当没有命中的时候,会触发一个中断,然后会访问内存来刷新页表,这样会造成比较大的时延,降低性能。Linux操作系统的页大小只有4K,所以当应用程序占用的内存比较大的时候,会需要较多的页表,开销比较大,而且容易造成未命中。相比于linux系统的4KB页,Intel DPDK缓冲区管理库提供了Hugepage大页内存,大小有2MB和1GB页面两种,可以得到明显性能的提升,因为采用大页内存的话,可以需要更少的页,从而需要更少的TLB,这样就减少了虚拟页地址到物理页地址的转换时间。‘

DPDK中的内存管理如图,最下面是连续的物理内存,这些物理内存是由2MB的大页组成,连续的物理内存上面是内存段,内存段之上则是内存区,我们分配的基本单元对象是在内存区中分配的,内存区包含了ring队列,内存池、LPM路由表还有其他一些高性能的关键结构。

dpdk 常见工具(架构之美DPDK技术介绍)(12)

DPDK中的内存管理

4、CPU亲和性

CPU的亲和性(CPU affinity),它是多核CPU发展的结果。随着核心的数量越来越多,为了提高程序工作的效率必须使用多线程。但是随着CPU的核心的数目的增长,Linux的核心间的调度和共享内存争用会严重影响性能。利用Intel DPDK的CPU affinity可以将各个线程绑定到不同的cpu,可以省去来回反复调度带来的性能上的消耗。

在一个多核处理器的机器上,每个CPU核心本身都存在自己的缓存,缓冲区里存放着线程使用的信息。如果线程没有绑定CPU核,那么线程可能被Linux系统调度到其他的CPU上,这样的话,CPU的cache命中率就降低了。利用CPU的affinity技术,一旦线程绑定到某个CPU后,线程就会一直在指定的CPU上运行,操作系统不会将其调度到其他的CPU上,节省了调度的性能消耗,从而提升了程序执行的效率。

多核轮询模式:多核轮询模式有两种,分别是IO独占式和流水线式。IO独占式是指每个核独立完成数据包的接收、处理和发送过程,核之间相互独立,其优点是其中一个核出现问题时不影响其他核的数据收发。流水线式则采用多核合作的方式处理数据包,数据包的接收、处理和发送由不同的核完成。流水线式适合面向流的数据处理,其优点是可对数据包按照接收的顺序有序进行处理,缺点是当某个环境(例如接收)所涉及的核出现阻塞,则会造成收发中断。

IO独占式多核轮询模式中每个网卡只分配给一个逻辑核进行处理。每个逻辑核给所接管的网卡分别分配一个发送队列和一个接收队列,并且独立完成数据包的接收、处理和发送的过程,核与核之间相互独立。系统数据包的处理由多个逻辑核同时进行,每个网卡的收发包队列只能由一个逻辑核提供。当数据包进入网卡的硬件缓存区,用户空间提供的网卡驱动通过轮询得知网卡收到数据包,从硬件缓冲区中取出数据包,并将数据包存入逻辑核提供的收包队列中,逻辑核取出收包队列中的数据包进行处理,处理完毕后将数据包存入逻辑核提供的发包队列,然后由网卡驱动取出发往网卡,最终发送到网络中。

dpdk 常见工具(架构之美DPDK技术介绍)(13)

IO独占式多核轮询模式架构

5、内存池和无锁环形缓存管理

此外Intel DPDK将库和API优化成了无锁,比如无锁队列,可以防止多线程程序发生死锁。然后对缓冲区等数据结构进行了cache对齐。如果没有cache对齐,则可能在内存访问的时候多读写一次内存和cache。

内存池缓存区的申请和释放采用的是生产者-消费者模式无锁缓存队列进行管理,避免队列中锁的开销,在缓存区的使用过程中提高了缓冲区申请释放的效率。

dpdk 常见工具(架构之美DPDK技术介绍)(14)

无锁环形队列生产过程

dpdk 常见工具(架构之美DPDK技术介绍)(15)

无锁环形队列消费过程

如图所示,生产者往队列里存放内容的方向和消费者从队列里取内容的方向一致,均以顺时针方向进行。当缓存区向内存池申请内存块,或者应用程序进行内存块的释放时,缓存区的无锁环形队列的生产者指针顺时针移动,往队列中存入内存块地址信息,进行缓存队列的生产过程。当应用程序需要向缓冲区申请内存块使用时,缓冲区的无锁环形队列的消费者指针以顺时针的方向取出队列的内存块地址,分配给应用程序使用,该过程为缓存队列的消费过程。

生产n个对象过程:首先生产者头指针往顺时针方向移n个位置获得新的头指针,然后从生产者尾指针指的区域开始逐个存入n个对象,最后生产者尾指针顺时针移动n个位置获得新的生产者尾指针

消费n个对象过程:首先消费者头指针顺时针移动n个位置获得新的消费者头指针,然后从消费者尾指针处开始逐个读取n个对象,最后消费者尾指针顺时针移动n个位置获得新的消费者尾指针。

6、网络存储优化

dpdk 常见工具(架构之美DPDK技术介绍)(16)

网络存储优化

二、架构与核心组件1、 DPDK总体架构

dpdk 常见工具(架构之美DPDK技术介绍)(17)

DPDK总体架构

2、核心组件

DPDK主要有六个核心组件

1、 环境抽象层(EAL):为DPDK其他组件和应用程序提供一个屏蔽具体平台特性的统一接口,环境抽象层提供的功能主要有:DPDK加载和启动;支持多核和多线程执行类型;CPU核亲和性处理;原子操作和锁操作接口;时钟参考;PCI总线访问接口;跟踪和调试接口;CPU特性采集接口;中断和告警接口等。

2、 堆内存管理组件(Malloc lib):堆内存管理组件为应用程序提供从大页内存分配对内存的接口。当需要分配大量内存小块时,使用这些接口可以减少TLB缺页。

3、 环缓冲区管理组件(Ring lib):环缓冲区管理组件为应用程序和其他组件提供一个无锁的多生产者多消费者FIFO队列API:Ring。Ring是借鉴了Linux内核kfifo无锁队列,可以无锁出入对,支持多消费/生产者同时出入队。

4、 内存池管理组件(Mem pool lib):为应用程序和其他组件提供分配内存池的接口,内存池是一个由固定大小的多个内存块组成的内存容器,可用于存储相同对象实体,如报文缓存块等。内存池由内存池的名称来唯一标识,它由一个环缓冲区和一组核本地缓存队列组成,每个核从自己的缓存队列分配内存块,当本地缓存队列减少到一定程度时,从内存缓冲区中申请内存块来补充本地队列。

5、** 网络报文缓存块管理组件(Mbuf lib)**:提供应用程序创建和释放用于存储报文信息的缓存块的接口,这些MBUF存储在内存池中。提供两种类型的MBUF,一种用于存储一般信息,一种用于存储报文信息。

6、 定时器组件(Timer lib):提供一些异步周期执行的接口(也可以只执行一次),可以指定某个函数在规定的时间异步的执行,就像LIBC中的timer定时器,但是这里的定时器需要应用程序在主循环中周期调用rte_timer_manage来使定时器得到执行。定时器组件的时间参考来自EAL层提供的时间接口。

除了以上六个核心组件外,DPDK还提供以下功能:

1) 以太网轮询模式驱动(PMD)架构:把以太网驱动从内核移到应用层,采用同步轮询机制而不是内核态的异步中断机制来提高报文的接收和发送效率。

2)报文转发算法支持:Hash 库和LPM库为报文转发算法提供支持。

3) 网络协议定义和相关宏定义:基于FreeBSD IP协议栈的相关定义如:TCP、UDP、SCTP等协议头定义。

4)报文QOS调度库:支持随机早检测、流量整形、严格优先级和加权随机循环优先级调度等相关QOS 功能。

5)内核网络接口库(KNI):提供一种DPDK应用程序与内核协议栈的通信的方法、类似普通Linux的TUN/TAP接口,但比TUN/TAP接口效率高。每个物理网口可以虚拟出多个KNI接口。

dpdk 常见工具(架构之美DPDK技术介绍)(18)

内核网络接口

3、KNI组件

KNI是DPDK平台提供的用于将数据重入内核协议栈的一个组件,其目的是充分运用传统内核协议栈已实现的较稳定的协议处理功能。DPDK平台对数据包的处理绕过了内核协议栈,直接交给用户空间处理,而用户空间没有完善的协议处理栈,如果让开发人员在用户空间实现完整独立的协议栈,开发工作是非常复杂的,因此DPDK平台提供了KNI组件,开发人员可以在用户空间实现一些特殊的协议处理功能,再通过KNI重入内核协议栈功能将普通常见的协议交由传统内核协议栈处理。

dpdk 常见工具(架构之美DPDK技术介绍)(19)

KNI通信机制

KNI组件通过创建KNI虚拟接口设备,将数据包经过虚拟接口实现用户空间和内核协议栈间的通信。当网卡接收到数据包时,应用程序通过用户空间驱动将数据包获取到用户空间,KNI组件将需要数据包发送至KNI虚拟接口,由KNI虚拟接口交给内核协议栈处理,处理后若有响应报文,则再交给KNI虚拟接口返回给应用程序。其中发送数据包至内核协议栈以及接收内核协议栈回复的数据包,是由两个不同的逻辑核分别进行处理,不阻塞应用程序让内核协议栈发送数据包或从内核协议栈接收数据包的过程。

KNI接口实际上是一个虚拟出来的设备,该虚拟设备定义了四个队列,分别是接收队列(rx_q)、发送队列(tx_q)、已分配内存块队列(alloc_q)、待释放内存块队列(free_q)。接收队列用于存放用户空间程序发往KNI虚拟设备的报文,发送队列用于存放内核协议栈要往KNI虚拟设备的报文。已分配内存块队列存放已向内存中申请的内存块,供内核协议栈发送报文时取出使用。待释放内存块队列用于记录KNI虚拟设备从用户空间程序处接收到报文后将不再使用的内存块,然后将该队列中的内存块释放回内存。用户空间程序从网卡接收到报文时,将报文发送给KNI虚拟设备,KNI虚拟设备接收到用户空间程序发来的报文后,交给内核协议栈进行协议解析。发送报文时,原始数据先由内核协议栈进行协议封装,然后将报文发送给KNI虚拟设备,KNI虚拟设备接收到报文后,再将报文发送给用户空间程序

dpdk 常见工具(架构之美DPDK技术介绍)(20)

KNI虚拟设备收发报文过程

猜您喜欢: