DPDK里的HelloWorld是最基础的入门程序,代码简短,功能也不复杂。
它建立了一个多核(线程)运行的基础环境,每个线程会打印“hello from core #”,core # 是由操作系统管理的。如无特别说明,本文里的DPDK线程与硬件线程是一一对应的关系。从代码角度, rte是指runtime environment,eal是指environmentabstraction layer。DPDK的主要对外函数接口都以rte_作为前缀,抽象化函数接口是典型软件设计思路,可以帮助DPDK可以运行多个操作系统上,DPDK官方支持Linux与FreeBSD。和多数并行处理系统类似,DPDK也有主线程,从线程的差异。
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");
/* call lcore_hello() on every slave lcore */ RTE_LCORE_FOREACH_SLAVE(lcore_id) { rte_eal_remote_launch(lcore_hello, NULL, lcore_id); }
/* call it on master lcore too */ lcore_hello(NULL);
rte_eal_mp_wait_lcore(); return 0; } |
1.初始化基础运行环境
主线程运行入口是main函数,调用了rte_eal_init入口函数,启动基础运行环境。
intrte_eal_init(int argc, char **argv);
入口参数是启动DPDK的命令行,可以是长长的一串很复杂的设置,需要深入了解的读者可以查看DPDK相关的文档与源代码\lib\librte_eal\common\eal_common_options.c 。对于HelloWorld这个实例,最需要的参数是“-c <core mask>”,线程掩码(coremask)指定了需要参与运行的线程(核)集合。rte_eal_init本身所完成的工作是复杂的,它读取入口参数,解析并保存作为DPDK运行的系统信息,依赖这些信息,构建一个针对包处理设计的运行环境。主要动作分解如下
配置初始化
内存初始化
内存池初始化
队列初始化
告警初始化
中断初始化
PCI初始化
定时器初始化
检测内存本地化(NUMA)
插件初始化
主线程初始化
轮询设备初始化
建立主从线程通道
将从线程设置在等待模式
PCI设备的探测与初始化
对于DPDK库的使用者,这些操作已经被EAL封装起来,接口清晰。如果需要对DPDK进行深度定制,二次开发,需要仔细研究内部操作,这里不做详解。
2.多核运行初始化
DPDK面向多核设计,程序会试图独占运行在逻辑核(lcore)上。Main函数里重要部分是启动多核运行环境,RTE_LCORE_FOREACH_SLAVE(lcore_id)如名所示,遍历所有EAL指定可以使用的lcore,然后通过rte_eal_remote_launch在每个lcore上,启动被指定的线程。
int rte_eal_remote_launch(int (*f)(void *),
void *arg, unsignedslave_id);
第一个参数是从线程,是被征召的线程,
第二个参数是传给从线程的参数
第三个参数是指定的逻辑核,从线程会执行在这个core上。
具体来说,int rte_eal_remote_launch(lcore_hello,NULL, lcore_id);
参数lcore_id指定了从线程ID,运行入口函数lcore_hello.
运行函数lcore_hello,它读取自己的逻辑核编号(lcore_id), 打印出“hellofrom core #”
static int lcore_hello(__attribute__((unused)) void *arg) { unsigned lcore_id; lcore_id = rte_lcore_id(); printf("hello from core %u\n", lcore_id); return 0; } |
这是个简单示例,从线程很快就完成了指定工作,在更真实场景里,这个从线程会是一个循环运行的处理过程。
(本文摘自《深入浅出DPDK》,若有购书需求,请点击阅读全文)