DPDK L2FWD Keepalive 示例分析与测试指南
DPDK L2FWD Keepalive 示例分析与测试指南
DPDK L2FWD Keepalive 示例分析与测试指南
本文档详细分析了 DPDK examples/l2fwd-keepalive/main.c 的代码逻辑,解释了 Keepalive(保活)机制与 Timer(定时器)的协作原理,并提供了测试方案。
1. 核心架构设计
l2fwd-keepalive 是标准二层转发(L2FWD)的增强版,增加了对数据面核心(Lcore)健康状态的监控功能。
1.1 设计思想
- 监控者(Master Core):负责定期检查各个工作核心的时间戳。
- 被监控者(Worker Cores):在处理业务的同时,定期更新自己的时间戳(盖章)。
- 自愈能力:如果监控者发现某个核心超时未响应,会判定其死亡并尝试重新启动该核心的任务。
1.2 关键数据结构
rte_global_keepalive_info:Keepalive 句柄,存储所有核心的状态。rte_keepalive_shm:共享内存结构,用于将核心状态暴露给非 DPDK 的外部进程(如监控代理)。
2. Keepalive 与 Timer 协作机制详解
这部分逻辑是整个应用的核心。Timer 是“节拍器”,Keepalive 是“逻辑状态机”。
2.1 交互流程图 (ASCII)
+-----------------------+ +------------------------+
| Master Core (Main) | | Worker Core (Lcore X)|
+-----------+-----------+ +------------+-----------+
| |
1. 创建 Keepalive 实例 |
2. 注册 Dead Core 回调 |
3. 注册 Relay Callback <------------------[NEW] | (用于同步状态给外部)
4. 注册 Worker Core X --------------------------> | (被加入监控列表)
| |
5. 设置 Timer (周期性) |
(回调=dispatch_pings) |
| |
[ Start Loop ] <------------------------+ |
| |
6. rte_timer_manage() |
(检查时间是否到期) |
| |
[ Timer 触发? ] |
| YES |
v |
7. 执行检查回调: |
rte_keepalive_dispatch_pings() |
| |
[ 检查核心盖章时间 ] |
| |
> 阈值(失败)? ------------------------|-----> 8. rte_keepalive_mark_alive()
/ \ | (Worker 循环盖章)
YES NO | (更新 Last Alive 时间戳)
| | |
判定 DEAD 判定 ALIVE |
| | |
9. 触发 Relay Callback |
(同步状态到共享内存 SHM) |
| |
10.触发 Dead 回调 (dead_core) |
尝试重启 Lcore X |
| |
+-------------------------------+ |
2.2 详细调用链分析
阶段一:监控系统启动 (Master Core)
- 初始化 SHM:
rte_keepalive_shm_create()创建共享内存,允许外部 Python 脚本监控。 - 创建实例:
rte_keepalive_create(&dead_core, shm)。- 这里绑定了核心死亡时的救护车函数:
dead_core。
- 这里绑定了核心死亡时的救护车函数:
- 注册 Relay:
rte_keepalive_register_relay_callback(..., relay_core_state, shm)。- 这是内部状态通往外部世界的桥梁。
- 定时器绑定:
rte_timer_reset()。- 周期:
check_period(例如 10ms)。 - 回调函数:
rte_keepalive_dispatch_pings。这是 DPDK 库内部提供的核心检查逻辑。
- 周期:
阶段二:工人打卡 (Worker Core)
- 注册: 在启动线程前,Master 调用
rte_keepalive_register_core(..., lcore_id)。- 这会在 Keepalive 内部的数组中标记该 lcore_id 为“受监控”。
- 打卡: Worker 的
while(1)循环第一行就是rte_keepalive_mark_alive()。- 底层实现: 这是一个极其轻量的操作。它仅仅是读取当前 TSC (CPU 周期数),然后原子写入到共享内存中的
core_state[lcore_id].last_alive字段。
- 底层实现: 这是一个极其轻量的操作。它仅仅是读取当前 TSC (CPU 周期数),然后原子写入到共享内存中的
阶段三:定期查岗与中继 (Master Core)
- Master 线程在
while循环中调用rte_timer_manage()。 - 当 10ms 时间到,触发
rte_keepalive_dispatch_pings()。 - 核心检查逻辑:
- 遍历所有 Registered 的核心。
- 计算
delta = current_tsc - core_last_alive。
- Relay (中继) 逻辑:
- 在检查过程中,如果状态发生变化(或为了定期同步),库会调用用户注册的 Relay Callback (
relay_core_state)。 - 实现:
relay_core_state函数会调用rte_keepalive_relayed_state,将当前的ALIVE/MISSING/DEAD状态写入到 /dev/shm/ 下的共享内存文件中。 - 意义: 这样外部的 Python 脚本或监控代理就能实时看到核心状态了。
- 在检查过程中,如果状态发生变化(或为了定期同步),库会调用用户注册的 Relay Callback (
阶段四:死而复生 (Recovery)
- 判定 DEAD:
delta超过致命阈值。 - 回调执行:
dead_core函数被 Master 线程执行。 - 重启判断: 代码检查该核心的状态
rte_eal_get_lcore_state(id_core)。- 如果是
WAIT(表示线程已经退出了,比如 break 出去了),则可以安全重启。
- 如果是
- 重启: 调用
rte_eal_remote_launch(l2fwd_launch_one_lcore, ..., id_core)。- 这会重新在这个物理核心上创建一个新的执行上下文,重新进入
l2fwd_main_loop。
- 这会重新在这个物理核心上创建一个新的执行上下文,重新进入
3. Demo 特有的“自杀”逻辑
为了演示监控有效性,代码中包含了一段随机触发线程退出的逻辑:
1
2
3
4
5
6
7
8
9
10
11
12
13
/* 在 l2fwd_main_loop 中 */
uint64_t tsc_initial = rte_rdtsc();
uint64_t tsc_lifetime = rte_rand_max(8 * rte_get_tsc_hz()); // 随机 0-8 秒寿命
while (!terminate_signal_received) {
rte_keepalive_mark_alive(...);
// 如果运行时间超过随机寿命,直接退出循环(模拟死亡)
if (check_period > 0 && cur_tsc - tsc_initial > tsc_lifetime)
break;
// ... 正常转发逻辑 ...
}
4. 使用虚拟网卡 (net_tap) 测试验证
4.1 关键前提:核心数量要求 (Lcores)
重要提示:此程序不支持单核心运行!
- 原因:该程序设计为 Master Core 负责运行 Timer 和监控,Worker Cores 负责转发流量。
- 如果使用
-l 0(单核):Master 核心只会进入 Timer 循环,RTE_LCORE_FOREACH_WORKER循环将为空,导致转发线程根本不会启动,程序看起来像没有任何反应。 - 要求:必须至少提供 2 个逻辑核心(1 个 Master + N 个 Worker)。
4.2 运行命令
使用 net_tap 模拟两个端口,并开启 Keepalive 检测。
1
2
3
4
5
6
7
8
# -l 0,1: 核心 0 监控,核心 1 转发
# -K 10: 10ms 检查周期
# -T 1: 每秒打印一次统计
sudo ./build/l2fwd-keepalive -l 0,1 \
--vdev=net_tap0,iface=dtap0 \
--vdev=net_tap1,iface=dtap1 \
-- \
-p 0x3 -q 1 -K 10 -T 1
4.3 验证步骤
- 观察日志:启动后 0-8 秒内,屏幕会打印
Dead core 1 - restarting..。 - 验证自愈:看到日志后,统计数据(Port statistics)应继续刷新,证明核心 1 已成功复活。
- 外部监控 (验证 Relay):查看
/dev/shm/目录,应存在rte_keepalive_shm_name文件。可以使用hexdump观察其中跳动的时间戳,这正是 Relay Callback 努力工作的证明。
5. 总结
l2fwd-keepalive 通过 Timer(驱动)+ Keepalive(检测)+ Relay Callback(通知)+ Dead Callback(恢复) 构成了一个完整的闭环监控系统。它是构建电信级高可用网络设备的基础组件之一。
This post is licensed under CC BY 4.0 by the author.