DPDK学习笔记1--基础概念篇(UIO,大页内存,CPU亲和性,NUMA机制等)

来源:互联网 发布:淄博最专业的seo公司 编辑:程序博客网 时间:2024/04/20 13:17

1.Linux环境下的UIO( Userspace I/O )

UIO 用户空间下驱动程序的支持机制。DPDK使用UIO机制使用网卡驱动程序(主要是Intel的千兆igb和万兆ixgbe驱动程序)运行在用户态,并采用轮询和零拷贝方式从网卡收取报文,提高收发报文的性能。UIO指的是运行在用户空间的I/O技术,是实现用户空间下驱动程序的支撑机制。
Linux系统中一般的驱动设备都是运行在内核空间,而在用户空间用应用程序调用即可,而UIO则是将驱动的很少一部分运行在内核空间,而在用户空间实现驱动的绝大多数功能。使用UIO可以避免设备的驱动程序需要随着内核的更新而更新的问题。由于DPDK是应用层平台,所以与此紧密相连的网卡驱动程序(主要是intel自身的千兆igb与万兆ixgbe驱动程序)都通过uio机制运行在用户态下。
Intel® DPDK 的igb_uio内核模块依赖于内核的UIO。 需要以模块方式编译,可以通过如下命令进行编译sudo /sbin/modprobe uio

2 大内存页(Hugepages)机制

2.1 大页内存介绍

Dpdk使用大页内存的初衷是为包处理的缓冲区分配更大的内存池,降低页表的查询负载,提高TLB的命中率,减少了虚拟页地址到物理页地址的转换时间。

大内存页最好在启动的时候进行分配,这样可以避免物理空间中有太多的碎片。

 

2.2 大页内存分类

Linux操作系统的大页内存,主要分为2M和1G大小。可以从CPU的标识中看出支持大内存页的类型。

如果有 “pse”的标识,说明支持2M的大内存页。  如果有“pdpe1gb”的标识,说明支持1G的大内存页,64位机建议使用1GB的大页。

 

比如如果想设置一个4G的大内存页,而且这个大内存页由4个1G大小的页构成,可以把如下配置传递给内核。

default_hugepagesz=1G hugepagesz=1G hugepages=4

但是如果使用大量的大内存页的话,最好增加当前登录会话打开文件限制,以减少运行DPDK程序时发生的错误。可以使用ulimit的命令,比如ulimit -Sn 2048。

 

在两插槽的NUMA系统中, 大内存页会在两个插槽的CPU中进行平均分配。

echo 1024 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages。

 

对于NUMA系统,需要对每个服务器都进行分配,使用命令

echo 1024 > /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages

echo 1024 > /sys/devices/system/node/node1/hugepages/hugepages-2048kB/nr_hugepages

2.3 大页内存使用

如果已经配置好了大内存页机制,就可以让DPDK利用大内存页的机制了。

可以输入如下的命令

mkdir /mnt/huge

mount -t hugetlbfs nodev /mnt/huge

在运行程序的时候,最好把分配给大页的所有空间都利用起来。如果DPDK的程序在运行的时候传递了-m 或者--socket-mem的参数,大内存页的分配在启动的时候会自动加载。

如果传递给程序的内存页数量比程序要求的要少,也就是内存页不够用,程序将会中止。但是,如果用户要求的内存空间小于预留的内存空间时,同样有可能出现程序崩溃的情况。原因如下,

假设系统为Socket1 和Socket 0 各预留了1024个大内存页。

如果用户需要128M的内存,也就是64个大内存页,但是这64个大页可能有以下的限制:

这64个大页可能都被分配给Socket 1,如果用户要求访问Socket 0的内存,程序中止。为了避免这个问题,尽量使用--socket-mem的选项,而不是-m。

 这些页面可能被分配到物理内存的任何地方,尽管Intel® DPDK EAL会尽力分配临近的内存块,但是还是不可避免的会遇到不临近的情况,在这种情况下可能不能分配大内存池。


socket-mem这个选项是用来为每个CPU的Socket分配特定的内存大小的。比如--socket-mem=0,512,其中0是表示给Socket 0 分配0MB, 512表示给Socket 1分配512MB内存大小。同理对拥有4个Socket的系统,如果要给Socket 0 ,2都分配1GB的内存大小,可以使用下面的选项配置:--socket-mem=1024,0,1024

2.4 大页内存配置脚本

1、创建大页内存挂接点
mkdir /mnt/huge_1GB
mount -t hugetlbfs nodev /mnt/huge_1GB

2、在/etc/fstab文件中加入如下命令,使其重启后有效
nodev /mnt/huge_1GB hugetlbfs pagesize=1GB 0 0

 

3、在CentOS 7.0,修改/etc/grub2.cfg文件中启动菜单的内核参数:
查找关键字"menuentry"启动项,定位到"linux16 /vmlinuz-3.10.0-327.el7.x86_64"

在其末尾添加"default_hugepagesz=1G hugepagesz=1G hugepages=4"(测试发现在hugepages=4一般不会生效!可设置成系统允许的合适的值)

 

4、启动机器后
cat /proc/meminfo|grep Huge   可以看到Hugepagesize已经设置成1GB
如果 HugePages_Total为0,以设置16GB为例
使用sysctl -w vm.nr_hugepages=16 
这样就设置了16GB的大页面,此时HugePages_Total=16

TLB 页表缓冲

其中TLB(Translation Lookaside Buffer)称为页表缓冲,是一个存放着页表缓存(虚拟地址到物理地址的转换表)的内存管理单元,用于改进虚拟地址到物理地址转换速度。

X86体系的系统内存里存放了两级页表,第一级页表称为页目录,第二级称为页表。由于“页表”存储在主存储器中,查询页表所付出的代价很大,由此产生了TLB。

TLB是内存里存放的页表的缓存,那么它里边存放的数据实际上和内存页表区的数据是一致的,在内存的页表区里,每一条记录虚拟页面和物理页框对应关系的记录称之为一个页表条目(Entry),同样地,在TLB里边也缓存了同样大小的页表条目(Entry)。

1:TLB在X86体系的CPU里的实际应用最早是从Intel的486CPU开始的,在X86体系的CPU里边,一般都设有如下4组TLB:

第一组:缓存一般页表(4K字节页面)的指令页表缓存(Instruction-TLB);

第二组:缓存一般页表(4K字节页面)的数据页表缓存(Data-TLB);

第三组:缓存大尺寸页表(2M/4M字节页面)的指令页表缓存(Instruction-TLB);

第四组:缓存大尺寸页表(2M/4M字节页面)的数据页表缓存(Data-TLB);

2:TLB命中和TLB失败

如果TLB中正好存放着所需的页表,则称为TLB命中(TLB Hit);

如果TLB中没有所需的页表,则称为TLB失败(TLB Miss)。

当CPU收到应用程序发来的虚拟地址后,

首先到TLB中查找相应的页表数据,如果TLB中正好存放着所需的页表,则称为TLB命中(TLB Hit)

接下来CPU再依次看TLB中页表所对应的物理内存地址中的数据是不是已经在一级、二级缓存里了,若没有则到内存中取相应地址所存放的数据。

4 CPU物理核,逻辑核

使用DPDK时经常进行CPU的core分配,本小节将对CPU的物理核,处理器,逻辑核的概念进行简单的介绍。

一个物理封装的CPU(通过physical id区分判断)可以有多个核(通过core id区分判断)。而每个核可以有多个逻辑cpu(通过processor区分判断)。一个核通过多个逻辑cpu实现这个核自己的超线程技术。

4.1 物理处理器封装个数

物理处理器封装个数,即俗称的“物理CPU数”.

例如一块“Intel Core i3-2310M”只有1个“物理处理器”。若对于有多个处理器插槽的服务器,“物理处理器”很可能会大于1。

可以使用如下命令查询物理处理器的封装个数。

cat /proc/cpuinfo| grep "physical id"| sort| uniq| wc -l

具有相同physical id的CPU是同一个CPU封装的线程或核心

 

使用lscpu命令可以NUMA架构下CPU node0和 node1的core分配情况。


4.2 处理器核心数

processor cores,即俗称的“CPU核心数”,也就是每个物理CPU中core的个数

例如“Intel Core i3-2310M”是双核处理器,它有2个“处理器核心数”。

可以通过以下的命令来查看:

cat /proc/cpuinfo| grep "cpu cores"| uniq

具有相同core id的CPU是同一个core的超线程


4.3 逻辑处理器数

逻辑处理器数英文名是logical processors,即俗称的“逻辑CPU数”,

逻辑核心处理器,就是虚拟物理核心处理器的一个超线程技术

例如“Intel Core i3-2310M”支持超线程,一个物理核心能模拟为两个逻辑处理器,即一块“Intel Core i3-2310M”有4个“逻辑处理器数”。

可以使用

cat /proc/cpuinfo| grep "processor"| wc -l


4.4 SIBLING

SIBLING是内核认为的单个物理处理器所有的超线程个数,也就是一个物理封装中的逻辑核的个数。

如果SIBLING等于实际物理核数的话,就说明没有启动超线程,反之启用超线程。

也就是说使用cat /proc/cpu命令(这条指令在centos7上无法执行)

l 如果“siblings”和“cpu cores”一致,则说明不支持超线程,或者超线程未打开。

l 如果“siblings”是“cpu cores”的两倍,则说明支持超线程,并且超线程已打开

 

 

图中,“siblings”是“cpu cores”的两倍,则说明支持超线程。


4.5 超线程

超线程全名为Hyper-Threading,利用特殊的硬件指令,把两个逻辑内核模拟成两个物理芯片,让单个处理器都能使用线程级并行计算,进而兼容多线程操作系统和软件,减少了CPU的闲置时间,提高的CPU的运行速度。

采用超线程即是可在同一时间里,应用程序可以使用芯片的不同部分。

虽然单线程芯片每秒钟能够处理成千上万条指令,但是在任一时刻只能够对一条指令进行操作。而超线程技术可以使芯片同时进行多线程处理,使芯片性能得到提升。

虽然采用超线程技术能同时执行两个线程,但它并不象两个真正的CPU那样,每个CPU都具有独立的资源。当两个线程都同时需要某一个资源时,其中一个要暂时停止,并让出资源,直到这些资源闲置后才能继续。因此超线程的性能并不等于两颗CPU的性能。

如果有两个逻辑CPU具有相同的”core id”,那么超线程是打开的。


4.6 CPU亲和性

CPU的亲和性也就是cpu affinity机制,指的是进程要在指定的CPU 上尽量长时间地运行而不被迁移到其他处理器,通过处理器关联可以将虚拟处理器映射到一个或多个物理处理器上 ,也就是说把一个程序绑定到一个物理CPU上。

在越来越多核心的cpu机器上,提高工作效率的最直观做法就是让各个cpu核心各自干专门的事情。

而且在多核运行的机器上,每个CPU本身自己会有缓存,缓存着进程使用的信息,而进程可能会被OS调度到其他CPU上,如此,CPU cache命中率就低了。当一个进程或线程绑定CPU后,程序就会一直在指定的cpu跑,不会由操作系统调度到其他CPU上,

减少了cpu的缓存,也减少了进程或者线程的上下文切换,从而提高性能和效率。

 

另一种场景使用绑核考虑,是将重要的业务进程隔离开,对于部分实时进程调度优先级高,可以将其绑定到一个指定核上,既可以保证实时进程的调度,也可以避免其他CPU上进程被该实时进程干扰。

 

DPDK利用cpu affinity主要是将控制面线程以及各个数据面线程绑定到不同的cpu,省却了来回反复调度的性能消耗,线程之间互不干扰的完成工作。


4.7 NUMA机制

查看系统是否支持NUMA机制

grep -i numa /var/log/dmesg 

 

NUMA即”非一致性内存访问”,Linux提供了一个命令numactl,可以用来查看系统的numa状态.


万丈高楼平地起,基础概念的理解很重要,对后面的学习能起到事半功倍的效果
基础概念整理文档的下载地址为:


原创粉丝点击