在上文说到,在VPP启动之前,会提前通过宏定义的方式注册各种需要初始化的业务逻辑函数,本文主要介绍VPP的启动流程
VPP的入口函数在src/vpp/vnet/main.c
1. 加载startup.conf,获取配置信息
VPP启动,可以通过命令:如vpp –c /etc/vpp/startup.conf启动
startup.conf包含了VPP默认的配置信息,涉及多种配置参数,每个参数用大括号进行区分:
可以包含的默认配置有:具体可见:https://wiki.fd.io/view/VPP/Command-line_Arguments
VPP启动时主要关心:pluginpath和heapsize,获取plugin路径和堆大小
2. clib_mem_init:
根据默认配置,分配主堆内存
3. vpe_main_init:
设置VPP提示,
注册srp_ips_process_node, srp_input_node, srp_control_input_node3个node节点
此处没细研究,主要理解应该是实现旁路操作系统,实现网络数据直接在网卡和VPP之间传递做准备
4. vlib_unix_main
核心的启动业务逻辑就在该函数中:
int vlib_unix_main (int argc, char *argv[])
{
vlib_main_t *vm = &vlib_global_main; /*one and only time for this! */
vlib_thread_main_t *tm = &vlib_thread_main;
unformat_input_t input;
u8*thread_stacks;
clib_error_t *e;
inti;
vm->argv = (u8 **) argv;
vm->name = argv[0];
vm->heap_base = clib_mem_get_heap ();
ASSERT (vm->heap_base);
unformat_init_command_line (&input, (char **) vm->argv);
//获取plugin信息并填充结构体
if ((e = vlib_plugin_config (vm, &input)))
{
clib_error_report (e);
return 1;
}
unformat_free (&input);
//加载启动插件功能
i =vlib_plugin_early_init (vm);
if(i)
return i;
unformat_init_command_line (&input, (char **) vm->argv);
if(vm->init_functions_called == 0)
vm->init_functions_called = hash_create (0, /* value bytes */ 0);
//启动所有宏定义VLIB_EARLY_CONFIG_FUNCTION和VLIB_CONFIG_FUNCTION注册的链表函数
e = vlib_call_all_config_functions (vm, &input, 1 /* early */ );
if(e != 0)
{
clib_error_report (e);
return 1;
}
unformat_free (&input);
/*
*allocate n x VLIB_THREAD_STACK_SIZE stacks, aligned to a
*VLIB_THREAD_STACK_SIZE boundary
*See also: os_get_cpu_number() in vlib/vlib/threads.c
*/
thread_stacks = clib_mem_alloc_aligned
((uword) tm->n_thread_stacks * VLIB_THREAD_STACK_SIZE,
VLIB_THREAD_STACK_SIZE);
vec_validate (vlib_thread_stacks, tm->n_thread_stacks - 1);
for(i = 0; i < vec_len (vlib_thread_stacks); i++)
{
vlib_thread_stacks[i] = thread_stacks;
/*
* Disallow writes to the bottom page of the stack, to
* catch stack overflows.
*/
if (mprotect (thread_stacks, clib_mem_get_page_size (), PROT_READ) <0)
clib_unix_warning("thread stack");
thread_stacks += VLIB_THREAD_STACK_SIZE;
}
//这里启动thread0
i =clib_calljmp (thread0, (uword) vm,
(void *) (vlib_thread_stacks[0] +
VLIB_THREAD_STACK_SIZE));
return i;
}
5. vlib/unix/main.c的thread0()函数
调用vlib/中的vlib_main函数
vlib_main (vlib_main_t * volatile vm,unformat_input_t * input)
{
clib_error_t *volatile error;
vm->queue_signal_callback = dummy_queue_signal_callback;
clib_time_init (&vm->clib_time);
/*Turn on event log. */
if(!vm->elog_main.event_ring_size)
vm->elog_main.event_ring_size = 128 << 10;
elog_init (&vm->elog_main, vm->elog_main.event_ring_size);
elog_enable_disable (&vm->elog_main, 1);
/*Default name. */
if(!vm->name)
vm->name = "VLIB";
vec_validate (vm->buffer_main, 0);
vlib_buffer_cb_init (vm);
if((error = vlib_thread_init (vm)))
{
clib_error_report (error);
goto done;
}
//启动宏定义VLIB_REGISTER_NODE注册的所有node节点功能函数,功能比较简单,就是将各个节点加到一个vlib_node_main_t-> nodes
/*Register static nodes so that init functions may use them. */
vlib_register_all_static_nodes (vm);
/*Set seed for random number generator.
Allow user to specify seed to make random sequence deterministic. */
if(!unformat (input, "seed %wd", &vm->random_seed))
vm->random_seed = clib_cpu_time_now ();
clib_random_buffer_init (&vm->random_buffer, vm->random_seed);
/*Initialize node graph. */
//初始化node graph,看代码基本就是填充节点关系字段
if((error = vlib_node_main_init (vm)))
{
/* Arrange for graph hook up error to not be fatal when debugging. */
if (CLIB_DEBUG > 0)
clib_error_report(error);
else
gotodone;
}
/*See unix/main.c; most likely already set up */
if(vm->init_functions_called == 0)
vm->init_functions_called = hash_create (0, /* value bytes */ 0);
//启动所有宏定义VLIB_INIT_FUNCTION:注册的链表函数
if ((error = vlib_call_all_init_functions(vm)))
goto done;
/*Create default buffer free list. */
vlib_buffer_get_or_create_free_list (vm,
VLIB_BUFFER_DEFAULT_FREE_LIST_BYTES,
"default");
switch (clib_setjmp (&vm->main_loop_exit,VLIB_MAIN_LOOP_EXIT_NONE))
{
case VLIB_MAIN_LOOP_EXIT_NONE:
vm->main_loop_exit_set = 1;
break;
case VLIB_MAIN_LOOP_EXIT_CLI:
goto done;
default:
error = vm->main_loop_error;
goto done;
}
if((error = vlib_call_all_config_functions (vm, input, 0 /* is_early */ )))
goto done;
/*Call all main loop enter functions. */
{
clib_error_t *sub_error;
//启动宏定义VLIB_MAIN_LOOP_ENTER_FUNCTION指定的函数,这里主要启动thread.c中的start_workers
sub_error =vlib_call_all_main_loop_enter_functions (vm);
if (sub_error)
clib_error_report (sub_error);
}
//处理节点的核心功能函数
vlib_main_loop (vm);
done:
/*Call all exit functions. */
{
clib_error_t *sub_error;
sub_error = vlib_call_all_main_loop_exit_functions (vm);
if (sub_error)
clib_error_report (sub_error);
}
if(error)
clib_error_report (error);
return 0;
}