文章首发于我的公众号「Linux云计算网络」,欢迎关注,第一时间掌握技术干货!

本文的一些概念依赖于上一篇 “CPU 拓扑:从 SMP 谈到 NUMA (理论篇)”,如果你对这块还没有概念,建议看完那篇再来看,如果对这块有自己独到的见解,欢迎探讨。

本文主要会侧重 NUMA 这块,我会通过自己的环境验证 NUMA 的几个概念,以便对 NUMA 的架构有个较深的印象。

NUMA 的几个概念:Node,Socket,Core,Thread


NUMA 技术的主要思想是将 CPU 进行分组,Node 即是分组的抽象,一个 Node 表示一个分组,一个分组可以由多个 CPU 组成。每个 Node 都有自己的本地资源,包括内存、IO 等。每个 Node 之间通过互联模块(QPI)进行通信,因此每个 Node 除了可以访问自己的本地内存之外,还可以访问远端 Node 的内存,只不过性能会差一些,一般用 distance 这个抽象的概念来表示各个 Node 之间互访资源的开销。

Node 是一个逻辑上的概念,与之相对的 Socket,是物理上的概念。它表示一颗物理 CPU 的封装,是主板上的 CPU 插槽,所以,一般就称之为插槽(敲黑板,这 Y 不是套接字吗?emmm……)

Core 就是 Socket 里独立的一组程序执行单元,称之为物理核。有了物理核,自然就有逻辑核,Thread 就是逻辑核。更专业的说法应该称之为超线程。

超线程是为了进一步提高 CPU 的处理能力,Intel 提出的新型技术。它能够将一个 Core 从逻辑上划分成多个逻辑核(一般是两个),每个逻辑核有独立的寄存器和中断逻辑,但是一个 Core 上的多个逻辑核共享 Core 内的执行单元和 Cache,频繁调度可能会引起资源竞争,影响性能。超线程必须要 CPU 支持才能开启。

综上所述,一个 NUMA Node 可以有一个或者多个 Socket,每个 Socket 也可以有一个(单核)或者多个(多核)Core,一个 Core 如果打开超线程,则会变成两个逻辑核(Logical Processor,简称 Processor)。

所以,几个概念从大到小排序依次是:

Node > Socket > Core > Processor

验证 CPU 拓扑


了解了以上基本概念,下面在实际环境中查看这些概念并验证。

Node

numactl --hardware 查看当前系统的 NUMA Node(numactl 是设定进程 NUMA 策略的命令行工具):

1
2
3
4
5
6
7
8
9
10
11
12
Linux # numactl --hardware
available: 2 nodes (0-1)
node 0 cpus: 0 1 2 3 4 5 12 13 14 15 16 17
node 0 size: 49043 MB
node 0 free: 20781 MB
node 1 cpus: 6 7 8 9 10 11 18 19 20 21 22 23
node 1 size: 49152 MB
node 1 free: 31014 MB
node distances:
node 0 1
0: 10 21
1: 21 10

可以得出的信息有:1)系统的 Node 数为 2;2)每个 Node 包含的 Processor 数为 12;3)每个 Node 的总内存大小和空闲内存大小;4)每个 Node 之间的 distance。

还可以查看 /sys/devices/system/node/ 目录,这里记录着具体哪些 Node。

Socket

/proc/cpuinfo 中记录着 Socket 信息,用 “physical id” 表示,可以用 cat /proc/cpuinfo | grep "physical id" 查看:

1
2
3
4
5
6
7
8
9
Linux # cat /proc/cpuinfo | grep "physical id"
physical id : 0
physical id : 0
physical id : 0
physical id : 0
physical id : 1
physical id : 1
physical id : 1
physical id : 1

可以看到有 2 个 Socket,我们还可以查看以下这几种变种:

1)查看有几个 Socket

1
2
3
4
Linux # grep 'physical id' /proc/cpuinfo | awk -F: '{print $2 | "sort -un"}'

0
1

1
2
3
Linux # grep 'physical id' /proc/cpuinfo | awk -F: '{print $2 | "sort -un"}' | wc -l

2

2)查看每个 Socket 有几个 Processor

1
2
3
4
Linux # grep 'physical id' /proc/cpuinfo | awk -F: '{print $2}' | sort | uniq -c

12 0
12 1

3)查看每个 Socket 有哪几个 Processor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Linux # awk -F: '{ 
> if ($1 ~ /processor/) {
> gsub(/ /,"",$2);
> p_id=$2;
> } else if ($1 ~ /physical id/){
> gsub(/ /,"",$2);
> s_id=$2;
> arr[s_id]=arr[s_id] " " p_id
> }
> }
>
> END{
> for (i in arr)
> print arr[i];
> }' /proc/cpuinfo | cut -c2-

0 1 2 3 4 5 12 13 14 15 16 17
6 7 8 9 10 11 18 19 20 21 22 23

Core

同样在 /proc/cpuinfo 中查看 Core 信息:

1
2
3
4
5
6
7
Linux # cat /proc/cpuinfo |grep "core id" | sort -u
core id : 0
core id : 1
core id : 2
core id : 3
core id : 4
core id : 5

上面的结果表明一个 Socket 有 5 个 Core。上面查到有 2 个 Socket,则一共就有 10 个 Core。

Processor

上面查看 Socket 信息时已经能够得到 Processor 的信息,总共有 24 个 Processor,不过也可以直接从 /proc/cpuinfo 中获取:

1)获取总的 Processor 数,查看 “processor” 字段:

1
2
3
Linux # cat /proc/cpuinfo | grep "processor" | wc -l

24

2)获取每个 Socket 的 Processor 数,查看 “siblings” 字段:

1
2
3
Linux # cat /proc/cpuinfo | grep "siblings" | sort -u

12

Cache

Cache 也一样通过 /proc/cpuinfo 查看:

1
2
3
4
5
processor  : 0

cache size : 15360 KB

cache_alignment : 64

不过这里的值 cache size 比较粗略,我们并不知道这个值是哪一级的 Cache 值(L1?L2?L3?),这种方法不能确定,我们换一种方法。

其实详细的 Cache 信息可以通过 sysfs 查看,如下:

比如查看 cpu0 的 cache 情况:

1
2
3
Linux # ls /sys/devices/system/cpu/cpu0/cache/

index0/ index1/ index2/ index3/

其中包含四个目录:
index0 存 L1 数据 Cache,index1 存 L1 指令 Cache,index2 存 L2 Cache,index3 存 L3 Cache。每个目录里面包含一堆描述 Cache 信息的文件。我们选 index0 具体看下:

其中,shared_cpu_list 和 shared_cpu_map 表示意思是一样的,都表示该 cache 被哪几个 processor 共享。对 shared_cpu_map 具体解释一下。

这个值表面上看是二进制,但其实是 16 进制,每个数字有 4 个bit,代表 4 个 cpu。比如上面的 001001 拆开后是:

1
0000 0000 0001 0000 0000 0001,1 bit 处即对应 cpu 标号,即 cpu0 和 cpu12。

同样我们可以对其他 index 进行统计,可以得出:
/proc/cpuinfo 中的 cache size 对应的 L3 Cache size

最后,综合以上所有信息我们可以绘制出一下的 CPU 拓扑图:

我们发现以上命令用得不太顺手,要获取多个数据需要输入多条命令,能不能一条命令就搞定,当然是有的,lscpu 就可以做到,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Linux # lscpu 
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 24 //共有24个逻辑CPU(threads)
On-line CPU(s) list: 0-23
Thread(s) per core: 2 //每个 Core 有 2 个 Threads
Core(s) per socket: 12 //每个 Socket 有 12 个 Threads
Socket(s): 2 //共有 2 个 Sockets
NUMA node(s): 2 //共有 2 个 Nodes
Vendor ID: GenuineIntel
CPU family: 6
Model: 63
Stepping: 2
CPU MHz: 2401.000
BogoMIPS: 4803.16
Virtualization: VT-x
L1d cache: 32K //L1 data cache 32k
L1 cache: 32K //L1 instruction cache 32k
L2 cache: 256K //L2 instruction cache 256k
L3 cache: 15360K //L3 instruction cache 15M
NUMA node0 CPU(s): 0-5,12-17
NUMA node1 CPU(s): 6-11,18-23

当然了,没有完美的命令,lscpu 也只能显示一些宽泛的信息,只是相对比较全面而已,更详细的信息,比如 Core 和 Cache 信息就得借助 cpuinfo 和 sysfs 了。

下面给大家提供一个脚本,能够比较直观的显示以上所有信息,有 shell 版的和 python 版的(不是我写的,文末附上了引用出处)。

大家有需要可以回复 “CPU” 获取,我就不贴出来了,显示的结果大概就是长下面这个样子:

python 版:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
============================================================
Core and Socket Information (as reported by '/proc/cpuinfo')
============================================================

cores = [0, 1, 2, 3, 4, 5]

sockets = [0, 1]


Socket 0 Socket 1

-------- --------

Core 0 [0, 12] [6, 18]

Core 1 [1, 13] [7, 19]

Core 2 [2, 14] [8, 20]

Core 3 [3, 15] [9, 21]

Core 4 [4, 16] [10, 22]

Core 5 [5, 17] [11, 23]

Reference:

  1. 玩转 CPU 拓扑:
    http://blog.itpub.net/645199/viewspace-1421876/
  2. NUMA 体系结构详解
    https://blog.csdn.net/ustc_dylan/article/details/45667227(shell 代码引用)
  3. dpdk 源代码(python 代码引用)

PS:文章未经我允许,不得转载,否则后果自负。

–END–

欢迎扫👇的二维码关注我的微信公众号,后台回复「m」,可以获取往期所有技术博文推送,更多资料回复下列关键字获取。

文章首发于我的公众号「Linux云计算网络」,欢迎关注,第一时间掌握技术干货!

随着计算机技术(特别是以芯片为主的硬件技术)的快速发展,CPU 架构逐步从以前的单核时代进阶到如今的多核时代,在多核时代里,多颗 CPU 核心构成了一个小型的“微观世界”。每颗 CPU 各司其职,并与其他 CPU 交互,共同支撑起了一个“物理世界”。从这个意义上来看,我们更愿意称这样的“微观世界”为 CPU 拓扑,就像一个物理网络一样,各个网络节点通过拓扑关系建立起连接来支撑起整个通信系统。

单核 or 多核 or 多 CPU or 超线程 ?


在单核时代,为了提升 CPU 的处理能力,普遍的做法是提高 CPU 的主频率,但一味地提高频率对于 CPU 的功耗也是影响很大的(CPU 的功耗正比于主频的三次方)。

另外一种做法是提高 IPC (每个时钟周期内执行的指令条数),这种做法要么是提高指令的并行度,要么是增加核数。显然,后一种方法更具有可扩展性,这也是摩尔定律的必然性。

CPU 的性能到底是如何体现的呢?为了弄清楚这个问题,我们结合单核 CPU 和多核 CPU 的结构来进一步剖析。

首先,对于一个单核结构,即一颗物理 CPU 封装里只集成了一个物理核心,其主要组件可以简化为:CPU 寄存器集合、中断逻辑、执行单元和 Cache,如下图:

对于一个多线程程序,主要通过时间片轮转的方式来获得 CPU 的执行权,从内部来看,这其实是串行执行的,性能自然并不怎么高。

其次,对于多核结构,则是在一颗物理 CPU 封装里集成了多个对等的物理核心,所谓对等,就是每个核心都有相同的内部结构。多个核心之间通过芯片内部总线来完成通信。随着 CPU 制造工艺的提升,每颗 CPU 封装中集成的物理核心也在不断提高。

对于一个多线程程序,这种情况能够实现真正的并发,但线程在不同核之间切换会存在一定的开销,但由于走的是芯片内部总线,开销相对会比较小。

除了上述两种结构,还有另外一种结构是多 CPU 结构,也就是多颗单独封装的 CPU 通过外部总线相连,构成的一个统一的计算平台。每个 CPU 都需要独立的电路支持,有自己的 Cache。它们之间的通信通过主板上的总线来完成。

同样对于一个多线程程序,不同于上一种情况的是,线程间的切换走的是外部总线,延迟较大,开销自然较大,而且对于有共享的数据还会因 Cache 一致性带来一定的开销(关于 Cache 下一小节说明)。

上述结构,一个 CPU 核心同一时间内只能执行一个线程,效率低下,为了提高 CPU 的利用率,CPU 芯片厂商又推出超线程(Hyper-Thread-ing)技术,即让一个物理核心同时执行多个线程,使整体性能得到提升。虽然物理上只有一个核心,但逻辑上被划分了多个逻辑核心,它们之间是完全隔离的。

对于每个逻辑核心,拥有完整独立的寄存器集合和中断逻辑,共享执行单元和 Cache。由于是共享执行单元,所以对高 IPC 的应用,其性能提升有限。

Cache


Cache 是一种 SRAM (Static Random Access Memory,静态访问随机存储器)。出于成本和生产工艺考虑,一般将 Cache 分为三级。一级(L1)访问速度最快,但是容量最小,一般只有几十 KB;二级(L2)次之,一般有几百 KB 到几 MB 不等,三级(LLC,Last Level Cache)最慢,但是容量也最大,一般有几 MB 到几十 MB。

一级 Cache 又分为数据 Cache 和指令 Cache,顾名思义,数据 Cache 用来存数据,指令 Cache 用来存指令。下图是一个简单的 Cache 系统逻辑示意图。

在多核结构中,每个物理核心都拥有独立的一级 Cache 和二级 Cache,而三级 Cache 是所有核心共享。这种共享需要解决的一个问题是公平地为每个核心分配 Cache 大小,避免 Cache 命中率低的问题。

对于现代计算机系统,说到 Cache,不得不提 TLB(Translation Look-aside Buffer) Cache。简单理解,如果说 Cache 存放的是内存中的内容,那么 TLB Cache 存放的是页表项。

为什么页表项需要用 Cache 存,原因当然是快。你可能觉得用三级 Cache 存就行了,为什么还要专门上 TLB Cache。

这里有两点考虑,一点是 TLB 采用基于内容的访问存储器 CAM,这种存储器能做到根据虚拟地址查询直接返回物理地址,效率极高,不需要像传统方式那样采用多级页表查询。另外一点是 Cache 的“淘汰”机制决定,Cache 会根据算法淘汰掉那些不常使用的内容,这对于页表这种需要频繁访问(每次程序寻址都要访问页表)的特性显然是矛盾的,所以就需要专门为页表提供一种特殊的 Cache,即 TLB Cache。

SMP or NUMA or MPP?


如果说前面咱们讨论的是 CPU 内部的“微观世界”,那么本节将跳出来,探讨一个系统级的“宏观世界”。

首先是 SMP,对称多处理器系统,指的是一种多个 CPU 处理器共享资源的电脑硬件架构,其中,每个 CPU 没有主从之分,地位平等,它们共享相同的物理资源,包括总线、内存、IO、操作系统等。每个 CPU 访问内存所用时间都是相同的,因此这种系统也被称为一致存储访问结构(UMA,Uniform Memory Access)。

这种系统由于共享资源,不可避免地要加锁来解决资源竞争的问题,带来一定的性能开销,另外,扩展能力还非常有限,实验证明,SMP 系统最好的情况是有 2-4 个 CPU,适用于 PC、笔记本电脑和小型服务器等。

tips: 查看系统是否是 SMP 结构:

1
ls /sys/devices/system/node/     # 如果只看到一个 node0 那就是 SMP 架构

为了应对大规模的系统要求(特别是云计算环境),就研制出了 NUMA 结构,即非一致存储访问结构。

这种结构引入了 CPU 分组的概念,用 Node 来表示,一个 Node 可能包含多个物理 CPU 封装,从而包含多个 CPU 物理核心。每个 Node 有自己独立的资源,包括内存、IO 等。每个 Node 之间可以通过互联模块总线(QPI)进行通信,所以,也就意味着每个 Node 上的 CPU 都可以访问到整个系统中的所有内存,但很显然,访问远端 Node 的内存比访问本地内存要耗时很多,这也是 NUMA 架构的问题所在,我们在基于 NUMA 架构开发上层应用程序要尽可能避免跨 Node 内存访问。

NUMA 架构在 SMP 架构的基础上通过分组的方式增强了可扩展性,但从性能上看,随着 CPU 数量的增加,并不能线性增加系统性能,原因就在于跨 Node 内存访问的问题。所以,一般 NUMA 架构最多支持几百个 CPU 就不错了。

但对于很多大型计算密集型的系统来说,NUMA 显然有些吃力,所以,后来又出现了 MPP 架构,即海量并行处理架构。这种架构也有分组的概念,但和 NUMA 不同的是,它不存在异地内存访问的问题,每个分组内的 CPU 都有自己本地的内存、IO,并且不与其他 CPU 共享,是一种完全无共享的架构,因此它的扩展性最好,可以支持多达数千个 CPU 的量级。

总结


1、在芯片技术已然发展成熟的今天,性能低,上核不是问题,但上核就一定能提高性能吗,另外上核怎么很好地利用多核来完成自身进化,这些问题都值得深思。

2、NUMA 架构算是多核时代应用较大的一种 CPU 架构,本文从核心谈到系统,让大家有个全面的了解,下文会特别针对 NUMA 架构做一些实验验证。

Reference:
1.《深入浅出 DPDK》

  1. SMP、NUMA、MPP体系结构介绍:
    https://www.cnblogs.com/yubo/archive/2010/04/23/1718810.html

PS:文章未经我允许,不得转载,否则后果自负。

–END–

欢迎扫👇的二维码关注我的微信公众号,后台回复「m」,可以获取往期所有技术博文推送,更多资料回复下列关键字获取。

文章首发于我的公众号「Linux云计算网络」,欢迎关注,第一时间掌握技术干货!

01 写在前面


我的读者当中应该有一部分人是做 DPDK 相关的,我自己虽然现在已经不做 DPDK 了,但对这块仍然有兴趣,今天这篇文章就来总结下 DPDK 的技术栈。注意:这篇文章是小白文,不适合大神哦。

文章从 DPDK 的产生背景,到核心技术,再到应用场景,都进行了阐述,有可能是你见过的讲得最全面的文章了,当然,讲得全面自然会少了深度,你如果不屑忽略就好了。

其实,本文之前已经发过,但在整理的时候不小心删了,索性就重发一次,但这一次多了一些内容,文末我还会推荐一些学习资料,有需要的可以拉到最下面查看获取方法。

02 高性能网络技术


随着云计算产业的异军突起,网络技术的不断创新,越来越多的网络设备基础架构逐步向基于通用处理器平台的架构方向融合,从传统的物理网络到虚拟网络,从扁平化的网络结构到基于 SDN 分层的网络结构,无不体现出这种创新与融合。

这在使得网络变得更加可控制和成本更低的同时,也能够支持大规模用户或应用程序的性能需求,以及海量数据的处理。究其原因,其实是高性能网络编程技术随着网络架构的演进不断突破的一种必然结果。

03 C10K 到 C10M 问题的演进


说到高性能网络编程,一定逃不过 C10K 问题(即单机 1 万个并发连接问题),不过这个问题已经成为历史了,很多技术可以解决它,如常用的 I/O 多路复用模型,select, poll, epoll 等。在此基础上也出了很多优秀的框架,比如 Nginx 基于事件驱动的 Web 服务框架,以及基于 Python 开发的 Tornado 和 Django 这种非阻塞的 Web 框架。

如今,关注的更多是 C10M 问题(即单机 1 千万个并发连接问题)。很多计算机领域的大佬们从硬件上和软件上都提出了多种解决方案。从硬件上,比如说,现在的类似很多 40Gpbs、32-cores、256G RAM 这样配置的 X86 服务器完全可以处理 1 千万个以上的并发连接。

但是从硬件上解决问题就没多大意思了,首先它成本高,其次不通用,最后也没什么挑战,无非就是堆砌硬件而已。所以,抛开硬件不谈,我们看看从软件上该如何解决这个世界难题呢?

这里不得不提一个人,就是 Errata Security 公司的 CEO Robert Graham,他在 Shmoocon 2013 大会上很巧妙地解释了这个问题。有兴趣可以查看其 YouTube 的演进视频: C10M Defending The Internet At Scale。

他提到了 UNIX 的设计初衷其实为电话网络的控制系统而设计的,而不是一般的服务器操作系统,所以,它仅仅是一个负责数据传送的系统,没有所谓的控制层面和数据层面的说法,不适合处理大规模的网络数据包。最后他得出的结论是:

OS 的内核不是解决 C10M 问题的办法,恰恰相反 OS 的内核正式导致 C10M 问题的关键所在。

04 为什么这么说?基于 OS 内核的数据传输有什么弊端?


1、中断处理: 当网络中大量数据包到来时,会产生频繁的硬件中断请求,这些硬件中断可以打断之前较低优先级的软中断或者系统调用的执行过程,如果这种打断频繁的话,将会产生较高的性能开销。

2、内存拷贝: 正常情况下,一个网络数据包从网卡到应用程序需要经过如下的过程:数据从网卡通过 DMA 等方式传到内核开辟的缓冲区,然后从内核空间拷贝到用户态空间,在 Linux 内核协议栈中,这个耗时操作甚至占到了数据包整个处理流程的 57.1%。

3、上下文切换: 频繁到达的硬件中断和软中断都可能随时抢占系统调用的运行,这会产生大量的上下文切换开销。另外,在基于多线程的服务器设计框架中,线程间的调度也会产生频繁的上下文切换开销,同样,锁竞争的耗能也是一个非常严重的问题。

4、局部性失效: 如今主流的处理器都是多个核心的,这意味着一个数据包的处理可能跨多个 CPU 核心,比如一个数据包可能中断在 cpu0,内核态处理在 cpu1,用户态处理在 cpu2,这样跨多个核心,容易造成 CPU 缓存失效,造成局部性失效。如果是 NUMA 架构,更会造成跨 NUMA 访问内存,性能受到很大影响。

5、内存管理: 传统服务器内存页为 4K,为了提高内存的访问速度,避免 cache miss,可以增加 cache 中映射表的条目,但这又会影响 CPU 的检索效率。

综合以上问题,可以看出内核本身就是一个非常大的瓶颈所在。那很明显解决方案就是想办法绕过内核。

05 解决方案探讨


针对以上弊端,分别提出以下技术点进行探讨。

1、控制层和数据层分离: 将数据包处理、内存管理、处理器调度等任务转移到用户空间去完成,而内核仅仅负责部分控制指令的处理。这样就不存在上述所说的系统中断、上下文切换、系统调用、系统调度等等问题。

2、多核技术: 使用多核编程技术代替多线程技术,并设置 CPU 的亲和性,将线程和 CPU 核进行一比一绑定,减少彼此之间调度切换。

3、NUMA 亲和性: 针对 NUMA 系统,尽量使 CPU 核使用所在 NUMA 节点的内存,避免跨内存访问。

4、大页内存: 使用大页内存代替普通的内存,减少 cache-miss。

5、无锁技术: 采用无锁技术解决资源竞争问题。

经研究,目前业内已经出现了很多优秀的集成了上述技术方案的高性能网络数据处理框架,如 6wind、Windriver、Netmap、DPDK 等,其中,Intel 的 DPDK 在众多方案脱颖而出,一骑绝尘。

DPDK 为 Intel 处理器架构下用户空间高效的数据包处理提供了库函数和驱动的支持,它不同于 Linux 系统以通用性设计为目的,而是专注于网络应用中数据包的高性能处理。

也就是 DPDK 绕过了 Linux 内核协议栈对数据包的处理过程,在用户空间实现了一套数据平面来进行数据包的收发与处理。在内核看来,DPDK 就是一个普通的用户态进程,它的编译、连接和加载方式和普通程序没有什么两样。

06 DPDK 的突破


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

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

1
2
硬件中断--->取包分发至内核线程--->软件中断--->内核线程在协议栈中处理包--->处理完毕通知用户层
用户层收包-->网络层--->逻辑层--->业务层

dpdk 网络数据流程:

1
2
硬件中断--->放弃中断流程
用户层通过设备映射取包--->进入用户层协议栈--->逻辑层--->业务层

下面就具体看看 DPDK 做了哪些突破?

6.1 UIO (用户空间的 I/O 技术)的加持。


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

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

6.2 内存池技术


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

6.3 大页内存管理


DPDK 实现了一组大页内存分配、使用和释放的 API,上层应用可以很方便使用 API 申请使用大页内存,同时也兼容普通的内存申请。

6.4 无锁环形队列


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

6.5 poll-mode 网卡驱动


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

6.6 NUMA


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

6.7 CPU 亲和性


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

6.8 多核调度框架


DPDK 基于多核架构,一般会有主从核之分,主核负责完成各个模块的初始化,从核负责具体的业务处理。

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

07 DPDK 的应用


DPDK 作为优秀的用户空间高性能数据包加速套件,现在已经作为一个“胶水”模块被用在多个网络数据处理方案中,用来提高性能。如下是众多的应用。

数据面(虚拟交换机)


OVS

Open vSwitch 是一个多核虚拟交换机平台,支持标准的管理接口和开放可扩展的可编程接口,支持第三方的控制接入。

https://github.com/openvswitch/ovs

VPP

VPP 是 cisco 开源的一个高性能的包处理框架,提供了 交换/路由 功能,在虚拟化环境中,使它可以当做一个虚拟交换机来使用。在一个类 SDN 的处理框架中,它往往充当数据面的角色。经研究表明,VPP 性能要好于 OVS+DPDK 的组合,但它更适用于 NFV,适合做特定功能的网络模块。

https://wiki.fd.io/view/VPP

Lagopus

Lagopus 是另一个多核虚拟交换的实现,功能和 OVS 差不多,支持多种网络协议,如 Ethernet,VLAN,QinQ,MAC-in-MAC,MPLS 和 PBB,以及隧道协议,如 GRE,VxLan 和 GTP。

https://github.com/lagopus/lagopus/blob/master/QUICKSTART.md

Snabb

Snabb 是一个简单且快速的数据包处理工具箱。

https://github.com/SnabbCo/snabbswitch/blob/master/README.md

数据面(虚拟路由器)


OPENCONTRAIL

一个集成了 SDN 控制器的虚拟路由器,现在多用在 OpenStack 中,结合 Neutron 为 OpenStack 提供一站式的网络支持。

http://www.opencontrail.org/

CloudRouter

一个分布式的路由器。

https://cloudrouter.org/

用户空间协议栈


mTCP

mTCP 是一个针对多核系统的高可扩展性的用户空间 TCP/IP 协议栈。

https://github.com/eunyoung14/mtcp/blob/master/README

IwIP

IwIP 针对 RAM 平台的精简版的 TCP/IP 协议栈实现。

http://git.savannah.gnu.org/cgit/lwip.git/tree/README

Seastar

Seastar 是一个开源的,基于 C++ 11/14 feature,支持高并发和低延迟的异步编程高性能库。

http://www.seastar-project.org/

f-stack

腾讯开源的用户空间协议栈,移植于 FreeBSD协议栈,粘合了 POSIX API,上层应用(协程框架,Nginx,Redis),纯 C 编写,易上手。

https://github.com/f-stack/f-stack

存储加速


SPDK

SPDK 是 DPDK 的孪生兄弟,专注存储性能加速,目前的火热程度丝毫不亚于 DPDK,Intel 近来对 SPDK 特别重视,隔三差五就发布新版本。

https://github.com/spdk/spdk

08 总结


DPDK 绕过了 Linux 内核协议栈,加速数据的处理,用户可以在用户空间定制协议栈,满足自己的应用需求,目前出现了很多基于 DPDK 的高性能网络框架,OVS 和 VPP 是常用的数据面框架,mTCP 和 f-stack 是常用的用户态协议栈,SPDK 是存储性能加速器,很多大公司都在使用 DPDK 来优化网络性能。

PS:本文所有的图来自网络,侵权必删。

09 DPDK 资料推荐


在我看来,DPDK 最好的学习资料是官网,没有之一:

http://core.dpdk.org/doc/

其次是看 Intel 技术专家出的书 《深入浅出 DPDK》。

本书详细介绍了DPDK 技术发展趋势,数据包处理,硬件加速技术,包处理和虚拟化 ,以及 DPDK 技术在 SDN,NFV ,网络存储等领域的实际应用。

本书是国内第一本全面的阐述网络数据面的核心技术的书籍,面向 IT 网络通讯行业的从业人员,以及大专院校的学生,用通俗易懂的文字打开了一扇通向新一代网络处理架构的大门。

本书我有电子版(但只有一部分,推荐大家买书),需要的公众号后台回复 “DPDK” 查看获取方式。

除了书之外,就是看大牛的博客,加入相关的群和优秀的人一起学习,我整理了几份网上较好的博客资料,和书一起附赠,如果想加群学习,回复 “加群”。

PS:文章未经我允许,不得转载,否则后果自负。

–END–

欢迎扫👇的二维码关注我的微信公众号,后台回复「m」,可以获取往期所有技术博文推送,更多资料回复下列关键字获取。