前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >深入浅出思科VPP24.02系列:vlib_unix_main初始化介绍

深入浅出思科VPP24.02系列:vlib_unix_main初始化介绍

作者头像
通信行业搬砖工
发布2024-10-10 19:29:25
发布2024-10-10 19:29:25
11600
代码可运行
举报
文章被收录于专栏:网络虚拟化网络虚拟化
运行总次数:0
代码可运行

01、vlib_unix_main初始化介绍

本文章我们将要进行思科VPP(Vector Packet Processing)软件架构初始化流程中设计的核心函数vlib_unix_main初始化介绍,其初始化流程如下所示。

02、分配空间逻辑介绍

介绍给vgm->vlib_mains这个vec分配足够的空间的逻辑,首先我们介绍一下这个参数的工程意义:每个线程维护自己的main

代码语言:javascript
代码运行次数:0
复制
  /* Per-thread Mains */
  vlib_main_t **vlib_mains;

VPP使用DPDK的RTC模式,每个work线程运行自己的业务处理。

涉及到DPDK的RTC模式介绍:

  • 在 run-to-completion 模式中,通过调用API来轮询指定端口的RX描述符以获取报文。紧接着,在同一个core上处理报文,并通过API调用将报文放到接口的TX描述符中以发送报文。

每个逻辑核处理数据包的流程包括以下步骤:

  • 通过PMD报文接收API来获取报文
  • 一次性处理每个数据报文,直到转发阶段
  • 通过PMD发包API将报文发送出去

03、readlink函数介绍

readlink部分的业务逻辑描述:

unix标准函数介绍

代码语言:javascript
代码运行次数:0
复制
#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 设置为下列其中一个值;

错误码:

  • EACCES:拒绝对路径前缀的组件的搜索许可权。
  • EINVAL:指定的文件不是符号链接。
  • EIO:从文件系统读取时发生 I/O 错误。
  • ELOOP:符号链接中存在循环。如果在解析 path 参数期间迂到多个 POSIX_SYMLOOP 符号链接,那么会发出此错误。
  • ENAMETOOLONG:pathname 的长度超过 PATH_MAX 个字符,或者当 _POSIX_NO_TRUNC 生效时, pathname 的某些组件的长度超过 NAME_MAX 个字符。对于符号链接,替代符号链接的路径名字符串的长度超过 PATH_MAX。可以使用 pathconf () 来确定 PATH_MAX 和 NAME_MAX 值。
  • ENOENT:指定的文件不存在。
  • ENOTDIR:路径前缀的组件不是目录。

04、elog日志相关函数介绍

初始化和使能日志相关的操作逻辑

代码语言:javascript
代码运行次数:0
复制
  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初始化逻辑

代码语言:javascript
代码运行次数:0
复制
__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));
}

功能使能逻辑:

代码语言:javascript
代码运行次数:0
复制
/** @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命令行初始化相关的操作逻辑

代码语言:javascript
代码运行次数:0
复制
/* 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、读取插件配置逻辑介绍

读取插件配置逻辑相关的操作逻辑

代码语言:javascript
代码运行次数:0
复制
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 = &in;
  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、加载路径下的插件介绍

加载路径下的插件相关的操作逻辑

代码语言:javascript
代码运行次数:0
复制
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 */ );
}

该部分的关键操作:

代码语言:javascript
代码运行次数:0
复制
  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初始化:创建相关的空间资源

代码语言:javascript
代码运行次数:0
复制
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切换运行栈逻辑:

代码语言:javascript
代码运行次数:0
复制
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做为入口函数:

代码语言:javascript
代码运行次数:0
复制
  i = clib_calljmp (thread0, (uword) vm,
        (void *) (vlib_thread_stacks[0] +
            VLIB_THREAD_STACK_SIZE));
  return i;

clib_calljmp在开启thread0的同时也向thread0这个线程传入了栈;

至此vlib_unix_main函数的初始化流程就介绍结束了,小伙伴们,你们学会了吗?

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-10-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 通信行业搬砖工 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档