1、Hello World例程
先搬一段代码,后面再逐步分析。
/* 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(__rte_unused void *arg)
{
unsigned lcore_id;
lcore_id = rte_lcore_id();
printf("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);
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;
}
首先,调用rte_eal_init()函数来初始化环境抽象层(EAL),此函数只能在主lcore中调用,并会将从lcore置位等待状态。当EAL层初始化完毕,程序准备好启动lcore上的功能。本例中定义lcore_hello()函数来打印当前icore的ID,其中rte_lcore_id()函数用来获取执行单元的线程ID。
之后,通过RTE_LCORE_FOREACH_SLAVE()宏来遍历除了主lcore以外的所有运行中的lcore。
最后,通过rte_eal_remote_launch()函数在lcore中启动传入的函数。
2、基础转发例程
介绍一下转发程序框架。
首先,初始化环境抽象层(EAL)。
int ret = rte_eal_init(argc, argv);
if (ret < 0)
rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
之后,调用rte_mempool_create(…)还申请内存池,以用来存放mbuf供应用使用。
mbuf_pool = rte_mempool_create("MBUF_POOL",
NUM_MBUFS * nb_ports,
MBUF_SIZE,
MBUF_CACHE_SIZE,
sizeof(struct rte_pktmbuf_pool_private),
rte_pktmbuf_pool_init, NULL,
rte_pktmbuf_init, NULL,
rte_socket_id(),
0);
rte_mempool_create(…)这个函数看起来实在是让人头大,因为它的参数实在是太多了,其函数原型如下:
struct rte_mempool* rte_mempool_create ( const char * name,
unsigned n,
unsigned elt_size,
unsigned cache_size,
unsigned private_data_size,
rte_mempool_ctor_t * mp_init,
void * mp_init_arg,
rte_mempool_obj_cb_t * obj_init,
void * obj_init_arg,
int socket_id,
unsigned flags
)
下面来逐一解释各个参数的含义:
-
name:mempool的名字。
-
n:mempool中的元素的个数。n = (2^q - 1)比较适宜。
-
elt_size:代表每个元素的大小。
-
cache_size:如果cache_size非零,rte_mempool库将会通过维护单逻辑核的目标缓存来尝试限制通用无锁池的访问。这个参数必须小于等于CONFIG_RTE_MEMPOOL_CACHE_MAX_SIZE 和 n / 1.5。建议的cache_size为“n取模cache_size == 0”:如果不这样的话,一些元素会一直留在pool中并且永远不会被用到。访问单逻辑核表肯定比访问多生产者/消费者池要快。cache_size设置成0的时候,缓存被关闭;可以用来避免在缓存中丢失对象。
-
private_data_size:添加在mempool结构后面的私有数据的大小。
-
mp_init:在对象初始化之前,用来初始化pool的函数的指针。如果需要,用户可以用来初始化私有数据。不需要的时候,可以将其设置为NULL。
-
mp_init_arg:mempool构造函数的数据的指针。
-
obj_init:每个对象初始化pool的函数指针。用户可以用来设置对象中的元数据。不用的时候可以将其设置为NULL。obj_init()函数用mempool指针、init_arg和对象数作为参数。
-
obj_init_arg:对象构造函数的参数的指针。
-
socket_id:在NUMA模式下的socket的标识符。如果保留区域没有NUMA约束,socket_id可以设置为SOCKET_ID_ANY 。
-
flags:flags可以是如下flag的OR关系:
- MEMPOOL_F_NO_SPREAD:默认情况下,对象地址被分配在RAM通道之间:pool的申请器将会在对象之间添加填充,实现基于硬件配置。如果设置了这个flag,申请器将会将他们对齐到缓存行。(内存对齐)
- MEMPOOL_F_NO_CACHE_ALIGN:不内存对齐。
- MEMPOOL_F_SP_PUT:设置的时候,调用 rte_mempool_put() 或rte_mempool_put_bulk()的时候,是单生产者模式,否则,是多生产者模式。
- MEMPOOL_F_SC_GET:设置的时候,调用 rte_mempool_get() 或rte_mempool_get_bulk()的时候,是单消费者模式,否则,是多消费者模式。
- MEMPOOL_F_NO_IOVA_CONTIG:设置的时候,分配的对象在IO内存中不一定是连续的。
函数返回rte_mempool结构,其定义如下:
struct rte_mempool {
/*
* Note: this field kept the RTE_MEMZONE_NAMESIZE size due to ABI
* compatibility requirements, it could be changed to
* RTE_MEMPOOL_NAMESIZE next time the ABI changes
*/
char name[RTE_MEMZONE_NAMESIZE];
RTE_STD_C11
union {
void *pool_data;
uint64_t pool_id;
};
void *pool_config;
const struct rte_memzone *mz;
unsigned int flags;
int socket_id;
uint32_t size;
uint32_t cache_size;
uint32_t elt_size;
uint32_t header_size;
uint32_t trailer_size;
unsigned private_data_size;
int32_t ops_index;
struct rte_mempool_cache *local_cache;
uint32_t populated_size;
struct rte_mempool_objhdr_list elt_list;
uint32_t nb_mem_chunks;
struct rte_mempool_memhdr_list mem_list;
#ifdef RTE_LIBRTE_MEMPOOL_DEBUG
struct rte_mempool_debug_stats stats[RTE_MAX_LCORE];
#endif
} __rte_cache_aligned;
随后,通用用户定义的prot_init()函数来进行端口的初始化:
RTE_ETH_FOREACH_DEV(portid) {
if (port_init(portid, mbuf_pool) != 0) {
rte_exit(EXIT_FAILURE,
"Cannot init port %" PRIu8 "\n", portid);
}
}
初始化完毕之后,lcore_main()就可以在逻辑核上面执行。下面分别对port_init()与lcore_main()进行说明。
port_init()函数如下:
static inline int
port_init(uint16_t port, struct rte_mempool *mbuf_pool)
{
struct rte_eth_conf port_conf =