暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

dpdk

原创 lakers 2023-06-11
469

在对DPDK 的原理和代码展开进一步解析之前,先看一些小而简单的例子,建立一个形
象上的认知。
1)helloworld,启动基础运行环境,DPDK 构建了一个基于操作系统的,但适合包处理
的软件运行环境,你可以认为这是个mini-OS。最早期DPDK,可以完全运行在没有操作系
统的物理核(bare-metal)上,这部分代码现在不在主流的开源包中。
2)skeleton,最精简的单核报文收发骨架,也许这是当前世界上运行最快的报文进出测
试程序。
3)l3fwd,三层转发是DPDK 用于发布性能测试指标的主要应用。
1.7.1 HelloWorld
DPDK 里的HelloWorld 是最基础的入门程序,代码简短,功能也不复杂。它建立了一个
多核(线程)运行的基础环境,每个线程会打印“ hello from core #”,core # 是由操作系统管
理的。如无特别说明,本文里的DPDK 线程与硬件线程是一一对应的关系。从代码角度,rte
是指runtime environment,eal 是指environment abstraction layer。DPDK 的主要对外函数接
口都以rte_ 作为前缀,抽象化函数接口是典型软件设计思路,可以帮助DPDK 运行在多个操
作系统上,DPDK 官方支持Linux 与FreeBSD。和多数并行处理系统类似,DPDK 也有主线程、
从线程的差异。
int
main(int argc, char **argv)
18  第一部分 DPDK 基础篇
{
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 入口函数,启动基础运行环境。
int rte_eal_init(int argc, char **argv);
入口参数是启动DPDK 的命令行,可以是长长的一串很复杂的设置,需要深入了解的读
者可以查看DPDK 相关的文档与源代码\lib\librte_eal\common\eal_common_options.c。对于
HelloWorld 这个实例,最需要的参数是“ -c <core mask>”,线程掩码(core mask)指定了需
要参与运行的线程(核)集合。rte_eal_init 本身所完成的工作很复杂,它读取入口参数,解
析并保存作为DPDK 运行的系统信息,依赖这些信息,构建一个针对包处理设计的运行环
境。主要动作分解如下
‰‰配置初始化
‰‰内存初始化
‰‰内存池初始化
‰‰队列初始化
‰‰告警初始化
‰‰中断初始化
‰‰PCI 初始化
‰‰定时器初始化
‰‰检测内存本地化(NUMA)
‰‰插件初始化
‰‰主线程初始化
‰‰轮询设备初始化
第1 章 认识DPDK  19
‰‰建立主从线程通道
‰‰将从线程设置在等待模式
‰‰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, unsigned slave_id);
第一个参数是从线程,是被征召的线程;
第二个参数是传给从线程的参数;
第三个参数是指定的逻辑核,从线程会执行在这个core 上。
具体来说,int rte_eal_remote_launch(lcore_hello, NULL, lcore_id);
参数lcore_id 指定了从线程ID,运行入口函数lcore_hello。
运行函数lcore_hello,它读取自己的逻辑核编号(lcore_id),打印出“hello from 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;
}
这是个简单示例,从线程很快就完成了指定工作,在更真实的场景里,这个从线程会是
一个循环运行的处理过程。
1.7.2 Skeleton
DPDK 为多核设计,但这是单核实例,设计初衷是实现一个最简单的报文收发示例,对收
入报文不做任何处理直接发送。整个代码非常精简,可以用于平台的单核报文出入性能测试。
主要处理函数main 的处理逻辑如下(伪码),调用rte_eal_init 初始化运行环境,检查
网络接口数,据此分配内存池rte_pktmbuf_pool_create,入口参数是指定rte_socket_id(),考
虑了本地内存使用的范例。调用port_init(portid, mbuf_pool) 初始化网口的配置,最后调用
lcore_main() 进行主处理流程。
20  第一部分 DPDK 基础篇
int main(int argc, char *argv[])
{
struct rte_mempool *mbuf_pool;
unsigned nb_ports;
uint8_t portid;
/* Initialize the Environment Abstraction Layer (EAL). */
int ret = rte_eal_init(argc, argv);
/* Check that there is an even number of ports t send/receive on. */
nb_ports = rte_eth_dev_count();
if (nb_ports < 2 || (nb_ports & 1))
rte_exit(EXIT_FAILURE, "Error: number of ports must be even\n");
/* Creates a new mempool in memory to hold the mbufs. */
mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", NUM_MBUFS * nb_ports,
MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
/* Initialize all ports. */
for (portid = 0; portid < nb_ports; portid++)
if (port_init(portid, mbuf_pool) != 0)
rte_exit(EXIT_FAILURE, "Cannot init port %"PRIu8 "\n",
portid);
/* Call lcore_main on the master core only. */
lcore_main();
return 0;
}
网口初始化流程:
port_init(uint8_t port, struct rte_mempool *mbuf_pool)

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论