VPP-main() 源码学习
VPP初始化
VLIB_INIT_FUNCTION用来定义构造函数,注册函数到vlib_main_t->init_function_registrations,这个链表在main()函数之前创建。
vlib_main()-> vlib_call_all_init_functions()注册的函数在这里被调用初始化,最后执行函数vlib_main_loop()。
像这样由宏定义和构造函数创建的全局链表的方式还有如下几个:
· VLIB_API_INIT_FUNCTION
· VLIB_CLI_COMMAND
· VLIB_CONFIG_FUNCTION
· VLIB_EARLY_CONFIG_FUNCTION
· VLIB_MAIN_LOOP_ENTER_FUNCTION
· VLIB_MAIN_LOOP_EXIT_FUNCTION
· VLIB_REGISTER_NODE
vpp/vnet/main.c的main()函数
程序的入口,设置vlib_plugin_main.handoff_structure_get_cb函数指针,指向vpp/vnet/main.c中的函数vnet_get_handoff_structure。
vlib/unix/plugin.c中的vnet_get_handoff_structure()函数调用上面的的函数指针handoff_structure_get_cb。
vnet_get_handoff_structure() 定义了一个静态变量,这个静态变量包含了主要数据指针,比如vlib_main,vnet_main和ethernet_main(看代码没有vlib_main)。每个插件注册的时候,都会通过vlib_plugin_register()将以上数据传递给插件。
最后调用vlib_unix_main()。
vlib/unix/main.c的vlib_unix_main()函数
vlib_plugin_early_init()函数会通过dlopen加载插件目录下的所有插件,这个目录可以通过命令行指定。默认的插件路径是 /usr/lib/vpp_plugins。
dlopen每个插件后,VPP会获取函数vlib_plugin_register的符号地址,所以每个插件都要求实现该函数,之前说过这个函数会传递非常重要的数据。
vlib_call_all_config_functions()函数解析所有的命令行选项,并且针对前期需求配置。
为以下线程创建线程栈,主要有三种类型的线程需要实现:
普通线程:比如统计采集。
EAL线程:处理包的工作。
Processes:这些都是定期执行、相互协作的多线程。比如DHCP租期续订的线程等。VPP主线程的超时到期之后会执行这些。
最后,该函数跳转到thread0()函数。
vlib/unix/main.c的thread0()函数
调用vlib/main.c的vlib_main()函数
vlib/main.c的vlib_main()函数
VLIB_REGISTER_NODE定义图节点,注册到vlib_main_t->node_registrations,vlib_register_all_static_nodes()遍历这个链表,创建图结点(不是连接,是创建)。
VLIB_INIT_FUNCTION声明的函数,由vlib_call_all_init_functions()调用初始化。
如果结点被创建,vlib/node.c的vlib_node_main_init()会对图结点进行初始化。
VLIB_MAIN_LOOP_ENTER_FUNCTION注册一个链表,vlib_call_all_main_loop_enter_functions()函数遍历该链表。
调用vlib_main_loop()
vlib/main.c的vlib_main_loop()函数
创建前面提到的相互协作的多线程,在while(1)循环中处理不同类型的图结点。
· VLIB_NODE_TYPE_PRE_INPUT:类似DBG_CLI的结点
· VLIB_NODE_TYPE_INPUT:这些是主要结点,主要从网卡或者硬件加速器获取数据包
· 进程等待信号,这个很重要,因为所有的客户端都要通过共享内存和VPP通信。客户端向共享内存发送一些API消息,并且向VPP发送信号(SIGUSR1)。
输入结点组织数据包,并且将他们发送到合适的中间结点。由dispatch_pending_node()进一步处理这些数据包。
1 int 2 main (int argc, char *argv[]) 3 { 4 int i; 5 vlib_main_t *vm = &vlib_global_main; 6 void vl_msg_api_set_first_available_msg_id (u16); 7 uword main_heap_size = (1ULL << 30); 8 u8 *sizep; 9 u32 size; 10 int main_core = 1; 11 cpu_set_t cpuset; 12 13 .......... 14 15 /* 16 * Look for and parse the "heapsize" config parameter. 17 * Manual since none of the clib infra has been bootstrapped yet. 18 * 19 * Format: heapsize <nn>[mM][gG] 20 */ 21 22 for (i = 1; i < (argc - 1); i++) 23 { 24 if (!strncmp (argv[i], "plugin_path", 11)) 25 { 26 if (i < (argc - 1)) 27 vlib_plugin_path = argv[++i]; 28 } 29 if (!strncmp (argv[i], "test_plugin_path", 16)) 30 { 31 if (i < (argc - 1)) 32 vat_plugin_path = argv[++i]; 33 } 34 else if (!strncmp (argv[i], "heapsize", 8)) 35 { 36 sizep = (u8 *) argv[i + 1]; 37 size = 0; 38 while (*sizep >= '0' && *sizep <= '9') 39 { 40 size *= 10; 41 size += *sizep++ - '0'; 42 } 43 if (size == 0) 44 { 45 fprintf 46 (stderr, 47 "warning: heapsize parse error '%s', use default %lld\n", 48 argv[i], (long long int) main_heap_size); 49 goto defaulted; 50 } 51 52 main_heap_size = size; 53 54 if (*sizep == 'g' || *sizep == 'G') 55 main_heap_size <<= 30; 56 else if (*sizep == 'm' || *sizep == 'M') 57 main_heap_size <<= 20; 58 } 59 else if (!strncmp (argv[i], "main-core", 9)) 60 { 61 if (i < (argc - 1)) 62 { 63 errno = 0; 64 unsigned long x = strtol (argv[++i], 0, 0); 65 if (errno == 0) 66 main_core = x; 67 } 68 } 69 } 70 71 defaulted: 72 73 /* set process affinity for main thread */ 74 CPU_ZERO (&cpuset); 75 CPU_SET (main_core, &cpuset); 76 pthread_setaffinity_np (pthread_self (), sizeof (cpu_set_t), &cpuset); 77 78 /* Set up the plugin message ID allocator right now... */ 79 vl_msg_api_set_first_available_msg_id (VL_MSG_FIRST_AVAILABLE); 80 81 /* Allocate main heap */ 82 if (clib_mem_init_thread_safe (0, main_heap_size)) 83 { 84 vm->init_functions_called = hash_create (0, /* value bytes */ 0); 85 vpe_main_init (vm); 86 return vlib_unix_main (argc, argv); 87 } 88 else 89 { 90 { 91 int rv __attribute__ ((unused)) = 92 write (2, "Main heap allocation failure!\r\n", 31); 93 } 94 return 1; 95 } 96 }
vlib_unix_main函数
1 int 2 vlib_unix_main (int argc, char *argv[]) 3 { 4 vlib_main_t *vm = &vlib_global_main; /* one and only time for this! */ 5 unformat_input_t input; 6 clib_error_t *e; 7 int i; 8 9 vm->argv = (u8 **) argv; 10 vm->name = argv[0]; 11 vm->heap_base = clib_mem_get_heap (); 12 vm->heap_aligned_base = (void *) 13 (((uword) vm->heap_base) & ~(VLIB_FRAME_ALIGN - 1)); 14 ASSERT (vm->heap_base); 15 16 unformat_init_command_line (&input, (char **) vm->argv); 17 if ((e = vlib_plugin_config (vm, &input))) 18 { 19 clib_error_report (e); 20 return 1; 21 } 22 unformat_free (&input); 23 24 i = vlib_plugin_early_init (vm); 25 if (i) 26 return i; 27 28 unformat_init_command_line (&input, (char **) vm->argv); 29 if (vm->init_functions_called == 0) 30 vm->init_functions_called = hash_create (0, /* value bytes */ 0); 31 e = vlib_call_all_config_functions (vm, &input, 1 /* early */ ); 32 if (e != 0) 33 { 34 clib_error_report (e); 35 return 1; 36 } 37 unformat_free (&input); 38 39 /* always load symbols, for signal handler and mheap memory get/put backtrace */ 40 clib_elf_main_init (vm->name); 41 42 vec_validate (vlib_thread_stacks, 0); 43 vlib_thread_stack_init (0); 44 45 __os_thread_index = 0; 46 vm->thread_index = 0; 47 48 i = clib_calljmp (thread0, (uword) vm, 49 (void *) (vlib_thread_stacks[0] + 50 VLIB_THREAD_STACK_SIZE)); 51 return i; 52 }
thread0 -> vlib_main() 下面介绍vlib_main函数
vlib_main
1 int 2 vlib_main (vlib_main_t * volatile vm, unformat_input_t * input) 3 { 4 clib_error_t *volatile error; 5 vlib_node_main_t *nm = &vm->node_main; 6 7 vm->queue_signal_callback = dummy_queue_signal_callback; 8 9 clib_time_init (&vm->clib_time); 10 11 /* Turn on event log. */ 12 if (!vm->elog_main.event_ring_size) 13 vm->elog_main.event_ring_size = 128 << 10; 14 elog_init (&vm->elog_main, vm->elog_main.event_ring_size); 15 elog_enable_disable (&vm->elog_main, 1); 16 vl_api_set_elog_main (&vm->elog_main); 17 (void) vl_api_set_elog_trace_api_messages (1); 18 19 /* Default name. */ 20 if (!vm->name) 21 vm->name = "VLIB"; 22 23 if ((error = vlib_physmem_init (vm))) 24 { 25 clib_error_report (error); 26 goto done; 27 } 28 29 if ((error = vlib_map_stat_segment_init (vm))) 30 { 31 clib_error_report (error); 32 goto done; 33 } 34 35 if ((error = vlib_buffer_main_init (vm))) 36 { 37 clib_error_report (error); 38 goto done; 39 } 40 41 if ((error = vlib_thread_init (vm))) 42 { 43 clib_error_report (error); 44 goto done; 45 } 46 47 /* Register static nodes so that init functions may use them. */ 48 vlib_register_all_static_nodes (vm); 49 50 /* Set seed for random number generator. 51 Allow user to specify seed to make random sequence deterministic. */ 52 if (!unformat (input, "seed %wd", &vm->random_seed)) 53 vm->random_seed = clib_cpu_time_now (); 54 clib_random_buffer_init (&vm->random_buffer, vm->random_seed); 55 56 /* Initialize node graph. */ 57 if ((error = vlib_node_main_init (vm))) 58 { 59 /* Arrange for graph hook up error to not be fatal when debugging. */ 60 if (CLIB_DEBUG > 0) 61 clib_error_report (error); 62 else 63 goto done; 64 } 65 66 /* Direct call / weak reference, for vlib standalone use-cases */ 67 if ((error = vpe_api_init (vm))) 68 { 69 clib_error_report (error); 70 goto done; 71 } 72 73 if ((error = vlibmemory_init (vm))) 74 { 75 clib_error_report (error); 76 goto done; 77 } 78 79 if ((error = map_api_segment_init (vm))) 80 { 81 clib_error_report (error); 82 goto done; 83 } 84 85 /* See unix/main.c; most likely already set up */ 86 if (vm->init_functions_called == 0) 87 vm->init_functions_called = hash_create (0, /* value bytes */ 0); 88 if ((error = vlib_call_all_init_functions (vm))) 89 goto done; 90 91 nm->timing_wheel = clib_mem_alloc_aligned (sizeof (TWT (tw_timer_wheel)), 92 CLIB_CACHE_LINE_BYTES); 93 94 vec_validate (nm->data_from_advancing_timing_wheel, 10); 95 _vec_len (nm->data_from_advancing_timing_wheel) = 0; 96 97 /* Create the process timing wheel */ 98 TW (tw_timer_wheel_init) ((TWT (tw_timer_wheel) *) nm->timing_wheel, 99 0 /* no callback */ , 100 10e-6 /* timer period 10us */ , 101 ~0 /* max expirations per call */ ); 102 103 vec_validate (vm->pending_rpc_requests, 0); 104 _vec_len (vm->pending_rpc_requests) = 0; 105 vec_validate (vm->processing_rpc_requests, 0); 106 _vec_len (vm->processing_rpc_requests) = 0; 107 108 if ((error = vlib_call_all_config_functions (vm, input, 0 /* is_early */ ))) 109 goto done; 110 111 /* Sort per-thread init functions before we start threads */ 112 vlib_sort_init_exit_functions (&vm->worker_init_function_registrations); 113 114 /* Call all main loop enter functions. */ 115 { 116 clib_error_t *sub_error; 117 sub_error = vlib_call_all_main_loop_enter_functions (vm); 118 if (sub_error) 119 clib_error_report (sub_error); 120 } 121 122 switch (clib_setjmp (&vm->main_loop_exit, VLIB_MAIN_LOOP_EXIT_NONE)) 123 { 124 case VLIB_MAIN_LOOP_EXIT_NONE: 125 vm->main_loop_exit_set = 1; 126 break; 127 128 case VLIB_MAIN_LOOP_EXIT_CLI: 129 goto done; 130 131 default: 132 error = vm->main_loop_error; 133 goto done; 134 } 135 136 vlib_main_loop (vm); 137 138 done: 139 /* Call all exit functions. */ 140 { 141 clib_error_t *sub_error; 142 sub_error = vlib_call_all_main_loop_exit_functions (vm); 143 if (sub_error) 144 clib_error_report (sub_error); 145 } 146 147 if (error) 148 clib_error_report (error); 149 150 return 0; 151 }