在 DPDK Programmer's Guides 中的 EAL 一篇中有一个图可以很清晰地看到一个DPDK的应用程序的大致执行思路:
初始化检查CPU支持、微架构配置等完成后,执行main()函数。
- 第一步是
rte_eal_init()
,核心初始化和启动。其中线程使用的是pthread库,创造线程,并设置CPU亲和性:DPDK通常每个核心固定一个pthread,以避免任务切换的开销。 - 然后是 other inits,其余对象的初始化(mbuf、mempool、ring、lpm hash table等)都应该作为主 lcore 上整体应用程序初始化的一部分来完成。
rte_eal_remote_lauch(func, NULL, lcore_id)
,在每个逻辑核上注册一个回调函数。rte_eal_mp_wait_lcore()
,等待各个线程返回。- 继续其余的部分。
下面对照此思路阅读 /dpdk/examples/helloworld/main.c 这个代码:
/* 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>static int
lcore_hello(__attribute__((unused)) void *arg)
{unsigned lcore_id;lcore_id = rte_lcore_id(); // ⑤ 返回当前执行单元的线程IDprintf("hello from core %u\n", lcore_id);return 0;
}int
main(int argc, char **argv)
{int ret;unsigned lcore_id;ret = rte_eal_init(argc, argv); // ① 初始化EALif (ret < 0)rte_panic("Cannot init EAL\n");RTE_LCORE_FOREACH_SLAVE(lcore_id) { // ② 浏览除主lcore之外的所有lcorerte_eal_remote_launch(lcore_hello, NULL, lcore_id); // ③ 在lcore上执行函数}lcore_hello(NULL);rte_eal_mp_wait_lcore(); // ④ 等待lcore完成工作return 0;
}
① rte_eal_init()
初始化环境抽象层(EAL)。此函数仅在应用程序的main()函数中尽快在MASTER lcore上执行。
② 宏:RTE_LCORE_FOREACH_SLAVE(i)
:浏览除主lcore之外的所有正在运行的lcores。
#define RTE_LCORE_FOREACH_SLAVE(i)
Value:
for (i = rte_get_next_lcore(-1, 1, 0); \i<RTE_MAX_LCORE; \i = rte_get_next_lcore(i, 1, 0))
③ rte_eal_remote_launch(lcore_hello, NULL, lcore_id);
在另一个lcore上启动一个函数。应仅在MASTER lcore上执行。第一个参数是函数名,第二个参数是以什么参数执行该函数,第三个参数是逻辑核的ID。
④ rte_eal_mp_wait_lcore();
应仅在MASTER lcore上执行,等到lcore完成它的工作。一个lcore有三种状态:WAIT、RUNNING、FINISHED,对应空闲、正在执行一个函数、执行完成。依据三个状态来协调线程的launch和wait。
⑤ rte_lcore_id()
返回执行单元的线程ID
API 文档:
- DPDK Programmer's Guides - initialization-and-core-launching
- EAL环境抽象层 包括上文的 ①
- CPU-multicore:lcore ②、⑤
- CPU-multicore:launch ③、④