Linux平台上DPDK入门指南:快速提升网络性能的利器

Linux平台上DPDK入门指南:快速提升网络性能的利器通过 pkg config 以及 libdpdk pc 的帮助 不必在 Makefile 文件里费心思写 CFLAGS LDFLAGS 等东西 而是让 pkg config 自动生成 DPDK 应用程序的 Makefile 一般都类

大家好,欢迎来到IT知识分享网。

一、DPDK简介

2008年,DPDK由英特尔公司的网络通信部门提出,主要是针对基于Intel的处理器和网卡开发。正如其全称(Data Plane Development Kit,数据平面开发套件),DPDK提供丰富、完整的框架,让CPU快速实现数据平面应用的数据包处理,高效完成网络转发等工作。

说到这里,有必要解释一下数据平面的概念。在通信框架中,数据传输和连接管理通常被拆分为多个独立的操作,这些操作被称为“平面”,平面包括管理平面、数据平面、控制平面。在传统网络,这些平面都在路由器和交换机的固件中实现,管理平面负责为网络堆栈各个层级和网络系统的其他部分提供管理、监控和配置服务;控制平面决定流量的传输路径;数据平面又称为用户平面,承载用户流量,并负责接口间的数据包转发。

二、系统要求

本章描述了编译DPDK所需的软件包。注意:假如在Intel公司的89xx通信芯片组平台上使用DPDK,请参阅文档 Intel® Communications Chipset 89xx Series Software for Linux Getting Started Guide。

2.1X86上预先设置BIOS

对大多数平台,使用基本DPDK功能无需对BIOS进行特殊设置。然而,对于HPET定时器和电源管理功能,以及为了获得40G网卡上小包处理的高性能,则可能需要更改BIOS设置。可以参阅章节 Enabling Additional Functionality 以获取更为详细的信息。

2.2编译DPDK

工具集:

注意:以下说明在Fedora 18上通过了测试。其他系统所需要的安装命令和软件包可能有所不同。有关其他Linux发行版本和测试版本的详细信息,请参阅DPDK发布说明。

GNU make。

coreutils: cmp, sed, grep, arch 等

gcc: 4.9以上的版本适用于所有的平台。 在某些发布版本中,启用了一些特定的编译器标志和链接标志(例如-fstack-protector)。请参阅文档的发布版本和 gcc -dumpspecs.

libc 头文件,通常打包成 gcc-multilib (glibc-devel.i686 / libc6-dev-i386; glibc-devel.x86_64 / libc6-dev 用于Intel 64位架构编译; glibc-devel.ppc64 用于IBM 64位架构编译;)

构建Linux内核模块所需要的头文件和源文件。(kernel – devel.x86_64; kernel – devel.ppc64)
在64位系统上编译32位软件包额外需要的软件为:

  • glibc.i686, libgcc.i686, libstdc++.i686 及 glibc-devel.i686, 适用于Intel的i686/x86_64;
  • glibc.ppc64, libgcc.ppc64, libstdc++.ppc64 及 glibc-devel.ppc64 适用于 IBM ppc_64;
    注意:x86_x32 ABI目前仅在Ubuntu 13.10及以上版本或者Debian最近的发行版本上支持。编译器必须是gcc 4.9+版本。 Python, 2.7+ or 3.2+版本, 用于运行DPDK软件包中的各种帮助脚本。

可选工具:

Intel® C++ Compiler (icc). 安装icc可能需要额外的库,请参阅编译器安装目录下的icc安装指南。

IBM® Advance ToolChain for Powerlinux. 这是一组开源开发工具和运行库。允许用户在Linux上使用IBM最新POWER硬件的优势。具体安装请参阅IBM的官方安装文档。

libpcap 头文件和库 (libpcap-devel) ,用于编译和使用基于libcap的轮询模式驱动程序。默认情况下,该驱动程序被禁用,可以通过在构建时修改配置文件 CONFIG_RTE_LIBRTE_PMD_PCAP=y 来开启。

需要使用libarchive 头文件和库来进行某些使用tar获取资源的单元测试。

2.3运行DPDK应用程序

要运行DPDK应用程序,需要在目标机器上进行某些定制。

系统软件

需求:

  • Kernel version >= 2.6.34, 当前内核版本可以通过命令查看:
uname -r

glibc >= 2.7 (方便使用cpuset相关特性), 版本信息通命令 ldd –version 查看。
Kernel configuration, 在 Fedora OS 及其他常见的发行版本中,如 Ubuntu 或 Red Hat Enterprise Linux,供应商提供的配置可以运行大多数的DPDK应用程序。


对于其他内核构件,应为DPDK开启的选项包括:

UIO 支持

HUGETLBFS 支持

PROC_PAGE_MONITOR 支持

如果需要HPET支持,还应开启 HPET and HPET_MMAP 配置选项。有关信息参考 High Precision Event Timer (HPET) Functionality 章节获取更多信息。

在 Linux 环境中使用 Hugepages

用于数据包缓冲区的大型内存池分配需要 Hugepages 支持(如上节所述,必须在运行的内核中开启 HUGETLBFS 选项)。通过使用大页分配,程序需要更少的页面,性能增加,因为较少的TLB减少了将虚拟页面地址翻译成物理页面地址所需的时间。如果没有大页,标准大小4k的页面会导致频繁的TLB miss,性能下降。

预留Hugepages给DPDK使用

大页分配应该在系统引导时或者启动后尽快完成,以避免物理内存碎片化。要在引导时预留大页,需要给Linux内核命令行传递一个参数。对于2MB大小的页面,只需要将hugepages选项传递给内核。如,预留1024个2MB大小的page,使用:

hugepages=1024

对于其他大小的hugepage,例如1G的页,大小必须同时指定。例如,要预留4个1G大小的页面给程序,需要传递以下选项给内核:

default_hugepagesz=1G hugepagesz=1G hugepages=4

注意:CPU支持的hugepage大小可以从Intel架构上的CPU标志位确定。如果存在pse,则支持2Mhugepages,如果page1gb存在,则支持1G的hugepages。在IBM Power架构中,支持的hugepage大小为16MB和16GB。

注意:对于64位程序,如果平台支持,建议使用1GB的hugepages在双插槽NUMA的系统上,在启动时预留的hugepage数目通常在两个插槽之间评分(假设两个插槽上都有足够的内存)。

有关这些和其他内核选项的信息,请参阅Linux源代码目录中/kernel-parameter.txt文件。

特例:对于2MB页面,还可以在系统启动之后再分配,通过向 /sys/devices/ 目录下的nr_hugepages文件写入hugepage数目来实现。 对于单节点系统,使用的命令如下(假设需要1024个页):

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

注意:对于1G页面,系统启动之后无法预留页面内存。

DPDK使用Hugepages

一旦预留了hugepage内存,为了使内存可用于DPDK,请执行以下步骤:

mkdir /mnt/huge mount -t hugetlbfs nodev /mnt/huge

通过将一下命令添加到 /etc/fstab 文件中,安装点可以在重启时永久保存:

nodev /mnt/huge hugetlbfs defaults 0 0

对于1GB内存,页面大小必须在安装选项中指定:

nodev /mnt/huge_1GB hugetlbfs pagesize=1GB 0 0

Linux环境中Xen Domain0支持

现有的内存管理实现是基于Linux内核的hugepage机制。在Xen虚拟机管理程序中,对于DomainU客户端的支持意味着DPDK程序与客户一样正常工作。

但是,Domain0不支持hugepages。为了解决这个限制,添加了一个新的内核模块rte_dom0_mm用于方便内存的分配和映射,通过 IOCTL (分配) 和 MMAP (映射)。

DPDK中使能Xen Dom0模式

默认情况下,DPDK构建时禁止使用Xen Dom0模式。要支持Xen Dom0,CONFIG_RTE_LIBRTE_XEN_DOM0设置应该改为 “y”,编译时弃用该模式。
此外,为了允许接收错误套接字ID,CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID也必须设置为 “y”。

加载DPDK rte_dom0_mm模块

要在Xen Dom0下运行任何DPDK应用程序,必须使用rsv_memsize选项将rte_dom0_mm 模块加载到运行的内核中。该模块位于DPDK目标目录的kmod子目录中。应该使用insmod命令加载此模块,如下所示:

sudo insmod kmod/rte_dom0_mm.ko rsv_memsize=X

X的值不能大于4096(MB)。

配置内存用于DPDK使用

在加载rte_dom0_mm.ko内核模块之后,用户必须配置DPDK使用的内存大小。这也是通过将内存大小写入到目录 /sys/devices/ 下的文件memsize中来实现的。 使用以下命令(假设需要2048MB):

echo 2048 > /sys/kernel/mm/dom0-mm/memsize-mB/memsize

用户还可以使用下面命令检查已经使用了多少内存:

cat /sys/kernel/mm/dom0-mm/memsize-mB/memsize_rsvd

Xen Domain0不支持NUMA配置,因此 –socket-mem 命令选项对Xen Domain0无效。

注意:memsize的值不能大于rsv_memsize。

在Xen Domain0上运行DPDK程序

要在Xen Domain0上运行DPDK程序,需要一个额外的命令行选项 –xen-dom0。

还不熟悉的朋友,这里可以先领取一份dpdk新手学习资料包(入坑不亏):

Linux平台上DPDK入门指南:快速提升网络性能的利器

DPDK技术交流群(),后台私信【DPDK】自行获取。

Dpdk/网络协议栈/vpp/OvS/DDos/NFV/虚拟化/高性能专家-学习视频教程-腾讯课堂

三、使用源码编译DPDK目标文件

注意:这个过程的部分工作可以通过章节“使用脚本快速构建”描述的脚本来实现。

3.1安装DPDK及源码

首先,解压文件并进入到DPDK源文件根目录下:

tar xJf dpdk-<version>.tar.xz cd dpdk-<version>

DPDK源文件由几个目录组成:

  • lib: DPDK 库文件
  • drivers: DPDK 轮询驱动源文件
  • app: DPDK 应用程序 (自动测试)源文件
  • examples: DPDK 应用例程
  • config, buildtools, mk: 框架相关的makefile、脚本及配置文件

3.2DPDK目标环境安装

DPDK目标文件的格式为:

ARCH-MACHINE-EXECENV-TOOLCHAIN

其中:

  • ARCH 可以是: i686, x86_64, ppc_64
  • MACHINE 可以是: native, power8
  • EXECENV 可以是: linuxapp, bsdapp
  • TOOLCHAIN 可以是: gcc, icc
    目标文件取决于运行环境是32位还是64位设备。可以在DPDK的 /config 目录中找到可用的目标,不能使用defconfig_前缀。


注意:配置文件根据 RTE_MACHINE 优化级别不同分别提供。在配置文件内部,RTE_MACHINE 配置为 native,意味着已编译的软件被调整到其构建的平台上。有关此设置的更多信息,请参阅 DPDK 编程指南。
当使用Intel® C++ 编译器 (icc)时,对64位和32位,需要使用以下命令进行调整。 注意,shell脚本会更新 $PATH 值,因此不能再同一个会话中执行。 此外,还应该检查编译器的安装目录,因为可能不同。

source /opt/intel/bin/iccvars.sh intel64 source /opt/intel/bin/iccvars.sh ia32

在顶级目录中使用 make install T=<target> 来生成目标文件。

例如,为了使用icc编译生成64位目标文件,运行如下命令:

make install T=x86_64-native-linuxapp-icc

为了使用gcc编译生成32位目标文件,命令如下:

make install T=i686-native-linuxapp-gcc

如果仅仅只是生成目标文件,并不运行,比如,配置文件改变需要重新编译,使用 make config T=<target> 命令:

make config T=x86_64-native-linuxapp-gcc

注意:任何需要运行的内核模块,如 igb_uio, kni, 必须在与目标文件编译相同的内核下进行编译。如果DPDK未在目标设备上构建,则应使用 RTE_KERNELDIR 环境变量将编译指向要在目标机上使用的内核版本的副本(交叉编译的内核版本)。

创建目标环境之后,用户可以移动到目标环境目录,并继续更改代码并编译。用户还可以通过编辑build目录中的.config文件对DPDK配置进行修改。 (这是顶级目录中defconfig文件的本地副本)。

cd x86_64-native-linuxapp-gcc vi .config make

此外,make clean命令可以用于删除任何现有的编译文件,以便后续完整、干净地重新编译代码。

3.3Browsing the Installed DPDK Environment Target

一旦目标文件本创建,它就包含了构建客户应用程序所需的DPDK环境的所有库,包括轮询驱动程序和头文件。 此外,test和testpmd应用程序构建在build/app目录下,可以用于测试。 还有一个kmod目录,存放可能需要加载的内核模块。

3.4加载模块启动DPDK环境需要的UIO功能

要运行任何的DPDK应用程序,需要将合适的uio模块线加载到当前内核中。在多数情况下,Linux内核包含了标准的 uio_pci_generic 模块就可以提供uio能力。 该模块可以使用命令加载

sudo modprobe uio_pci_generic

区别于 uio_pci_generic ,DPDK提供了一个igb_uio模块(可以在kmod目录下找到)。可以通过如下方式加载:

sudo modprobe uio sudo insmod kmod/igb_uio.ko

注意:对于一下不支持传统中断的设备,例如虚拟功能(VF)设备,必须使用 igb_uio 来替代 uio_pci_generic 模块。由于DPDK 1.7版本提供VFIO支持,所以,对于支持VFIO的平台,可选则UIO,也可以不用。

3.5加载VFIO模块

DPDK程序选择使用VFIO时,需要加载 vfio-pci 模块:

sudo modprobe vfio-pci

注意,要使用VFIO,首先,你的平台内核版本必须支持VFIO功能。 Linux内核从3.6.0版本之后就一直包含VFIO模块,通常是默认存在的。不够请查询发行文档以确认是否存在。

此外,要使用VFIO,内核和BIOS都必须支持,并配置为使用IO虚拟化 (如 Intel® VT-d)。为了保证非特权用户运行DPDK时能够正确操作VFIO,还应设置正确的权限。这可以通过DPDK的配置脚本(dpdk-setup.sh文件位于usertools目录中)。

3.6网络端口绑定/解绑定到内核去顶模块

从版本1.4开始,DPDK应用程序不再自动解除所有网络端口与原先内核驱动模块的绑定关系。 相反的,DPDK程序在运行前,需要将所要使用的端口绑定到 uio_pci_generic, igb_uio 或 vfio-pci 模块上。 任何Linux内核本身控制的端口无法被DPDK PMD驱动所使用。

注意:默认情况下,DPDK将在启动时不再自动解绑定内核模块与端口的关系。DPDK应用程序使用的任何端口必须与Linux无关,并绑定到 uio_pci_generic, igb_uio 或 vfio-pci 模块上。

将端口从Linux内核解绑,然后绑定到 uio_pci_generic, igb_uio 或 vfio-pci 模块上供DPDK使用,可以使用脚本dpdk_nic _bind.py(位于usertools目录下)。 这个工具可以用于提供当前系统上网络接口的状态图,绑定或解绑定来自不同内核模块的接口。 以下是脚本如何使用的一些实例。通过使用 –help or –usage 选项调用脚本,可以获得脚本的完整描述与帮助信息。 请注意,要将接口绑定到uio或vfio的话,需要先将这两个模块加载到内核,再运行 dpdk-devbind.py 脚本。

注意:由于VFIO的工作方式,设备是否可用VFIO是有明确限制的。大部分是由IOMMU组的功能决定的。
任何的虚拟设备可以独立使用VFIO,但是物理设备则要求将所有端口绑定到VFIO,或者其中一些绑定到VFIO,而其他端口不能绑定到任何其他驱动程序。

如果你的设备位于PCI-to-PCI桥之后,桥接器将成为设备所在的IOMMU组的一部分。因此,桥接驱动程序也应该从端口解绑定。

注意:虽然任何用户都可以运行dpdk-devbind.py脚本来查看网络接口的状态,但是绑定和解绑定则需要root权限。查看系统中所有网络接口的状态:

./usertools/dpdk-devbind.py --status Network devices using DPDK-compatible driver ============================================ 0000:82:00.0 '82599EB 10-GbE NIC' drv=uio_pci_generic unused=ixgbe 0000:82:00.1 '82599EB 10-GbE NIC' drv=uio_pci_generic unused=ixgbe Network devices using kernel driver =================================== 0000:04:00.0 'I350 1-GbE NIC' if=em0 drv=igb unused=uio_pci_generic *Active* 0000:04:00.1 'I350 1-GbE NIC' if=eth1 drv=igb unused=uio_pci_generic 0000:04:00.2 'I350 1-GbE NIC' if=eth2 drv=igb unused=uio_pci_generic 0000:04:00.3 'I350 1-GbE NIC' if=eth3 drv=igb unused=uio_pci_generic Other network devices ===================== <none>

绑定设备 eth1,04:00.1, 到 uio_pci_generic 驱动:

./usertools/dpdk-devbind.py --bind=uio_pci_generic 04:00.1 或者 ./usertools/dpdk-devbind.py --bind=uio_pci_generic eth1

恢复设备 82:00.0 到Linux内核绑定状态:

./usertools/dpdk-devbind.py --bind=ixgbe 82:00.0

四、编译和运行简单应用程序

本章介绍如何在DPDK环境下编译和运行应用程序。还指出应用程序的存储位置。注意:此过程的部分操作也可以使用脚本来完成。参考 使用脚本快速构建 章节描述。

4.1编译一个简单应用程序

一个DPDK目标环境创建完成时(如 x86_64-native-linuxapp-gcc),它包含编译一个应用程序所需要的全部库和头文件。

当在Linux* 交叉环境中编译应用程序时,以下变量需要预先导出:

  • RTE_SDK – 指向DPDK安装目录。
  • RTE_TARGET – 指向DPDK目标环境目录。

以下是创建helloworld应用程序实例,该实例将在DPDK Linux环境中运行。这个实例可以在目录${RTE_SDK}/examples找到。

该目录包含 main.c 文件。该文件与DPDK目标环境中的库结合使用时,调用各种函数初始化DPDK环境,然后,为每个要使用的core启动一个入口点(调度应用程序)。 默认情况下,二进制文件存储在build目录中。

cd examples/helloworld/ export RTE_SDK=$HOME/DPDK export RTE_TARGET=x86_64-native-linuxapp-gcc make CC main.o LD helloworld INSTALL-APP helloworld INSTALL-MAP helloworld.map ls build/app helloworld helloworld.map

注意:在上面的例子中, helloworld 是在DPDK的目录结构下的。 当然,也可以将其放在DPDK目录之外,以保证DPDK的结构不变。 下面的例子, helloworld 应用程序被复制到一个新的目录下。

export RTE_SDK=/home/user/DPDK cp -r $(RTE_SDK)/examples/helloworld my_rte_app cd my_rte_app/ export RTE_TARGET=x86_64-native-linuxapp-gcc make CC main.o LD helloworld INSTALL-APP helloworld INSTALL-MAP helloworld.map

4.2运行一个简单的应用程序

注意: UIO驱动和hugepage必须在程序运行前设置好。
注意:应用程序使用的任何端口,必须绑定到合适的内核驱动模块上,如章节 网络端口绑定/解绑定到内核去顶模块 描述的那样。应用程序与DPDK目标环境的环境抽象层(EAL)库相关联,该库提供了所有DPDK程序通用的一些选项。

以下是EAL提供的一些选项列表:

./rte-app -c COREMASK [-n NUM] [-b <domain:bus:devid.func>] \ [--socket-mem=MB,...] [-m MB] [-r NUM] [-v] [--file-prefix] \ [--proc-type <primary|secondary|auto>] [-- xen-dom0]

选项描述如下:

  • -c COREMASK: 要运行的内核的十六进制掩码。注意,平台之间编号可能不同,需要事先确定。
  • -n NUM: 每个处理器插槽的内存通道数目。
  • -b <domain:bus:devid.func>: 端口黑名单,避免EAL使用指定的PCI设备。
  • –use-device: 仅使用指定的以太网设备。使用逗号分隔 [domain:]bus:devid.func 值,不能与 -b 选项一起使用。
  • –socket-mem: 从特定插槽上的hugepage分配内存。
  • -m MB: 内存从hugepage分配,不管处理器插槽。建议使用 –socket-mem 而非这个选项。
  • -r NUM: 内存数量。
  • -v: 显示启动时的版本信息。
  • –huge-dir: 挂载hugetlbfs的目录。
  • –file-prefix: 用于hugepage文件名的前缀文本。
  • –proc-type: 程序实例的类型。
  • –xen-dom0: 支持在Xen Domain0上运行,但不具有hugetlbfs的程序。
  • –vmware-tsc-map: 使用VMware TSC 映射而不是本地RDTSC。
  • –base-virtaddr: 指定基本虚拟地址。
  • –vfio-intr: 指定要由VFIO使用的中断类型。(如果不支持VFIO,则配置无效)。
    其中 -c 是强制性的,其他为可选配置。

将DPDK应用程序二进制文件拷贝到目标设备,按照如下命令运行(我们假设每个平台处理器有4个内存通道,并且存在core0~3用于运行程序):

./helloworld -c f -n 4

注意:选项 –proc-type 和 –file-prefix 用于运行多个DPDK进程。请参阅 “多应用程序实例” 章节及 DPDK 编程指南 获取更多细节。

应用程序使用的逻辑Core

对于DPDK应用程序,coremask参数始终是必须的。掩码的每个位对应于Linux提供的逻辑core ID。 由于这些逻辑core的编号,以及他们在NUMA插槽上的映射可能因平台而异,因此建议在选择每种情况下使用的coremaks时,都要考虑每个平台的core布局。

在DPDK程序初始化EAL层时,将显示要使用的逻辑core及其插槽位置。可以通过读取 /proc/cpuinfo 文件来获取系统上所有core的信息。例如执行 cat /proc/cpuinfo。 列出来的physical id 属性表示其所属的CPU插槽。当使用了其他处理器来了解逻辑core到插槽的映射时,这些信息很有用。

注意:可以使用另一个Linux工具 lstopo 来获取逻辑core布局的图形化信息。在Fedora Linux上, 可以通过如下命令安装并运行工具:

sudo yum install hwloc ./lstopo

注意:逻辑core在不同的电路板上可能不同,在应用程序使用coremaks时需要先确定。

应用程序使用的Hugepage内存

当运行应用程序时,建议使用的内存与hugepage预留的内存一致。如果运行时没有 -m 或 –socket-mem 参数传入,这由DPDK应用程序在启动时自动完成。

如果通过显示传入 -m 或 –socket-mem 值,但是请求的内存超过了该值,应用程序将执行失败。 但是,如果用户请求的内存小于预留的hugepage-memory,应用程序也会失败,特别是当使用了 -m 选项的时候。 因为,假设系统在插槽0和插槽1上有1024个预留的2MB页面,如果用户请求128 MB的内存,可能存在64个页不符合要求的情况:

内核只能在插槽1中将hugepage-memory提供给应用程序。在这种情况下,如果应用程序尝试创建一个插槽0中的对象,例如ring或者内存池,那么将执行失败 为了避免这个问题,建议使用 –socket-mem 选项替代 -m 选项。

这些页面可能位于物理内存中的任意位置,尽管DPDK EAL将尝试在连续的内存块中分配内存,但是页面可能是不连续的。在这种情况下,应用程序无法分配大内存。

使用socket-mem选项可以为特定的插槽请求特定大小的内存。通过提供 –socket-mem 标志和每个插槽需要的内存数量来实现的,如 –socket-mem=0,512 用于在插槽1上预留512MB内存。 类似的,在4插槽系统上,如果只能在插槽0和2上分配1GB内存,则可以使用参数–socket-mem=1024,0,1024 来实现。 如果DPDK无法在每个插槽上分配足够的内存,则EAL初始化失败。

五、DPDK开发环境

DPDK工程创建要求Linux环境及相关的工具链,例如一个或多个编译工具、汇编程序、make工具、编辑器及DPDK组件用到的库。

当指定环境和架构的库编译出来时,这些库就可以用于创建我们自己的数据面处理程序。创建Linux用户空间应用程序时,需要用到glibc库。对于DPDK应用程序,必须使用两个全局环境变量(RTE_SDK和RTE_TARGET),这两个变量需要在编译应用程序之前配置好:

export RTE_SDK=/home/user/DPDK export RTE_TARGET=x86_64-native-linuxapp-gcc

5.1环境适配层

环境适配层为应用程序和库提供了通用的接口,隐藏了底层环境细节。EAL提供的服务有

  • DPDK的加载和启动
  • 多线程和多进程执行方式支持
  • CPU亲和性设置
  • 系统内存分配和释放
  • 原子操作和锁操作
  • 定时器引用
  • PCI总线访问
  • 跟踪调试功能
  • CPU特性识别
  • 中断处理
  • 警告操作
  • 内存管理

5.2核心组件

核心组件指的是一系列库,用于为高性能包处理程序提供所有必须的元素。核心组件及其之间的关系如下图所示:

Linux平台上DPDK入门指南:快速提升网络性能的利器

环形缓冲区管理(librte_ring)

Ring数据结构提供了一个无锁的多生产者,多消费者的FIFO表处理接口。相对于无锁队列来讲,它容易部署,适合大量的操作,而且更快。Ring库在“内存池库(librte_mempool)”中使用,而且,ring还用于不同逻辑核上处理单元之间的通信。

内存池管理(librte_mempool)

内存池管理的主要职责就是在内存中分配指定数目对象的Pool。每个Pool以名称来唯一标识,并且使用一个Ring来存储空闲的对象节点。它还提供了一些其他的服务,如对象节点的每核缓存备份,及自动对齐以保证对象能够均衡分布到内存通道上。

网络报文缓冲区管理(librte_mbuf)

报文缓冲区库提供了创建和销毁报文缓冲区的能力,DPDK应用程序中使用这些缓冲区来存储消息。这些缓冲区通常在程序开始时通过DPDK的内存池库(librte_mempool)申请并存储在内存池中。缓冲区库(librte_mbuf)提供了报文申请和释放的API,通常情况下,消息Buffer用于缓存消息,报文Buffer用于缓存网络报文。

定时器管理(librte_timer)

这个库位DPDK的执行单元提供了定时器服务,为函数异步执行提供支持。定时器可以设置成周期调用,或者只调用一次。使用EAL提供的接口可以获取高精度时钟,并且能在每个核上根据需要初始化。

以太网轮询模式驱动架构

DPDK的PMD驱动支持1G、10G、40G。 同时DPDK提供了虚拟的以太网控制器,被设计成非异步,基于中断信号的模式。

报文转发算法支持

DPDK提供了哈希(librte_hash)、最长前缀匹配(librte_lpm)算法库用于支持相应的分组转发算法。

网络库(librte_net)

这个库包括IP协议的一些定义及常见的宏定义。这些定义都是基于FreeBSD*中IP协议栈的代码,包括协议号(用于IP头部)、IP相关的宏、IPv4/IPv6头部结构体以及TCP、UDP和STCP头部结构体。

六、DPDK实战

dpdk环境搭建+创建dpdk项目,并连接dpdk库

6.1新的编译方式

DPDK 较新版本已经支持 meson+ninja 的编译方式, 而在 20.11 应该就要抛弃上述老的编译方式. 其中我个人理解, meson 相当于 CMake, ninja 相当于 make.

依赖项

有些依赖项是和编译方式没有关系的, 比如对 make, gcc, Python, NUMA库等的依赖

如果使用新编译方式, 需要依赖:

meson 0.47.1+ ninja 1.7+

通过系统包管理器也可以安装 meson/ninja, 但一般版本很老. 比如 Ubuntu 16.04 用 apt 安装的话 meson 版本是 0.29.0, 远不能满足需求. 这时可以使用 pip3 来安装:

$ sudo pip3 install meson $ sudo pip3 install ninja

6.2编译DPDK

与 CMake 类似, 调用 meson 跟一个编译输出目录, 这目录可以放在源码目录之外, 避免污染. 本文中我把它设置为源码上一级目录的 dpdk_build/ 目录.

dpdk源码下载再地址:DPDK

进入 DPDK 源码主目录, 运行:

$ meson <options> ../dpdk_build

输出:

zzq@ubuntu16:~/dev/dpdk (main) $meson ../dpdk_build The Meson build system Version: 0.55.1 Source dir: /home/zzq/dev/dpdk Build dir: /home/zzq/dev/dpdk_build Build type: native build Program cat found: YES Project name: DPDK Project version: 20.11.0-rc0 C compiler for the host machine: cc (gcc 5.4.0 "cc (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 ") C linker for the host machine: cc ld.bfd 2.26.1 Host machine cpu family: x86_64 Host machine cpu: x86_64 Program pkg-config found: YES Program gen-pmdinfo-cfile.sh found: YES Program list-dir-globs.py found: YES ... Build targets in project: 929 Found ninja-1.10.0.git.kitware.jobserver-1 at /home/zzq/.local/bin/ninja

这里可以在 <options> 中设置编译选项, 也可以在运行此命令后在 ../dpdk_build 中再进行设置. 详见下文:编译选项的设置. 设置好编译选项后, 在 ../dpdk_build 目录开始编译:

$ cd ../dpdk_build/ $ meson configure -Dbuildtype=debug -Dexamples=l3fwd-graph $ ninja

成功编译后编译输出目录内容如下:

zzq@ubuntu16:~/dev/dpdk_build $ ls app/ config/ lib/ meson-uninstalled/ build.ninja doc/ meson-info/ rte_build_config.h buildtools/ drivers/ meson-logs/ usertools/ compile_commands.json examples/ meson-private/

编译成功后, 可以进行安装, 以便后续使用, 此时需要 root 权限。

$ sudo ninja install $ sudo ldconfig

在centos8下,并不会链接成功,可以手动将dpdk的lib目录链接到ldconfig下

linux 下有两种添加加载库路径的方式:

1.修改环境变量:    #export LD_LIBRARY_PATH=path_name 2.修改配置文件   修改  /etc/ld.so.conf 的内容在最后添加库加载的新的路径即可.   最后执行: #ldconfig 使配置生效. 目前使用的是第二种方案: 在目录/etc/ld.so.conf.d/下新建文件:dpdk-ling.conf 文件内容: [root@localhost build]# cat /etc/ld.so.conf.d/dpdk-ling.conf /usr/local/src/dpdk/dpdk-20.11/build/lib [root@localhost build]# 

设置库查找路径

ldconfig

默认会安装在 /usr/local/ 目录, 其中库文件会在 /usr/local/lib/x86_64-linux-gnu/ 下面, 执行 ldconfig 是为了让 ld.so 更新 cache, 这样依赖到它的应用程序运行时就可以找到它了。

在某些系统上, 如 Fedora/Redhat, /usr/local 并不在默认路径中, 这需要在运行 ldconfig 之前把库路径添加到 /etc/ld.so.conf.d/ 中的某个文件里 (比如, dpdk.conf)

可以通过ldconfig -p来检查安装后的 DPDK 库是不是已经在 cache 里了:

执行将上述dpdk的lib目录写入到ldconf配置中,可以用以下确认: $ ldconfig -p | grep librte | wc -l 286 $ ldconfig -p | grep librte_graph librte_graph.so.21 (libc6,x86-64) => /usr/local/lib/x86_64-linux-gnu/librte_graph.so.21 librte_graph.so (libc6,x86-64) => /usr/local/lib/x86_64-linux-gnu/librte_graph.so

pkg-config

后续如果用 meson 编译 DPDK 程序, 默认会使用 pkg-config 来寻找 DPDK 库, 而 DPDK 安装后会把对应的 libdpdk.pc 安装在某个目录, 如 /usr/local/lib/x86_64-linux-gnu/pkgconfig/libdpdk.pc, 我们需要确保这个路径 pkg-config 可以找到。

$pkg-config --variable pc_path pkg-config /usr/local/lib/x86_64-linux-gnu/pkgconfig: /usr/local/lib/pkgconfig: /usr/local/share/pkgconfig: /usr/lib/x86_64-linux-gnu/pkgconfig: /usr/lib/pkgconfig: /usr/share/pkgconfig

而在 CentOS 上, libdpdk.pc 的路径是/usr/local/lib64/pkgconfig/libdpdk.pc, 而 pkg-config 的默认搜索路径是:

$pkg-config --variable pc_path pkg-config /usr/lib64/pkgconfig: /usr/share/pkgconfig

可以看到 libdpdk.pc 路径并不在查找路径中, 所以需要添加, 方法之一:

$export PKG_CONFIG_PATH=/usr/local/lib64/pkgconfig

最终, 可通过以下命令检查 pkg-config 设置是否正确, 如果正确会显示版本号:

$pkg-config --modversion libdpdk 20.11.0-rc5

失败则会显示以下类似信息:

$pkg-config --modversion libdpdk Package libdpdk was not found in the pkg-config search path. Perhaps you should add the directory containing `libdpdk.pc' to the PKG_CONFIG_PATH environment variable No package 'libdpdk' found

当在 meson 中使用此路径时, 可直接通过选项设置, 如:

$meson –pkg-config-path=/usr/local/lib64/pkgconfig <BUILD>

设置编译选项

在新的编译系统里, 不是通过修改 config/ 下面的文件来修改配置, 而是通过指定编译选项. 编译选项有两种指定方式:

  • meson <options> ../dpdk_build 中的 options 里指定
  • 执行完 meson ../dpdk_build 之后, 在生成的目录 ../dpdk_build 里执行 meson configure <options> , 在 options 里指定

两种设置方式的语法都是 -D<option> , 比如:

  • 修改编译版本 -Dbuildtype=debug
  • 修改最大 lcore 数定义 -Dmax_lcores=256

其中第一种方式还支持 –<option> 语法, 如

meson –buildtype=debug ../dpdk_build

DPDK 内置了很多程序示例(examples), 默认不会编译, 如果想要编译某个示例, 比如 l3fwd 和 ip_reassembly, 语法是: -Dexamples=l3fwd,ip_reassembly , 多个示例名用逗号分开. 也可以通过 -Dexamples=all 来编译所有当前编译选项所支持的示例程序.

6.3编译DPDK应用程序

有两种方式. 下文通过把 DPDK 自带的示例 examples/helloworld 移到一个外部目录进行编译来说明.

当动态链接某些版本的 DPDK 库时, 可能会出现运行时找不到网口的错误, 但明明已经绑定了网卡. 这是因为在外部编译的 DPDK 程序没有链接相关的 PMD 驱动库, 问题类似于: https://www.yuque.com/zzqcn/opensource/wdr49w 不依赖 PMD 驱动的 DPDK 程序不受此影响, 因为默认链接会链接基本的 DPDK 动态库. 要解决此问题, 不论是用 Makefile 还是 meson, 都需要设法链接要用到的 PMD 驱动, 如 -lrte_net_ixgbe (早期库名是 -lrte_pmd_ixgbe) -lrte_mempool_ring

使用Makefile

DPDK 编译系统使用 pkg-config 来方便 Makefile 的编写. DPDK 安装后把 libdpdk.pc 文件放在(Ubuntu) /usr/local/lib/x86_64-linux-gnu/pkgconfig/libdpdk.pc.

通过 pkg-config 以及 libdpdk.pc 的帮助, 不必在 Makefile 文件里费心思写 CFLAGS, LDFLAGS 等东西, 而是让 pkg-config 自动生成. DPDK 应用程序的 Makefile 一般都类似下面这样:

PKGCONF = pkg-config CFLAGS += -O3 $(shell $(PKGCONF) --cflags libdpdk) LDFLAGS += $(shell $(PKGCONF) --libs libdpdk) $(APP): $(SRCS-y) Makefile $(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS)

可以看到 CFLAGS, LDFLAGS 是推断出来的. 像我这样把 helloword 示例代码移到另一个外部目录编译的话, 相当于编译自己写的一个外部 DPDK 程序, 直接 make 就可以了:

zzq@ubuntu16:~/dev/helloworld $ make cc -O3 -include rte_config.h -march=native -I/usr/local/include main.c -o build/helloworld-shared -L/usr/local/lib/x86_64-linux-gnu -Wl,--as-needed -lrte_node -lrte_graph -lrte_bpf -lrte_flow_classify -lrte_pipeline -lrte_table -lrte_port -lrte_fib -lrte_ipsec -lrte_vhost -lrte_stack -lrte_security -lrte_sched -lrte_reorder -lrte_rib -lrte_regexdev -lrte_rawdev -lrte_pdump -lrte_power -lrte_member -lrte_lpm -lrte_latencystats -lrte_kni -lrte_jobstats -lrte_ip_frag -lrte_gso -lrte_gro -lrte_eventdev -lrte_efd -lrte_distributor -lrte_cryptodev -lrte_compressdev -lrte_cfgfile -lrte_bitratestats -lrte_bbdev -lrte_acl -lrte_timer -lrte_hash -lrte_metrics -lrte_cmdline -lrte_pci -lrte_ethdev -lrte_meter -lrte_net -lrte_mbuf -lrte_mempool -lrte_rcu -lrte_ring -lrte_eal -lrte_telemetry -lrte_kvargs ln -sf helloworld-shared build/helloworld

编译完成会在 build/ 下生成可执行文件

如果动态链接 DPDK 库出现找不到网卡问题, 请在链接选项中添加类似以下项: -L/usr/local/lib64 -lrte_net_ixgbe -lrte_mempool_ring 

使用meson

helloword 示例代码目录中已经有一个 meson.build 文件, 但这个文件是用于编译整个 DPDK 源码时编译 helloworld 用的, 而不是用于在外部单独编译它. 所以我们需要创建一个新的 meson 配置文件。

project('helloworld', 'c') dpdk = dependency('libdpdk') allow_experimental_apis = true sources = files( 'main.c' ) executable('helloworld', sources, dependencies: dpdk)

语法我目前也不清楚, 但对于简单的示例来说够用了. // TODO写好之后, 其余编译步骤就和编译 DPDK 一样了:

$ cd ~/dev/helloword/ $ meson build $ cd build/ $ meson configure $ ninja

编译好之后会在 build/ 下生成可执行文件。

如果动态链接 DPDK 库出现找不到网卡问题, 请参考以下写法:

project('l2fwd', 'c') dpdk_dep = declare_dependency( dependencies: dependency('libdpdk'), link_args: [ '-Wl,--no-as-needed', '-L/usr/local/lib64', '-lrte_net_vmxnet3', '-lrte_net_ixgbe', '-lrte_net_i40e', '-lrte_mempool_ring' ], ) sources = files( 'main.c' ) executable('l2fwd',sources, dependencies: dpdk_dep )

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/96469.html

(0)
上一篇 2024-11-04 12:26
下一篇 2024-11-05 10:00

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

关注微信