主线程其实就是 VPP 运行时中第一个调度线程,它是线程 0(vlib_worker_threads[0]
),它的职责是:
-
完成所有 VPP 框架初始化工作;
-
加载并解析配置;
-
创建和初始化子系统(如 buffer pool、interface、plugin、graph、nodes);
-
启动 worker threads;
-
开始 graph node 调度循环。
🔧 初始化主线流程(以 vpp_main()
为起点)
0. 启动点 main()
(vpp/vpp.c
)
int main(int argc, char *argv[]) { return vpp_main(argc, argv); }
1. vpp_main()
入口(vpp/vpp.c)
📌 核心职责:
-
初始化 VPP 核心模块
-
调用 VLIB/VNET 各子系统初始化
-
启动插件
-
进入调度循环
int vpp_main(int argc, char *argv[]) { unformat_input_t input; clib_mem_init_thread_safe(0, 128 << 20); // 初始化 thread-safe 内存 vlib_main_init(&vm); // 初始化主 vlib_main_t 对象 vlib_plugin_early_init(); // 早期插件初始化(加载配置、注册信息) unformat_init_command_line(&input, argv); vlib_main(argv, &input); // 真正进入 vlib 框架入口 }
2. vlib_main()
框架初始化(vlib/main.c)
这是 VPP 主流程的中枢,它完成了绝大部分系统初始化操作。
🧩 关键模块初始化顺序:
步骤 | 模块 | 描述 |
---|---|---|
1 | vlib_worker_thread_init() | 分配线程结构,初始化线程池(main thread + worker threads) |
2 | vlib_physmem_init() | 初始化物理内存池,用于 buffer 分配 |
3 | vlib_frame_queue_init() | 初始化 inter-thread frame 队列(用于 handoff) |
4 | vlib_buffer_main_init() | 初始化 buffer 系统(创建 freelist、预分配) |
5 | vlib_register_all_static_nodes() | 注册所有静态 node(如 device-input, ip4-lookup) |
6 | vlib_node_main_init() | 初始化 node 主结构(创建调度器、记录 node ID) |
7 | vlib_call_all_main_loop_enter_funcs() | 调用所有 main_loop_enter 回调(用于注册模块事件) |
8 | vlib_plugin_config() | 加载插件配置,调用 plugin 注册函数 |
9 | vlib_thread_init() | 启动子线程(worker threads),阻塞直到子线程 ready |
10 | vlib_main_loop() | 启动主线程的调度循环 |
3. Node 注册阶段
节点通过 VLIB_REGISTER_NODE()
宏注册,系统在 vlib_register_all_static_nodes()
时收集所有 .node
段内容。
VLIB_REGISTER_NODE (device_input_node, static) = { .function = device_input, .name = "device-input", ... };
每个节点被赋予唯一 node_index
,其对应的 function
在调度器中被调用。
4. Graph 构建
VPP 使用 graph engine 进行 packet processing,这里构建 DAG(有向无环图),包括:
-
节点之间的 next 配置
-
建立调度路径
-
每个节点维护自己的 runtime state(frame pool、next frame 指针等)
完成后,每个线程将拥有一份独立的 vlib_node_runtime_t[]
。
5. Buffer 初始化(vlib_buffer_main_init()
)
主线程初始化:
-
buffer freelist
-
default buffer pool
-
缓存 preallocated buffers 到每个线程中(避免竞争)
-
配置 I/O buffer sizes,支持多种 payload size
6. Thread 初始化与同步(vlib_thread_init()
)
-
创建 worker 线程数由
workers {}
配置段决定 -
线程创建后进入
vlib_worker_thread_bootstrap_fn()
-
每个线程注册 signal,设置 thread-local storage(TLS)
-
所有线程 ready 后,主线程继续执行
7. Plugin 加载(vlib_plugin_config()
)
-
加载
plugin_path
目录下的所有.so
插件 -
每个插件调用
vlib_register_all_static_nodes()
注册自己的节点 -
调用插件的
vlib_plugin_register()
初始化逻辑
8. Main Loop 启动(vlib_main_loop()
)
主线程开始进入主循环,实际就是图调度器在不断调用每个 active node:
while (1) { dispatch_nodes(); // 选择 active 节点调度执行 process_timers(); // 执行定时任务 handle_signals(); // 接收外部信号 }
🖼️ 总体流程图
+------------------+ | main() | +--------+---------+ | v +------------------+ | vpp_main() | +--------+---------+ | v +---------------------------+ | vlib_main() | +---------------------------+ | init threads | | init buffers | | register nodes | | init plugins | | build graph | | launch workers | | enter dispatch loop | +---------------------------+
✅ 总结:主线程启动顺序一览
阶段 | 说明 |
---|---|
Step 1 | 入口函数 vpp_main() 启动 VLIB |
Step 2 | 初始化内存系统、线程、frame、buffer |
Step 3 | 注册节点、构建 graph、创建调度结构 |
Step 4 | 加载并初始化插件 |
Step 5 | 启动 worker threads 并同步 |
Step 6 | 启动主线程调度循环,等待事件包处理 |