Featured image of post DPDK例程helloworld代码解析

DPDK例程helloworld代码解析

解析DPDK的helloworld例程,该例程在指定核心上运行lcore_hello子程序,显示Core ID并等待所有子线程结束

初始化基础运行环境

查看Ubuntu虚拟机中的网卡类型,Intel 82545EM Gigabit Ethernet NIC即为E1000,是Intel的经典千兆以太网卡,支持标准的以太网协议

1
2
3
4
int ret;
ret = rte_eal_init(argc, argv);
	if (ret < 0)
		rte_panic("Cannot init EAL\n");

主线程运行入口是main函数,调用了rte_eal_init入口函数,以初始化环境抽象层(EAL,Environment Abstraction Layer)。EAL 是 DPDK 中负责与硬件和操作系统进行交互的部分。这个函数会初始化必要的资源,对于DPDK库的使用者而言,初始化操作已经被EAL封装起来,其初始化主要动作包括:

  1. 配置初始化
  2. 内存初始化
  3. 内存池初始化
  4. 队列初始化
  5. 告警初始化
  6. 中断初始化
  7. PCI初始化
  8. 定时器初始化
  9. 监测内存本地化(NUMA)
  10. 插件初始化
  11. 主线程初始化
  12. 轮询设备初始化
  13. 建立主从线程通道
  14. 将从线程设置在等待模式
  15. PCI设备的探测与初始化

argc(命令行参数的数量)和argv(包含命令行参数的字符串数组)参数传递给rte_eal_init函数,返回值是解析的参数个数,常见的 EAL 启动参数有以下几种:

  1. -c <coremask>:指定使用的CPU核心掩码,核心掩码是一个64位的位图,每一位代表一个CPU核心,例如-c 0x3 表示使用CPU核心0和1
  2. -l <core_list>:指定需要绑定的CPU核心列表,可以使用逗号分隔的核心编号(例如-l 0,1,4,5表示绑定到CPU核心0、1、4、5),也可以使用范围表示法(例如,0-3表示核心0到3)
  3. -r <socket_id>:指定NUMA节点的ID,如果系统有多个NUMA节点,可以指定程序运行在哪个节点上,例如-r 1表示绑定到NUMA节点1
  4. -v:启用详细的输出信息,通常用于调试
  5. - -master-lcore:指定主核心,用于控制EAL的主进程(通常是启动时的第一个核心),例如- -master-lcore 0表示将主进程绑定到核心0

多核运行初始化

1
2
3
4
5
6
7
unsigned lcore_id;
/* 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. */
}

RTE_LCORE_FOREACH_WORKER(lcore_id)遍历所有EAL指定可以使用的逻辑核(不包括主核心)。遍历时,在每个逻辑核上通过rte_eal_remote_launch(lcore_hello, NULL, lcore_id)启动被指定的线程:

  1. 第一个参数是从线程,表示在线程中执行的任务
  2. 第二个参数是传给从线程的参数, NULL表示没有传递任何参数给 lcore_hello
  3. 第三个参数是目标逻辑核的编号,从线程会执行在这个逻辑核上
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
/* 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);
	return 0;
}
/* >8 End of launching function on lcore. */

在函数lcore_hello中,通过rte_lcore_id()获取当前执行线程所在逻辑核心的编号,并随后打印出来

注意其中的__rte_unused是一个DPDK的宏,用于标记函数参数为未使用。这是为了防止编译器对未使用的参数发出警告。尽管在此函数中arg未使用,但为了保持接口的一致性或扩展性,arg作为参数被保留

主核运行与清理运行环境

1
2
3
4
5
6
7
8
/* 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();

RTE_LCORE_FOREACH_WORKER(lcore_id)遍历的逻辑核心不包括主核心,因此若需要在主核心上调用lcore_hello函数,需额外调用执行

rte_eal_mp_wait_lcore()是DPDK提供的函数,它用于让阻塞主核心或者调用者,以使其等待所有其它的逻辑核心完成任务,这样可以避免主核心提前退出而导致的未完成任务或内存泄漏

rte_eal_cleanup()是DPDK中用于清理EAL环境的函数,它用于释放DPDK启动时分配的资源,包括巨页、内存池、线程、定时器、中断回调、内部结构体、全局状态信息等。这些资源在程序退出时若不释放,会导致内存泄漏或资源残留,尤其是在重复运行DPDK应用程序时可能造成巨页无法重新分配、共享对象名冲突、VFIO绑定失败等故障。

程序运行

1
sudo ./dpdk-helloworld

PixPin_2025-04-20_23-13-48

源程序

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/* SPDX-License-Identifier: BSD-3-Clause
 * Copyright(c) 2010-2014 Intel Corporation
 */

#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <sys/queue.h>

#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>

/* 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);
	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;
}
Licensed under CC BY-NC-SA 4.0
皖ICP备2025083746号-1


使用 Hugo 构建
主题 StackJimmy 设计