DPDK总结(eal_thread_loop)

rte_eal_remote_launch()

MASTER lcore调用rte_eal_remote_launch()将回调函数和参数注册到lcore_config[].f和lcore_config[].arg中,并通知SLAVE lcore执行该回调函数

/*
 * Send a message to a slave lcore identified by slave_id to call a
 * function f with argument arg. Once the execution is done, the
 * remote lcore switch in FINISHED state.
 */
int
rte_eal_remote_launch(int (*f)(void *), void *arg, unsigned slave_id)
{
	int n;
	char c = 0;
	int m2s = lcore_config[slave_id].pipe_master2slave[1];
	int s2m = lcore_config[slave_id].pipe_slave2master[0];

	if (lcore_config[slave_id].state != WAIT)
		return -EBUSY;

	/* 注册回调函数和参数 */
	lcore_config[slave_id].f = f;
	lcore_config[slave_id].arg = arg;

	/* send message */
	/* 发送消息给SLAVE lcore */
	n = 0;
	while (n == 0 || (n < 0 && errno == EINTR))
		n = write(m2s, &c, 1);
	if (n < 0)
		rte_panic("cannot write on configuration pipe\n");

	/* wait ack */
	/* 等待SLAVE lcore的确认 */
	do {
		n = read(s2m, &c, 1);
	} while (n < 0 && errno == EINTR);

	if (n <= 0)
		rte_panic("cannot read on configuration pipe\n");

	return 0;
}

rte_eal_mp_remote_launch()

/*
 * Check that every SLAVE lcores are in WAIT state, then call
 * rte_eal_remote_launch() for all of them. If call_master is true
 * (set to CALL_MASTER), also call the function on the master lcore.
 */
int
rte_eal_mp_remote_launch(int (*f)(void *), void *arg,
			 enum rte_rmt_call_master_t call_master)
{
	int lcore_id;
	int master = rte_get_master_lcore();

	/* check state of lcores */
	/* 检查每个SLAVE lcore的状态是否为WAIT */
	RTE_LCORE_FOREACH_SLAVE(lcore_id) {
		if (lcore_config[lcore_id].state != WAIT)
			return -EBUSY;
	}

	/* send messages to cores */
	/* 发送消息给每个SLAVE lcore执行回调函数 */
	RTE_LCORE_FOREACH_SLAVE(lcore_id) {
		rte_eal_remote_launch(f, arg, lcore_id);
	}

	/* 若需要,在MASTER lcore上也执行回调函数 */
	if (call_master == CALL_MASTER) {
		lcore_config[master].ret = f(arg);
		lcore_config[master].state = FINISHED;
	}

	return 0;
}

rte_eal_wait_lcore()

/*
 * Wait until a lcore finished its job.
 */
int
rte_eal_wait_lcore(unsigned slave_id)
{
	if (lcore_config[slave_id].state == WAIT)
		return 0;

	/* 等待SLAVE lcore执行完回调函数
	 * 注意:大部分DPDK应用的回调函数都是一个死循环,MASTER lcore会阻塞在这里 */
	while (lcore_config[slave_id].state != WAIT &&
	       lcore_config[slave_id].state != FINISHED)
		rte_pause();

	rte_rmb();

	/* we are in finished state, go to wait state */
	lcore_config[slave_id].state = WAIT; /* 设置SLAVE lcore的状态为WAIT */
	return lcore_config[slave_id].ret;
}

rte_eal_mp_wait_lcore()

/*
 * Do a rte_eal_wait_lcore() for every lcore. The return values are
 * ignored.
 */
void
rte_eal_mp_wait_lcore(void)
{
	unsigned lcore_id;

	/* 等待每个SLAVE lcore执行完回调函数 */
	RTE_LCORE_FOREACH_SLAVE(lcore_id) {
		rte_eal_wait_lcore(lcore_id);
	}
}

eal_thread_loop()

SLAVE lcore的主循环函数

/* main loop of threads */
__attribute__((noreturn)) void *
eal_thread_loop(__attribute__((unused)) void *arg)
{
	char c;
	int n, ret;
	unsigned lcore_id;
	pthread_t thread_id;
	int m2s, s2m;
	char cpuset[RTE_CPU_AFFINITY_STR_LEN];

	thread_id = pthread_self();

	/* retrieve our lcore_id from the configuration structure */
	/* 根据tid找到对应的lcore_id */
	RTE_LCORE_FOREACH_SLAVE(lcore_id) {
		if (thread_id == lcore_config[lcore_id].thread_id)
			break;
	}
	if (lcore_id == RTE_MAX_LCORE)
		rte_panic("cannot retrieve lcore id\n");

	m2s = lcore_config[lcore_id].pipe_master2slave[0];
	s2m = lcore_config[lcore_id].pipe_slave2master[1];

	/* set the lcore ID in per-lcore memory area */
	RTE_PER_LCORE(_lcore_id) = lcore_id;

	/* set CPU affinity */
	/* 绑定SLAVE lcore到logical CPU */
	if (eal_thread_set_affinity() < 0)
		rte_panic("cannot set affinity\n");

	ret = eal_thread_dump_affinity(cpuset, RTE_CPU_AFFINITY_STR_LEN);

	RTE_LOG(DEBUG, EAL, "lcore %u is ready (tid=%x;cpuset=[%s%s])\n",
		lcore_id, (int)thread_id, cpuset, ret == 0 ? "" : "...");

	/* read on our pipe to get commands */
	while (1) {
		void *fct_arg;

		/* wait command */
		/* 等待MASTER lcore的消息 */
		do {
			n = read(m2s, &c, 1);
		} while (n < 0 && errno == EINTR);

		if (n <= 0)
			rte_panic("cannot read on configuration pipe\n");

		lcore_config[lcore_id].state = RUNNING; /* 设置SLAVE lcore的状态为RUNNING */

		/* send ack */
		/* 发送确认给MASTER lcore */
		n = 0;
		while (n == 0 || (n < 0 && errno == EINTR))
			n = write(s2m, &c, 1);
		if (n < 0)
			rte_panic("cannot write on configuration pipe\n");

		if (lcore_config[lcore_id].f == NULL)
			rte_panic("NULL function pointer\n");

		/* call the function and store the return value */
		fct_arg = lcore_config[lcore_id].arg;
		/* 执行MASTER lcore通过rte_eal_remote_launch()注册的回调函数
		 * 注意:大部分DPDK应用的回调函数都是一个死循环,SLAVE lcore会阻塞在这里 */
		ret = lcore_config[lcore_id].f(fct_arg);
		lcore_config[lcore_id].ret = ret;
		rte_wmb();

		/* when a service core returns, it should go directly to WAIT
		 * state, because the application will not lcore_wait() for it.
		 */
		if (lcore_config[lcore_id].core_role == ROLE_SERVICE)
			lcore_config[lcore_id].state = WAIT;
		else
			lcore_config[lcore_id].state = FINISHED; /* 设置SLAVE lcore的状态为FINISHED */
	}

	/* never reached */
	/* pthread_exit(NULL); */
	/* return NULL; */
}
### DPDK 输入节点实现与细节 DPDK (Data Plane Development Kit) 是一组用于快速数据包处理的库和驱动程序集合。对于输入节点而言,主要涉及如何高效接收网络流量并将其传递给应用程序。 #### 数据包接收流程 在网络接口卡(NIC)接收到数据包后,硬件会将这些数据包存储到预先分配好的内存缓冲区中。`rte_eth_rx_burst()` 函数负责从指定端口批量读取多个数据包,并返回实际接收到的数据包数量[^1]。 ```c struct rte_mbuf *pkts_burst[MAX_PKT_BURST]; uint16_t nb_rx = rte_eth_rx_burst(port_id, queue_id, pkts_burst, MAX_PKT_BURST); ``` 这段代码展示了如何调用 `rte_eth_rx_burst()` 来一次性获取一批数据包。其中 `port_id` 和 `queue_id` 分别表示网卡端口号以及队列编号;而 `MAX_PKT_BURST` 则定义了一次最多可以接收多少个数据包。 #### 内存管理机制 为了提高性能,DPDK 使用了无锁环形缓存来管理和重用 mbuf 结构体对象。当不再需要某个已处理完毕的数据包时,应该通过 `rte_pktmbuf_free()` 将其释放回池子中以便后续重复利用。 ```c for (i = 0; i < nb_rx; ++i){ struct rte_mbuf *pkt = pkts_burst[i]; /* Process packet here */ rte_pktmbuf_free(pkt); // Return buffer back to mempool after processing. } ``` 上述例子说明了在完成对单个数据包的操作之后立即执行释放操作的重要性,从而确保资源能够被及时回收再利用。 #### 中断模式 vs 轮询模式 传统 Linux 系统通常依赖于中断通知 CPU 存在一个新的传入帧可用。然而,在高吞吐量场景下频繁触发中断可能会带来显著开销。因此,DPDK 推荐采用轮询方式代替中断——即让应用线程持续不断地查询是否有新到达的数据包等待处理,以此减少上下文切换带来的延迟影响[^2]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值