DPDK逻辑核与多线程

DPDK逻辑核与多线程

开始了漫长的看代码的道路. 从这篇文章开始, 少则一个函数, 多则一个example. 逐渐学习DPDK自带的一些例子程序. 希望通过这部分的学习, 能够很快实现数据包分析的线程绑定.

[TOC]

逻辑核结构体分析

这个是每个逻辑核的配置文件, 可以通过 core_id 来区分. 里面包含的是否可见, 状态, 以及运行的线程函数.

struct lcore_config {
unsigned detected; /**< true if lcore was detected */
pthread_t thread_id; /**< pthread identifier */
int pipe_master2slave[2]; /**< communication pipe with master */
int pipe_slave2master[2]; /**< communication pipe with master */
lcore_function_t * volatile f; /**< function to call */
void * volatile arg; /**< argument of function */
volatile int ret; /**< return value of function */
volatile enum rte_lcore_state_t state; /**< lcore state */
unsigned socket_id; /**< physical socket id for this lcore */
unsigned core_id; /**< core number on socket for this lcore */
int core_index; /**< relative index, starting from 0 */
rte_cpuset_t cpuset; /**< cpu set which the lcore affinity to */
uint8_t core_role; /**< role of core eg: OFF, RTE, SERVICE */
};

这个是整个运行环境的配置文件, 包含主线程核id, 逻辑核数量以及每个逻辑核的role, 进程优先级状态.

struct rte_config {
uint32_t master_lcore; /**< Id of the master lcore */
uint32_t lcore_count; /**< Number of available logical cores. */
uint32_t service_lcore_count;/**< Number of available service cores. */
enum rte_lcore_role_t lcore_role[RTE_MAX_LCORE]; /**< State of cores. */

/** Primary or secondary configuration */
enum rte_proc_type_t process_type;

/**
* Pointer to memory configuration, which may be shared across multiple
* DPDK instances
*/
struct rte_mem_config *mem_config;
} __attribute__((__packed__));
/**
* The type of process in a linuxapp, multi-process setup
*/
enum rte_proc_type_t {
RTE_PROC_AUTO = -1, /* allow auto-detection of primary/secondary */
RTE_PROC_PRIMARY = 0, /* set to zero, so primary is the default */
RTE_PROC_SECONDARY,

RTE_PROC_INVALID
};

环境抽象层初始化

环境抽象层 environment abstract layer[eal]

是对硬件资源的抽象, 即将硬件资源的细节内容隐藏起来, 只提供一些简单的接口来实现对资源的分配和利用.

eal 初始化函数

int rte_eal_init(argc, argv);   // 实现代码 并未提供

是对环境抽象层的初始化, 只能在 Master_core 上运行, 并且越早执行它越好. 它使所有的逻辑核置于等待状态. 它在main 执行前结束初始化进程. 下面一句话来自代码注释, 并未懂, 等待以后考究:

* When the multi-partition feature is supported, depending on the
* configuration (if CONFIG_RTE_EAL_MAIN_PARTITION is disabled), this
* function waits to ensure that the magic number is set before
* returning. See also the rte_eal_get_configuration() function. Note:
* This behavior may change in the future.

这个初始化函数的返回值是 传递参数的个数 . 如果返回值ret大于等于0, 则初始化成功. 并且, 所有 argv[x], x< ret 的值都会被这个函数改变, 这些值不应该再被应用解释.

如果初始化失败, 会返回-1, 或者报错. 有以下几种错误:

*   Error codes returned via rte_errno:
* EACCES indicates a permissions issue.
*
* EAGAIN indicates either a bus or system resource was not available,
* setup may be attempted again.
*
* EALREADY indicates that the rte_eal_init function has already been
* called, and cannot be called again.
*
* EFAULT indicates the tailq configuration name was not found in
* memory configuration.
*
* EINVAL indicates invalid parameters were passed as argv/argc.
*
* ENOMEM indicates failure likely caused by an out-of-memory condition.
*
* ENODEV indicates memory setup issues.
*
* ENOTSUP indicates that the EAL cannot initialize on this system.
*
* EPROTO indicates that the PCI bus is either not present, or is not
* readable by the eal.
*
* ENOEXEC indicates that a service core failed to launch successfully.

逻辑核与线程相关API

线程启动任务

RTE_LCORE_FOREACH_SLAVE(lcore_id) {
rte_eal_remote_launch(lcore_hello, NULL, lcore_id);
}

其中RTE_LCORE_FOREACH_SLAVE(lcore_id) {} 是遍历所有逻辑核, 以lcore_id 为迭代器.

而最重要的一个api是:


int rte_eal_remote_launch ( lcore_function_t * f,
void * arg,
unsigned slave_id
)

函数参数:

  • lcore_function_t * f 是要调用的线程函数.
    void * arg 是传递给这个线程函数的参数.
    unsigned slave_id 是执行这个线程函数的逻辑核的id. 同时也是这个线程的唯一标识~

返回值:

  • 0: 如果成功的话.
  • (-EBUSY): 如果目标核不是wait状态.

由此我们可以获得以下信息:

  • 遍历所有逻辑核用 RTE_LCORE_FOREACH_SLAVE(lcore_id) { }
RTE_LCORE_FOREACH_SLAVE(lcore_id) { 
// something
}
  • 线程函数的指针类型是 lcore_function_t *
  • 逻辑核id的类型是 unsigned. 他的大小是 4 Bytes

全部调用的一个直接api

int rte_eal_mp_remote_launch	(	lcore_function_t * 	f,
void * arg,
enum rte_rmt_call_master_t call_master
)

前面两个参数已经了解了, 第三个参数需要先了解一下 这个这个结构体:

enum rte_rmt_call_master_t
{
SKIP_MASTER //lcore handler not executed by master core.
CALL_MASTER //lcore handler executed by master core.
}

意思就是, 看看主线程是否也需要这个任务! 跳过, 或者执行!

主线程等待

void rte_eal_mp_wait_lcore(void)

他的作用是: 等待所有从线程执行完毕, 自己才结束, 相当于java中的 join 方法!!! 只能再主线程中使用!

int rte_eal_wait_lcore(unsigned slave_id)

等待 slave_id 这个线程执行完毕, 如果该线程是running 状态, 则等待它执行到 finished状态. 如果这个线程已经是finished状态, 则将他置wait状态.

返回值: 如果是0, 那么这个线程已经是WAIT状态,

线程状态

下面是逻辑核的几种状态:

enum rte_lcore_state_t {
WAIT, /**< waiting a new command */
RUNNING, /**< executing command */
FINISHED, /**< command executed */
};
enum rte_lcore_state_t rte_eal_get_lcore_state	(	unsigned 	slave_id	)

根据 slave_id 得到线程的状态.

线程中得到自己的信息

static unsigned rte_lcore_id	(void )  //得到自己执行的应用线程的id
static unsigned rte_get_master_lcore	(	void )	 //得到master core的id
static unsigned rte_lcore_count	(	void 		)  //得到系统上有多少个 逻辑核(执行单元)
static int rte_lcore_index	(	int 	lcore_id	)	//得到核的index, 是从0 开始记数的

翻译出来可能不准确:

Return the index of the lcore starting from zero. When option -c or -l is given, the index corresponds to the order in the list. For example: -c 0x30, lcore 4 has index 0, and 5 has index 1. -l 22,18 lcore 22 has index 0, and 18 has index 1.

unsigned rte_socket_id	(	void 		)	 //得到这个核的 物理socket 的id
static unsigned rte_lcore_to_socket_id	(	unsigned 	lcore_id	)	//根据 lcore_id 得到
static int rte_lcore_is_enabled	(	unsigned 	lcore_id	)  //返回的起始时 true 和 false
static unsigned rte_get_next_lcore	(	unsigned 	i,   //当前的核id
int skip_master, //是否跳过master
int wrap // 如果到了 max_locre, 是否退回0继续检索
) //得到下一个可用的 lcore
int rte_thread_set_affinity	(	rte_cpuset_t * 	cpusetp	)	 //成功返回0 /否则 -1
void rte_thread_get_affinity	(	rte_cpuset_t * 	cpusetp	)	 // 得到亲和度
int rte_thread_setname	(	pthread_t 	id,
const char * name
) //设置名字的
int rte_lcore_has_role(unsigned int lcore_id,enum rte_lcore_role_t role )

这个需要先了解以下这个结构体:

/**
* The lcore role (used in RTE or not).
*/
enum rte_lcore_role_t {
ROLE_RTE, //被 rte 所使用
ROLE_OFF, // 没有被使用
ROLE_SERVICE, //暂时不知道什么意思
};

所以这个api 的作用: 判断一个逻辑核 是否有 特定的角色, 是 返回0, 否则 为负数.

实战技能

看了这么多, 优点手痒想写个小程序.

任务如下:

  • 每个线程说一句: I love you Hox, I’m core x.

程序代码

#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <sys/queue.h>

#include <rte_memory.h>
#include <rte_memzone.h>
#include <rte_launch.h>
#include <rte_eal.h>
#include <rte_per_lcore.h>
#include <rte_lcore.h>
#include <rte_debug.h>

int lcore_speak(__attribute__((unused)) void * argc)
{
unsigned id = rte_lcore_id();
printf("I love U Hox, I'm %d\n",id);
return 0;
}

int main(int argc, char** argv)
{
int ret = rte_eal_init(argc,argv);
if(ret < 0)
printf("Can't not init eal");

rte_eal_mp_remote_launch(lcore_speak,NULL,CALL_MASTER);

rte_eal_mp_wait_lcore();

int lcore_num = rte_lcore_count();
printf("%d 个 逻辑核向你表白了~\n",lcore_num);
}

效果截图

loveTest