参考资料
dpdk第二课------helloworld源码剖析
DPDK 环境适配层EAL
前言
dpdk的example-helloworld学习
源码
c
/* 在逻辑核心上启动一个处理函数 */
static int
lcore_hello(__rte_unused void *arg)
{
unsigned lcore_id;
lcore_id = rte_lcore_id(); // 获取当前函数所使用的lcore逻辑核心
printf("hello from core %u\n", lcore_id);
return 0;
}
int main(int argc, char **argv)
{
int ret;
unsigned lcore_id;
// EAL 初始化
ret = rte_eal_init(argc, argv);
if (ret < 0)
rte_panic("Cannot init EAL\n");
/* 设置日志级别 */
rte_log_set_level(RTE_LOGTYPE_EAL, RTE_LOG_DEBUG);
// master - worker 架构
// 遍历每一个worker逻辑核心,并在worker核心上运行lcore_hello函数
RTE_LCORE_FOREACH_WORKER(lcore_id) {
rte_eal_remote_launch(lcore_hello, NULL, lcore_id);
}
/* 在main核心上也调用lcore_hello 函数 */
lcore_hello(NULL);
// 等待线程结束。
rte_eal_mp_wait_lcore();
/* clean up the EAL */
rte_eal_cleanup();
return 0;
}
EAL
EAL 初始化通过调用 DPDK 的rte_eal_init()函数完成,该过程会处理一系列关键操作,主要包括:
-
命令行参数解析解析启动 DPDK 应用时传入的命令行参数(如核心掩码、大页配置、设备绑定等),常见参数包括:
-c <core_mask>:指定应用使用的 CPU 核心(通过掩码表示,如-c 0x3表示使用核心 0 和 1);-n <num>:指定内存通道数(与 CPU 内存控制器相关);--huge-dir <path>:指定大页内存的挂载路径;-w <pci_addr>:指定需要绑定的 PCI 设备(如网卡)。
-
大页内存初始化 DPDK 为减少内存访问延迟、避免 TLB(Translation Lookaside Buffer) miss,依赖大页内存(Hugepages)。EAL 初始化会:
- 检查系统是否配置了足够的大页内存(通常需要提前在系统中预留,如
echo 1024 > /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages); - 挂载大页内存到指定目录(如
/mnt/huge); - 为 DPDK 应用分配大页内存池,供后续数据包缓冲区(如
rte_mbuf)使用。
- 检查系统是否配置了足够的大页内存(通常需要提前在系统中预留,如
-
CPU 核心管理为避免操作系统进程调度带来的延迟,DPDK 通常将线程绑定到特定的 CPU 核心(核绑定)。EAL 初始化会:
- 识别系统中的 CPU 核心拓扑(如物理核、逻辑核、NUMA 节点);
- 根据
-c参数预留指定的 CPU 核心,避免被其他进程占用; - 提供核心绑定接口(如
rte_eal_remote_launch()),供后续应用线程绑定使用。
-
PCI 设备探测与初始化DPDK 需要直接操作网卡等 PCIe 设备(绕过操作系统内核协议栈),EAL 初始化会:
- 扫描系统中的 PCI 设备,识别支持 DPDK 的网卡(如 Intel、Mellanox 等厂商的高性能网卡);
- 将指定的 PCI 设备(通过
-w参数)从操作系统内核驱动(如ixgbe)解绑,重新绑定到 DPDK 兼容的用户态驱动(如vfio-pci、uio_pci_generic); - 为设备分配资源(如 I/O 内存、中断),并初始化设备硬件队列(如收包队列、发包队列)。
-
其他基础组件初始化
- 日志系统:初始化日志输出(控制台、文件)和级别控制;
- 内存池(Mempool):创建默认的内存池,用于管理数据包缓冲区;
- 多进程 / 多线程支持:初始化进程间通信(IPC)机制(如共享内存),支持多进程模式;
- 计时器:初始化高精度计时器,供性能统计或定时任务使用。
核心
DPDK 应用通常运行在多个 CPU 核心上,这些核心被分为两类:
- 主核心(master lcore) :负责初始化 EAL、管理全局资源(如设备配置、内存池)等 "控制平面" 工作,通常是 EAL 初始化时第一个被启用的核心(由
-c参数指定的核心掩码中,序号最小的核心)。 - 工作核心(worker lcore) :负责 "数据平面" 的核心工作(如数据包收发、解析、转发等),是除 master 之外的所有被 EAL 启用的核心。
RTE_LCORE_FOREACH_WORKER
RTE_LCORE_FOREACH_WORKER 宏的核心功能是迭代遍历所有 worker lcore,方便开发者在每个 worker 核心上部署并行任务(如启动数据包处理线程)。
c
#define RTE_LCORE_FOREACH_WORKER(lc) \
for (lc = rte_get_next_lcore(-1, 1, 0); lc < RTE_MAX_LCORE; \
lc = rte_get_next_lcore(lc, 1, 0))
- 参数
lc:循环变量,用于存储当前遍历到的 worker lcore 的 ID(逻辑核心编号)。 rte_get_next_lcore(-1, 1, 0):从第一个核心开始查找,1表示只返回 worker lcore(排除 master),0表示不循环查找
rte_eal_remote_launch
rte_eal_remote_launch 是一个核心函数,用于在指定的逻辑核心(lcore)上启动一个用户定义的函数,是实现多核心并行任务调度的关键接口。它的设计目标是将数据平面的处理逻辑(如数据包收发、转发)绑定到特定的 CPU 核心,避免操作系统调度带来的延迟,从而保证高性能。
c
int rte_eal_remote_launch(int (*f)(void *), void *arg, unsigned int lcore_id);
- 参数解析 :
f:函数指针,指向要在目标 lcore 上执行的任务函数(返回值为int,参数为void *)。arg:传递给任务函数f的参数(可以是任意类型的指针,需自行保证生命周期)。lcore_id:目标逻辑核心的 ID(必须是 EAL 初始化时通过-c参数启用的 lcore,否则会失败)。
- 返回值 :
- 成功:返回
0(函数已被提交到目标 lcore 的任务队列,等待执行)。 - 失败:返回
-1(可能原因:lcore_id无效、目标 lcore 未启用、该 lcore 已有任务在运行等)。
- 成功:返回
通过管道发送唤醒信号,通知worker线程任务。