一个 dpdk 程序最基础的框架需要包含:dpdk 程序初始化、主处理函数、资源清理释放。
官方示例程序中,helloworld 是一个最基础的 dpdk 程序之一(多核运行),它包含了初始化、在每个核心上运行 helloworld 打印、资源清理释放。示例程序 skeleton 则是一个最简单的报文转发程序,它实现了在一个端口接收,在另一个配对的端口(可为自身)发送出去。
初始化流程
在处理数据包之前,每个 DPDK 应用程序都必须遵循特定的初始化顺序:
EAL Initialization(环境抽象层初始化)
环境抽象层(EAL)是 DPDK 应用程序中必须初始化的第一个组件。它处理与底层平台的交互,包括内存管理、PCI 设备发现和 CPU 核心分配等。
int ret = rte_eal_init(argc, argv);
if (ret < 0)
rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
EAL 初始化解析命令行参数,为 DPDK 分配大页面,为数据包处理分配 CPU 核心,并探测可用的网络设备。
使用 Mempools 进行内存管理
通常在 EAL 初始化之后创建 mempool ,DPDK 应用程序使用内存池(mempools)来有效地分配和释放数据包缓冲区(mbufs)。
struct rte_mempool *mbuf_pool = rte_pktmbuf_pool_create(
"MBUF_POOL", /* name */
NUM_MBUFS * nb_ports, /* number of elements */
MBUF_CACHE_SIZE, /* cache size */
0, /* private data size */
RTE_MBUF_DEFAULT_BUF_SIZE, /* data buffer size */
rte_socket_id() /* socket ID */
);
if (mbuf_pool == NULL)
rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
以太网设备配置
初始化 EAL 并创建内存池之后,应用程序配置以太网设备。通常包括设置端口、配置 Rx 和 Tx 队列、启动端口和配置混杂模式等步骤:
/* Configure the Ethernet device */
ret = rte_eth_dev_configure(port_id, rx_rings, tx_rings, &port_conf);
if (ret < 0)
rte_exit(EXIT_FAILURE, "Cannot configure device: err=%d, port=%u\n",
ret, port_id);
/* Set up Rx queues */
for (q = 0; q < rx_rings; q++) {
ret = rte_eth_rx_queue_setup(port_id, q, RX_DESC_DEFAULT,
rte_eth_dev_socket_id(port_id),
NULL, mbuf_pool);
if (ret < 0)
rte_exit(EXIT_FAILURE, "rte_eth_rx_queue_setup: err=%d, port=%u\n",
ret, port_id);
}
/* Set up Tx queues */
for (q = 0; q < tx_rings; q++) {
ret = rte_eth_tx_queue_setup(port_id, q, TX_DESC_DEFAULT,
rte_eth_dev_socket_id(port_id),
NULL);
if (ret < 0)
rte_exit(EXIT_FAILURE, "rte_eth_tx_queue_setup: err=%d, port=%u\n",
ret, port_id);
}
/* Start the Ethernet device */
ret = rte_eth_dev_start(port_id);
if (ret < 0)
rte_exit(EXIT_FAILURE, "rte_eth_dev_start: err=%d, port=%u\n",
ret, port_id);
/* Optional: Enable promiscuous mode */
rte_eth_promiscuous_enable(port_id);
至此,dpdk 程序的基本的初始化完成。
包处理循环
程序初始化完成后,进入程序处理循环,通常为包处理循环,步骤可以简单概括为:收包 -> 包处理 -> 发包。
一个简单的数据包转发循环可能看起来像这样:
/* Main processing loop */
for (;;) {
/* Get burst of packets from Rx queue */
const uint16_t nb_rx = rte_eth_rx_burst(port_id, 0,
pkts_burst, MAX_PKT_BURST);
if (unlikely(nb_rx == 0))
continue;
/* Process packets (in this case, just forward them) */
uint16_t nb_tx = rte_eth_tx_burst(port_id ^ 1, 0,
pkts_burst, nb_rx);
/* Free any unsent packets */
if (unlikely(nb_tx < nb_rx)) {
uint16_t buf;
for (buf = nb_tx; buf < nb_rx; buf++)
rte_pktmbuf_free(pkts_burst[buf]);
}
}
其中rte_eth_rx_burst
函数负责收包,能够一次接收多个报文,函数返回接收到的报文数量;
rte_eth_tx_burst
函数完成发包,能够一次发送多个报文,返回发送的报文数量。
当报文未能全部发送时,剩余的报文需要手动释放处理。
多核数据包处理
DPDK 应用程序可以利用多个 CPU 内核进行数据包处理。典型的方法是使用运行到完成模型(RTC)将包处理循环运行到多个的 CPU 核上。
rte_eal_mp_remote_launch(lcore_main, NULL, CALL_MASTER);
结束
程序主循环结束后,程序需要对资源进行释放,通常包括:设备停用、资源清理释放等,具体代码参考官方示例。
wl
05 / 07用上了!谢!
From Nginx Proxy Manager 登录出错 Bad gateway