初始化基础运行环境
查看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封装起来,其初始化主要动作包括:
- 配置初始化
- 内存初始化
- 内存池初始化
- 队列初始化
- 告警初始化
- 中断初始化
- PCI初始化
- 定时器初始化
- 监测内存本地化(NUMA)
- 插件初始化
- 主线程初始化
- 轮询设备初始化
- 建立主从线程通道
- 将从线程设置在等待模式
- PCI设备的探测与初始化
argc(命令行参数的数量)和argv(包含命令行参数的字符串数组)参数传递给rte_eal_init函数,返回值是解析的参数个数,常见的 EAL 启动参数有以下几种:
- -c <coremask>:指定使用的CPU核心掩码,核心掩码是一个64位的位图,每一位代表一个CPU核心,例如-c 0x3 表示使用CPU核心0和1
- -l <core_list>:指定需要绑定的CPU核心列表,可以使用逗号分隔的核心编号(例如-l 0,1,4,5表示绑定到CPU核心0、1、4、5),也可以使用范围表示法(例如,0-3表示核心0到3)
- -r <socket_id>:指定NUMA节点的ID,如果系统有多个NUMA节点,可以指定程序运行在哪个节点上,例如-r 1表示绑定到NUMA节点1
- -v:启用详细的输出信息,通常用于调试
- - -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)启动被指定的线程:
- 第一个参数是从线程,表示在线程中执行的任务
- 第二个参数是传给从线程的参数, NULL表示没有传递任何参数给 lcore_hello
- 第三个参数是目标逻辑核的编号,从线程会执行在这个逻辑核上
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
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;
}
|