01、vlib_unix_main初始化介绍
本文章我们将要进行思科VPP(Vector Packet Processing)软件架构初始化流程中设计的核心函数vlib_unix_main初始化介绍,其初始化流程如下所示。
02、分配空间逻辑介绍
介绍给vgm->vlib_mains这个vec分配足够的空间的逻辑,首先我们介绍一下这个参数的工程意义:每个线程维护自己的main
/* Per-thread Mains */
vlib_main_t **vlib_mains;
VPP使用DPDK的RTC模式,每个work线程运行自己的业务处理。
涉及到DPDK的RTC模式介绍:
每个逻辑核处理数据包的流程包括以下步骤:
03、readlink函数介绍
readlink部分的业务逻辑描述:
unix标准函数介绍
#define _POSIX1_SOURCE 2
#include <unistd.h>
int readlink(const char *path,
char *buf, size_t bufsiz);
#define _POSIX_C_SOURCE 200112L
#include <unistd.h>
ssize_t readlink(const char *__restrict__path,
char *__restrict__buf, size_t bufsiz);
将符号链接 path 的内容放在缓冲区 buf中。缓冲区的大小由 bufsiz设置。存储在 buf 中的结果不包含终止 NULL 字符。
如果缓冲区太小而无法包含符号链接的值,那么该值将截断为缓冲区的大小 (bufsiz)。如果返回的值是缓冲区的大小,请使用 lstat () 来确定符号链接的实际大小。
返回值介绍:
如果成功,当 bufsiz 大于 0 时, readlink () 将返回缓冲区中放置的字节数。当 bufsiz 为 0 并且 readlink () 成功完成时,它将返回符号链接中包含的字节数,并且不会更改缓冲区。
如果返回的值等于 bufsiz,那么可以使用 bufsiz的 0 值来确定 lstat () 或 readlink () 的符号链接的内容。
如果失败,那么 readlink () 将返回 -1 并将 errno 设置为下列其中一个值;
错误码:
04、elog日志相关函数介绍
初始化和使能日志相关的操作逻辑
vgm->configured_elog_ring_size = 128 << 10;
elog_init (vlib_get_elog_main (), vgm->configured_elog_ring_size);
elog_enable_disable (vlib_get_elog_main (), 1);
elog初始化逻辑
__clib_export void
elog_init (elog_main_t * em, u32 n_events)
{
clib_memset (em, 0, sizeof (em[0]));
em->lock = 0;
if (n_events > 0)
elog_alloc (em, n_events);
clib_time_init (&em->cpu_timer);
em->n_total_events_disable_limit = ~0;
/* Make track 0. */
em->default_track.name = "default";
elog_track_register (em, &em->default_track);
elog_time_now (&em->init_time);
em->string_table_hash = hash_create_string (0, sizeof (uword));
}
功能使能逻辑:
/** @brief Enable or disable event logging
@param em elog_main_t *
*/
always_inline void
elog_enable_disable (elog_main_t * em, int is_enabled)
{
em->n_total_events = 0;
em->n_total_events_disable_limit = is_enabled ? ~0 : 0;
}
05、elog日志相关函数介绍
unix命令行初始化相关的操作逻辑
/* Setup for unformat of Unix style command line. */
__clib_export void
unformat_init_command_line (unformat_input_t * input, char *argv[])
{
uword i;
unformat_init (input, 0, 0);
/* Concatenate argument strings with space in between. */
for (i = 1; argv[i]; i++)
{
vec_add (input->buffer, argv[i], strlen (argv[i]));
if (argv[i + 1])
vec_add1 (input->buffer, ' ');
}
}
06、读取插件配置逻辑介绍
读取插件配置逻辑相关的操作逻辑
clib_error_t *
vlib_plugin_config (vlib_main_t * vm, unformat_input_t * input)
{
plugin_main_t *pm = &vlib_plugin_main;
clib_error_t *error = 0;
unformat_input_t in;
unformat_init (&in, 0, 0);
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
{
u8 *s, *v;
if (unformat (input, "%s %v", &s, &v))
{
if (strncmp ((const char *) s, "plugins", 8) == 0)
{
if (vec_len (in.buffer) > 0)
vec_add1 (in.buffer, ' ');
vec_add (in.buffer, v, vec_len (v));
}
}
else
{
error = clib_error_return (0, "unknown input '%U'",
format_unformat_error, input);
goto done;
}
vec_free (v);
vec_free (s);
}
done:
input = ∈
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
{
unformat_input_t sub_input;
u8 *s = 0;
if (unformat (input, "path %s", &s))
pm->plugin_path = s;
else if (unformat (input, "add-path %s", &s))
pm->plugin_path_add = s;
else if (unformat (input, "name-filter %s", &s))
pm->plugin_name_filter = s;
else if (unformat (input, "vat-path %s", &s))
pm->vat_plugin_path = s;
else if (unformat (input, "vat-name-filter %s", &s))
pm->vat_plugin_name_filter = s;
else if (unformat (input, "plugin default %U",
unformat_vlib_cli_sub_input, &sub_input))
{
pm->plugins_default_disable =
unformat (&sub_input, "disable") ? 1 : 0;
unformat_free (&sub_input);
}
else if (unformat (input, "plugin %s %U", &s,
unformat_vlib_cli_sub_input, &sub_input))
{
error = config_one_plugin (vm, (char *) s, &sub_input);
unformat_free (&sub_input);
if (error)
goto done2;
}
else
{
error = clib_error_return (0, "unknown input '%U'",
format_unformat_error, input);
{
vec_free (s);
goto done2;
}
}
}
done2:
unformat_free (&in);
return error;
}
07、加载路径下的插件介绍
加载路径下的插件相关的操作逻辑
int
vlib_plugin_early_init (vlib_main_t * vm)
{
plugin_main_t *pm = &vlib_plugin_main;
pm->logger =
vlib_log_register_class_rate_limit ("plugin", "load",
0x7FFFFFFF /* aka no rate limit */ );
if (pm->plugin_path == 0)
pm->plugin_path = format (0, "%s", vlib_plugin_path);
if (pm->plugin_path_add)
pm->plugin_path = format (pm->plugin_path, ":%s", pm->plugin_path_add);
pm->plugin_path = format (pm->plugin_path, "%c", 0);
PLUGIN_LOG_DBG ("plugin path %s", pm->plugin_path);
pm->plugin_by_name_hash = hash_create_string (0, sizeof (uword));
pm->plugin_overrides_by_name_hash = hash_create_string (0, sizeof (uword));
pm->vlib_main = vm;
return vlib_load_new_plugins (pm, 1 /* from_early_init */ );
}
该部分的关键操作:
pm->plugin_by_name_hash = hash_create_string (0, sizeof (uword));
pm->plugin_overrides_by_name_hash = hash_create_string (0, sizeof (uword));
pm->vlib_main = vm;
然后通过vlib_load_new_plugins函数,加载插件路径下的所有插件。后续文章中,我们会详细描述加载的逻辑。
08、创建线程相关的逻辑介绍
创建线程相关的操作逻辑:
thread stack初始化:创建相关的空间资源
u8 *vlib_thread_stack_init (uword thread_index)
{
void *stack;
ASSERT (thread_index < vec_len (vlib_thread_stacks));
stack = clib_mem_vm_map_stack (VLIB_THREAD_STACK_SIZE,
CLIB_MEM_PAGE_SZ_DEFAULT,
"thread stack: thread %u", thread_index);
if (stack == CLIB_MEM_VM_MAP_FAILED)
clib_panic ("failed to allocate thread %u stack", thread_index);
vlib_thread_stacks[thread_index] = stack;
return stack;
}
thread stack切换运行栈逻辑:
static_always_inline void
vlib_process_start_switch_stack (vlib_main_t * vm, vlib_process_t * p)
{
#ifdef CLIB_SANITIZE_ADDR
void *stack = p ? (void *) p->stack : vlib_thread_stacks[vm->thread_index];
u32 stack_bytes =
p ? (1ULL < p->log2_n_stack_bytes) : VLIB_THREAD_STACK_SIZE;
__sanitizer_start_switch_fiber (&vm->asan_stack_save, stack, stack_bytes);
#endif
}
这个是vpp 引入工具进行内存检测功能的特性。如果小伙伴们感兴趣,后续我们再介绍如何使用该功能。
09、clib_calljmp逻辑介绍
clib_calljmp相关的操作逻辑:调用了clib_calljmp实现了协程,clib_calljmp函数会调用thread0做为入口函数:
i = clib_calljmp (thread0, (uword) vm,
(void *) (vlib_thread_stacks[0] +
VLIB_THREAD_STACK_SIZE));
return i;
clib_calljmp在开启thread0的同时也向thread0这个线程传入了栈;
至此vlib_unix_main函数的初始化流程就介绍结束了,小伙伴们,你们学会了吗?