dpdk原理,dpdk编译与环境搭建,dpdk简单示例

DPDK是一个用于快速数据包处理的开源工具,通过环境抽象层和轮询模式驱动提高性能。关键特性包括大页内存优化、mempool内存池和ring通信机制。文章详细介绍了DPDK的原理、环境搭建步骤以及编译过程中可能遇到的问题和解决方法。
摘要由CSDN通过智能技术生成

dpdk官网地址:http://core.dpdk.org/
dpdk在线文档:https://dpdk-docs.readthedocs.io/en/latest/
dpdk收发示例:https://github.com/NEOAdvancedTechnology/MinimalDPDKExamples

dpdk原理

DPDK简介
1、数据平面开发套件(DPDK ,Data Plane Development Kit)是由6WIND,Intel等多家公司开发,主要基于Linux系统运行,用于快速数据包处理的函数库与驱动集合,可以极大提高数据处理性能和吞吐量,提高数据平面应用程序的工作效率。
2、DPDK是一个linux基金会的开源项目,开发DPDK的主要目的,是在数据平面应用中为快速的数据包处理提供一个简单而完善的架构。在理解此工具集之后,开发人员可以以此为基础进行新的原型设计,或简单地为我所用。
3、DPDK架构通过创建EAL(Environment Abstraction Layer,环境抽象层)来为不同的工作环境创造函数库集,创建后开发者即可把自己的应用与函数库进行链接。

工作原理
DPDK使用了轮询(polling)而不是中断来处理数据包。在收到数据包时,经DPDK重载的网卡驱动不会通过中断通知CPU,而是直接将数据包存入内存,交付应用层软件通过DPDK提供的接口来直接处理,这样节省了大量的CPU中断时间和内存拷贝时间。

关键技术
1、环境抽象层:DPDK的创造的环境抽象层(EAL, Environment Abstraction Layer)主要负责对计算机底层资源(如硬件和内存空间)的访问,并对提供给用户的接口实施了实现细节的封装。其初始化例程决定了如何分配这些资源(PCI设备、计时器、控制台等)。
2、轮询模式驱动:(PMD, Poll Mode Driver)PMD由用户空间的特定的驱动程序提供的API组成,用于对设备和它们相应的队列进行设置。避免基于中断的异步信号发送机制带来的性能瓶颈。

大页
x86(包括x86-32和x86-64)架构的CPU默认使用4KB大小的内存页面(getconf PAGESIZE),但是它们也支持较大的内存页,如x86-64系统就支持2MB大小的大页(huge page)。
Linux 2.6及以上的内核都支持huge page
1、只有内核才可以直接访问物理内存,进程并不可以。
2、Linux 内核给每个进程都提供了一个独立的虚拟地址空间,这个地址空间是连续的。进程可以访问虚拟内存。
3、虚拟地址空间的内部又被分为内核空间和用户空间两部分,不同字长(单个 CPU 指令可以处理数据的最大长度)的处理器,地址空间的范围也不同。
4、内存映射,就是将虚拟内存地址映射到物理内存地址。
5、为了完成内存映射,内核为每个进程都维护了一张页表,记录虚拟地址与物理地址的映射关系。
6、页的大小只有 4 KB ,导致的另一个问题就是,当物理内存很大时,页表会变得非常大,占用大量物理内存。
7、大页的优点:减少页表大小,减少页表遍历,减少页表查找开销,避免swap,减少了内存开销。

Mbuf
1、为了高效访问数据,DPDK将内存封装在Mbuf(struct rte_mbuf)结构体内。 Mbuf主要用来封装网络帧缓存,也可用来封装通用控制信息缓存(缓存类型需使 用CTRL_MBUF_FLAG来指定)。
2、网络帧元数据的一部分内容由DPDK的网卡驱动写入。这些内容包括VLAN标签、 RSS哈希值、网络帧入口端口号以及巨型帧所占的Mbuf个数等。对于巨型帧,网络 帧元数据仅出现在第一个帧的Mbuf结构中,其他的帧该信息为空。

mempool
1、DPDK提供了mempool内存池机制。mempool 是固定大小的对象分配器。在DPDK中,它由名称唯一标识。
2、在DPDK中,数据包的内存操作对象被抽象化为Mbuf结构,而有限的rte_mbuf结 构对象则存储在内存池中。内存池使用环形缓存区来保存空闲对象。当一个网络帧被网卡接收时,DPDK的网卡驱动将其存储在一个高效的环形缓存区 中,同时在Mbuf的环形缓存区中创建一个Mbuf对象。
3、内存在内存池被创建时就已经申请好了。Mbuf对象被创建好后,网卡驱动根据分析出的帧信息将其初始化,并将其和实际帧对象逻辑相连。对网络帧的分析处理都集中于Mbuf,仅在必要的时候访 问实际网络帧。这就是内存池的双环形缓存区结构。

相关名词
1、PCI(Peripheral Component Interconnect)是 一种由英特尔(Intel)公司1991年推出的用于定义局部总线的标准。此标准允许在计算机内安装多达10个遵从PCI标准的扩展卡。
2、NIC 网络接口控制器(英语:network interface controller,NIC),网络适配器(network adapter),网卡(network interface card),或局域网接收器(LAN adapter),是一块被设计用来允许计算机在计算机网络上进行通讯的计算机硬件。由于其拥有MAC地址,因此属于OSI模型的第1层。它使得用户可以通过电缆或无线相互连接。
3、自旋锁,自旋锁是专为防止多处理器并发而引入的一种锁,它在内核中大量应用于中断处理等部分。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁。
4、mmap,mmap将一个文件或者其它对象映射进内存。mmap操作提供了一种机制,让用户程序直接访问设备内存,这种机制,相比较在用户空间和内核空间互相拷贝数据,效率更高。在要求高性能的应用中比较常用。mmap映射内存必须是页面大小的整数倍,面向流的设备不能进行mmap,mmap的实现和硬件有关。

ring
rte_ring的实质是FIFO(First In First Out)的无锁环形队列,无锁队列的出队入队操作是rte_ring实现的关键。常用于多线程/多进程之间的通信。

dpdk环境搭建与编译

可以通过网络下载dpdk开发包,通常包含bin(脚本)include(头文件)kernel、lib64(动态库)、share(案例examples)几个文件夹。
也可以下载源码自己编译,下面介绍dpdk源码编译过程。

编译环境
条件所限,使用虚拟机搭建环境。
虚拟机版本:VMware Workstation 16 Pro
操作系统:ubuntu20.04
dpdk版本:DPDK 19.08.2
虚拟机除通信网卡外,再添加两块网卡,处理器数量要大于1

资源下载,dpdk官网,下载DPDK 19.08.2版本,http://core.dpdk.org/download/
安装依赖

sudo apt install build-essential
sudo apt-get install libnuma-dev

配置大页内存,开启immon功能

sudo vim /etc/default/grub 
#在GRUB_CMDLINE_LINUX末尾追加:hugepages=512 intel_iommu=on iommu=pt
#GRUB_CMDLINE_LINUX="find_preseed=/preseed.cfg auto noprompt priority=critical locale=en_US hugepages=512 intel_iommu=on iommu=pt"

重新生成引导配置

sudo grub-mkconfig -o /boot/grub/grub.cfg

重启并验证

cat /proc/meminfo |grep -i HugePages
dmesg | grep DMAR

编译dpdk

cd dpdk-stable-19.08.2/
export RTE_SDK=$PWD
export RTE_TARGET=x86_64-native-linuxapp-gcc
cd usertools/
sudo ./dpdk-setup.sh

编译报错

error: this statement may fall through [-Werror=implicit-fallthrough=]
在报错c文件路径找到makefile,删掉-Werror
MODULE_CFLAGS += -Winline -Wall -Werror

/home/chw/soft/dpdk-19.08.2/dpdk-stable-19.08.2/x86_64-native-linuxapp-gcc/build/kernel/linux/kni/kni_net.c:737:20: error: initialization of ‘void (*)(struct net_device , unsigned int)’ from incompatible pointer type ‘void ()(struct net_device *)’ [-Werror=incompatible-pointer-types]
kni_net.c文件,注释掉737行.ndo_tx_timeout = kni_net_tx_timeout,

/home/chw/soft/dpdk-19.08.2/dpdk-stable-19.08.2/x86_64-native-linuxapp-gcc/build/kernel/linux/kni/kni_net.c:591:1: error: ‘kni_net_tx_timeout’ defined but not used [-Werror=unused-function]
Makefile里的-Werror误报,打开kni_net.c所在目录里的makefile文件,删除-Werror

/usr/bin/env: ‘python’: No such file or directory 错误
sudo apt install python

运行sudo ./dpdk-setup.sh,进入操作选项
选择编译36

INSTALL-APP dpdk-test-eventdev
INSTALL-MAP dpdk-test-eventdev.map
Build complete [x86_64-native-linuxapp-gcc]
Installation cannot run with T defined and DESTDIR undefined
RTE_TARGET exported as x86_64-native-linuxapp-gcc

加载驱动43

Option: 43
Unloading any existing DPDK UIO module
Loading uio module
Loading DPDK UIO module

配置大页内存46

Option: 46
Removing currently reserved hugepages
Unmounting /mnt/huge and removing directory
Input the number of 2048kB hugepages
Example: to have 128MB of hugepages available in a 2MB huge page system,
enter ‘64’ to reserve 64 * 2MB pages
Number of pages: 512
Reserving hugepages
Creating /mnt/huge and mounting as hugetlbfs

查看网卡状态48

Option: 48
Network devices using kernel driver
===================================
0000:02:01.0 ‘82545EM Gigabit Ethernet Controller (Copper) 100f’ if=ens33 drv=e1000 unused=igb_uio,vfio-pci Active
0000:03:00.0 ‘VMXNET3 Ethernet Controller 07b0’ if=ens160 drv=vmxnet3 unused=igb_uio,vfio-pci Active

绑定网卡49

ifconfig ens160 down#先down掉一个网卡
echo 1 > /sys/module/vfio/parameters/enable_unsafe_noiommu_mode

Option: 49
Network devices using kernel driver
===================================
0000:02:01.0 ‘82545EM Gigabit Ethernet Controller (Copper) 100f’ if=ens33 drv=e1000 unused=igb_uio,vfio-pci Active
0000:03:00.0 ‘VMXNET3 Ethernet Controller 07b0’ if=ens160 drv=vmxnet3 unused=igb_uio,vfio-pci
No ‘Baseband’ devices detected
==============================
No ‘Crypto’ devices detected
============================
No ‘Eventdev’ devices detected
==============================
No ‘Mempool’ devices detected
=============================
No ‘Compress’ devices detected
==============================
No ‘Misc (rawdev)’ devices detected
===================================
Enter PCI address of device to bind to IGB UIO driver: 03:00.0
OK

重启服务器后恢复环境

echo 1 > /sys/module/vfio/parameters/enable_unsafe_noiommu_mode
modprobe vfio-pci
lsmod | grep vfio_pci
./dpdk-devbind.py -s
ifconfig ens160 down
./dpdk-devbind.py -b vfio-pci 0000:03:00.0
./dpdk-devbind.py -s
./dpdk-hugepages.py -p 1G --setup 2G
./dpdk-hugepages.py -s

dpdk简单示例

使用dpdk自带的例子:/dpdk-stable-19.08.2/examples/helloworld。

#include <rte_memory.h>
#include <rte_launch.h>
#include <rte_eal.h>
#include <rte_per_lcore.h>
#include <rte_lcore.h>
#include <rte_debug.h>
#include <thread>

/* Launch a function on lcore. 8< */
static int
lcore_hello(__rte_unused void *arg)
{
        unsigned lcore_id;
        lcore_id = rte_lcore_id();
        printf("hello from core %u\n", lcore_id);
        printf("tid=%lu\n",pthread_self());
        return 0;
}
/* >8 End of launching function on lcore. */

/* Initialization of Environment Abstraction Layer (EAL). 8< */
int main(int argc, char **argv)
{
        int ret;
        unsigned lcore_id;

        ret = rte_eal_init(argc, argv);
        if (ret < 0)
                rte_panic("Cannot init EAL\n");
        /* >8 End of initialization of Environment Abstraction Layer */

        /* Launches the function on each lcore. 8< */
        RTE_LCORE_FOREACH_WORKER(lcore_id) {
                /* Simpler equivalent. 8< */
                rte_eal_remote_launch(lcore_hello, NULL, lcore_id);
                /* >8 End of simpler equivalent. */
        }

        /* call it on main lcore too */
        lcore_hello(NULL);
        /* >8 End of launching the function on each lcore. */

        rte_eal_mp_wait_lcore();

        /* clean up the EAL */
        rte_eal_cleanup();

        return 0;
}

代码说明

1、rte_eal_init,该函数的作用是用来初始化DPDK的运行时环境(runtime environment, rte),在DPDK中称为环境抽象层(environment abstract layer, eal)。
2、DPDK的eal环境初始化完毕之后,会在各处理核(lcore)上启动各线程,DPDK用RTE_LCORE_FOREACH_WORKER这个宏定义来遍历所有的worker处理核,并在该处理核上启动线程的入口函数。
3、本例入口函数是lcore_hello,DPDK通过rte_eal_remote_launch将线程入口函数与处理核进行绑定,绑定之后,在DPDK进程结束前,该线程会独占该处理核且不会有其他线程调度到该处理核上运行。
4、与worker处理核相对应的是main处理核,在执行DPDK进程时,进程被调度到哪个处理核上执行,该处理核就是main处理核,即main处理核承担了初始化DPDK的任务。
5、在本例中,main函数在各worker处理核上启动线程之后,同样执行了lcore_hello,并且调用rte_eal_mp_wait_lcore等待所有线程执行完毕之后再退出。

打印

EAL: Detected CPU lcores: 8
EAL: Detected NUMA nodes: 1
EAL: Detected shared linkage of DPDK
EAL: Multi-process socket /var/run/dpdk/rte/mp_socket
EAL: Selected IOVA mode 'VA'
EAL: No available 1048576 kB hugepages reported
EAL: VFIO support initialized
TELEMETRY: No legacy callbacks, legacy socket not created
hello from core 1
tid=139741341693696
hello from core 2
tid=139741333300992
hello from core 3
tid=139741324908288
hello from core 4
tid=139741316515584
hello from core 5
tid=139741308122880
hello from core 6
tid=139741299730176
hello from core 7
tid=139741291337472
hello from core 0
tid=139741358487872

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单DPDK示例程序,用于网口收包: ```c #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <inttypes.h> #include <string.h> #include <errno.h> #include <stdbool.h> #include <rte_eal.h> #include <rte_ethdev.h> #include <rte_mbuf.h> #define RX_RING_SIZE 128 #define NUM_MBUFS 8191 #define MBUF_CACHE_SIZE 250 #define BURST_SIZE 32 static const struct rte_eth_conf port_conf_default = { .rxmode = { .max_rx_pkt_len = ETHER_MAX_LEN, }, }; int main(int argc, char *argv[]) { int ret; uint16_t nb_ports; uint16_t portid; struct rte_mempool *mbuf_pool; struct rte_eth_conf port_conf = port_conf_default; struct rte_eth_dev_info dev_info; struct rte_eth_rxconf rxq_conf; struct rte_eth_dev_tx_buffer *tx_buffer; /* Initialize the Environment Abstraction Layer (EAL). */ ret = rte_eal_init(argc, argv); if (ret < 0) rte_exit(EXIT_FAILURE, "Error with EAL initialization\n"); argc -= ret; argv += ret; /* Check that there is at least one port available. */ nb_ports = rte_eth_dev_count_avail(); if (nb_ports < 1) rte_exit(EXIT_FAILURE, "Error: no ports available\n"); /* Configure the first Ethernet device. */ portid = 0; ret = rte_eth_dev_configure(portid, 1, 1, &port_conf); if (ret < 0) rte_exit(EXIT_FAILURE, "Error configuring the Ethernet device\n"); /* Get the device information. */ rte_eth_dev_info_get(portid, &dev_info); /* Allocate a mbuf pool. */ mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", NUM_MBUFS, MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id()); if (mbuf_pool == NULL) rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n"); /* Configure the Ethernet device RX queue. */ rxq_conf = dev_info.default_rxconf; rxq_conf.rx_drop_en = 1; ret = rte_eth_rx_queue_setup(portid, 0, RX_RING_SIZE, rte_eth_dev_socket_id(portid), &rxq_conf, mbuf_pool); if (ret < 0) rte_exit(EXIT_FAILURE, "Cannot configure RX queue\n"); /* Start the Ethernet device. */ ret = rte_eth_dev_start(portid); if (ret < 0) rte_exit(EXIT_FAILURE, "Cannot start Ethernet device\n"); /* Enable RX in promiscuous mode for the Ethernet device. */ rte_eth_promiscuous_enable(portid); /* Initialize the transmit buffer. */ tx_buffer = rte_zmalloc_socket("tx_buffer", RTE_ETH_TX_BUFFER_SIZE(BURST_SIZE), 0, rte_eth_dev_socket_id(portid)); if (tx_buffer == NULL) rte_exit(EXIT_FAILURE, "Cannot allocate transmit buffer\n"); rte_eth_tx_buffer_init(tx_buffer, BURST_SIZE); /* Receive and transmit packets. */ struct rte_mbuf *bufs[BURST_SIZE]; while (true) { const uint16_t nb_rx = rte_eth_rx_burst(portid, 0, bufs, BURST_SIZE); if (nb_rx == 0) continue; for (uint16_t i = 0; i < nb_rx; i++) { struct rte_mbuf *buf = bufs[i]; /* Process the packet here. */ rte_pktmbuf_free(buf); } } return 0; } ``` 此示例程序初始化EAL、配置和启动第一个以太网设备、创建mbuf池、配置RX队列、启用混杂模式并接收和处理包。可以使用以下命令编译此程序: ``` $ gcc -o example example.c -I /usr/include/dpdk -L /usr/lib -ldpdk ``` 该程序需要在root权限下运行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值