前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >深入浅出思科VPP24.02系列:vlib_main函数业务逻辑介绍

深入浅出思科VPP24.02系列:vlib_main函数业务逻辑介绍

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

01=上期内容回顾

在往期的内容中,我们介绍了思科VPP软件核心函数thread0的业务逻辑介绍,

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

深入浅出思科VPP24.02系列:thread0函数业务逻辑介绍

本期我们将继续深入浅出思科vpp24.02系列专题,介绍VPP核心业务vlib_main函数的业务逻辑介绍。

02=VPP函数vlib_main()介绍

函数路径介绍:thread0函数位于vlib/main.c中

函数功能介绍:该负责启动和初始化VPP的各种组件,是VPP程序的入口点。该函数首先加载VPP配置文件,解析配置参数,并进行相应的初始化操作。通过相关的函数完成主函数堆的分配,确保系统资源的正确初始化‌。

代码逻辑展示

代码语言:javascript
代码运行次数:0
复制
vlib_main (vlib_main_t * volatile vm, unformat_input_t * input)
{
  vlib_global_main_t *vgm = vlib_get_global_main ();
  clib_error_t *volatile error;
  vlib_node_main_t *nm = &vm->node_main;

  vm->queue_signal_callback = placeholder_queue_signal_callback;

  /* Reconfigure event log which is enabled very early */
  if (vgm->configured_elog_ring_size &&
      vgm->configured_elog_ring_size != vgm->elog_main.event_ring_size)
    elog_resize (&vgm->elog_main, vgm->configured_elog_ring_size);
  vl_api_set_elog_main (vlib_get_elog_main ());
  (void) vl_api_set_elog_trace_api_messages (1);

  /* Default name. */
  if (!vgm->name)
    vgm->name = "VLIB";

  if ((error = vlib_physmem_init (vm)))
    {
      clib_error_report (error);
      goto done;
    }

  if ((error = vlib_log_init (vm)))
    {
      clib_error_report (error);
      goto done;
    }

  if ((error = vlib_stats_init (vm)))
    {
      clib_error_report (error);
      goto done;
    }

  if ((error = vlib_buffer_main_init (vm)))
    {
      clib_error_report (error);
      goto done;
    }

  if ((error = vlib_thread_init (vm)))
    {
      clib_error_report (error);
      goto done;
    }

  /* Register node ifunction variants */
  vlib_register_all_node_march_variants (vm);

  /* 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. */
  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
  goto done;
    }

  /* Direct call / weak reference, for vlib standalone use-cases */
  if ((error = vpe_api_init (vm)))
    {
      clib_error_report (error);
      goto done;
    }

  if ((error = vlibmemory_init (vm)))
    {
      clib_error_report (error);
      goto done;
    }

  if ((error = map_api_segment_init (vm)))
    {
      clib_error_report (error);
      goto done;
    }

  /* See unix/main.c; most likely already set up */
  if (vgm->init_functions_called == 0)
    vgm->init_functions_called = hash_create (0, /* value bytes */ 0);
  if ((error = vlib_call_all_init_functions (vm)))
    goto done;

  nm->timing_wheel = clib_mem_alloc_aligned (sizeof (TWT (tw_timer_wheel)),
               CLIB_CACHE_LINE_BYTES);

  vec_validate (nm->process_restore_current, 10);
  vec_validate (nm->process_restore_next, 10);
  vec_set_len (nm->process_restore_current, 0);
  vec_set_len (nm->process_restore_next, 0);

  /* Create the process timing wheel */
  TW (tw_timer_wheel_init)
  ((TWT (tw_timer_wheel) *) nm->timing_wheel,
   process_expired_timer_cb /* callback */, 10e-6 /* timer period 10us */,
   ~0 /* max expirations per call */);

  vec_validate (vm->pending_rpc_requests, 0);
  vec_set_len (vm->pending_rpc_requests, 0);
  vec_validate (vm->processing_rpc_requests, 0);
  vec_set_len (vm->processing_rpc_requests, 0);

  /* Default params for the buffer allocator fault injector, if configured */
  if (VLIB_BUFFER_ALLOC_FAULT_INJECTOR > 0)
    {
      vm->buffer_alloc_success_seed = 0xdeaddabe;
      vm->buffer_alloc_success_rate = 0.80;
    }

  if ((error = vlib_call_all_config_functions (vm, input, 0 /* is_early */ )))
    goto done;

  /*
   * Use exponential smoothing, with a half-life of 1 second
   * reported_rate(t) = reported_rate(t-1) * K + rate(t)*(1-K)
   *
   * Sample every 20ms, aka 50 samples per second
   * K = exp (-1.0/20.0);
   * K = 0.95
   */
  vm->damping_constant = exp (-1.0 / 20.0);

  /* Sort per-thread init functions before we start threads */
  vlib_sort_init_exit_functions (&vgm->worker_init_function_registrations);

  /* Call all main loop enter functions. */
  {
    clib_error_t *sub_error;
    sub_error = vlib_call_all_main_loop_enter_functions (vm);
    if (sub_error)
      clib_error_report (sub_error);
  }

  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;
    }

  vlib_main_loop (vm);

done:
  /* Stop worker threads, barrier will not be released */
  vlib_worker_thread_barrier_sync (vm);

  /* 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 vm->main_loop_exit_status;
}

03=VPP函数vlib_main()逻辑解析

vlib_main是vpp的一个核心函数,下面是该函数的详细解释其业务逻辑:

  1. 初始化全局变量和配置:
    • 通过vlib_get_global_main()获取全局主结构体vlib_global_main_t的指针。
    • 设置一个占位符回调函数placeholder_queue_signal_callback用于队列信号。
    • 如果配置了事件日志环(elog ring)的大小,并且当前大小与配置不一致,则调整其大小。
    • 设置日志系统和跟踪API消息的开关。
代码语言:javascript
代码运行次数:0
复制
业务逻辑代码
  vlib_global_main_t *vgm = vlib_get_global_main ();
  clib_error_t *volatile error;
  vlib_node_main_t *nm = &vm->node_main;

  vm->queue_signal_callback = placeholder_queue_signal_callback;

  /* Reconfigure event log which is enabled very early */
  if (vgm->configured_elog_ring_size &&
      vgm->configured_elog_ring_size != vgm->elog_main.event_ring_size)
    elog_resize (&vgm->elog_main, vgm->configured_elog_ring_size);
  vl_api_set_elog_main (vlib_get_elog_main ());
  (void) vl_api_set_elog_trace_api_messages (1);
  1. 设置默认名称:
    • 如果没有设置全局主结构体的名称,则默认设置为"VLIB"。
代码语言:javascript
代码运行次数:0
复制
  业务逻辑代码展示:
  if (!vgm->name)
    vgm->name = "VLIB";
  1. 初始化各个模块:
    • 依次初始化物理内存、日志、统计信息、缓冲区管理、线程等模块。如果在初始化过程中遇到错误,则报告错误并跳转到结束处理。
代码语言:javascript
代码运行次数:0
复制
  if ((error = vlib_physmem_init (vm)))
    {
      clib_error_report (error);
      goto done;
    }

  if ((error = vlib_log_init (vm)))
    {
      clib_error_report (error);
      goto done;
    }

  if ((error = vlib_stats_init (vm)))
    {
      clib_error_report (error);
      goto done;
    }

  if ((error = vlib_buffer_main_init (vm)))
    {
      clib_error_report (error);
      goto done;
    }

  if ((error = vlib_thread_init (vm)))
    {
      clib_error_report (error);
      goto done;
    }
  1. 注册节点和处理函数:
    • 注册所有节点的指令集变体(march variants)和静态节点。
    • 根据用户输入或当前时间设置随机数生成器的种子。
代码语言:javascript
代码运行次数:0
复制
  业务逻辑代码:
  /* Register node ifunction variants */
  vlib_register_all_node_march_variants (vm);

  /* 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);
  1. 初始化节点图和定时器:
    • 初始化节点管理,如果在调试模式下遇到错误,则仅报告错误而不停止执行。
    • 初始化其他模块,如VPE API、内存管理和API段映射。
代码语言:javascript
代码运行次数:0
复制
  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
  goto done;
    }

  /* Direct call / weak reference, for vlib standalone use-cases */
  if ((error = vpe_api_init (vm)))
    {
      clib_error_report (error);
      goto done;
    }

  if ((error = vlibmemory_init (vm)))
    {
      clib_error_report (error);
      goto done;
    }

  if ((error = map_api_segment_init (vm)))
    {
      clib_error_report (error);
      goto done;
    }
  1. 调用初始化和配置函数:
    • 调用所有初始化函数和配置函数,如果在执行过程中遇到错误,则跳转到结束处理。
代码语言:javascript
代码运行次数:0
复制
  if (vgm->init_functions_called == 0)
    vgm->init_functions_called = hash_create (0, /* value bytes */ 0);
  if ((error = vlib_call_all_init_functions (vm)))
    goto done;

  nm->timing_wheel = clib_mem_alloc_aligned (sizeof (TWT (tw_timer_wheel)),
               CLIB_CACHE_LINE_BYTES);
  1. 设置默认参数:
    • 如果启用了缓冲区分配故障注入器,则设置默认参数。
代码语言:javascript
代码运行次数:0
复制
 代码逻辑:
  vec_validate (nm->process_restore_current, 10);
  vec_validate (nm->process_restore_next, 10);
  vec_set_len (nm->process_restore_current, 0);
  vec_set_len (nm->process_restore_next, 0);

  /* Create the process timing wheel */
  TW (tw_timer_wheel_init)
  ((TWT (tw_timer_wheel) *) nm->timing_wheel,
   process_expired_timer_cb /* callback */, 10e-6 /* timer period 10us */,
   ~0 /* max expirations per call */);

  vec_validate (vm->pending_rpc_requests, 0);
  vec_set_len (vm->pending_rpc_requests, 0);
  vec_validate (vm->processing_rpc_requests, 0);
  vec_set_len (vm->processing_rpc_requests, 0);
  1. 准备主循环:
    • 排序线程初始化和退出函数。
    • 调用所有主循环进入函数。
    • 设置主循环退出状态,并进入主循环。
代码语言:javascript
代码运行次数:0
复制
  /* Default params for the buffer allocator fault injector, if configured */
  if (VLIB_BUFFER_ALLOC_FAULT_INJECTOR > 0)
    {
      vm->buffer_alloc_success_seed = 0xdeaddabe;
      vm->buffer_alloc_success_rate = 0.80;
    }

  if ((error = vlib_call_all_config_functions (vm, input, 0 /* is_early */ )))
    goto done;

  /*
   * Use exponential smoothing, with a half-life of 1 second
   * reported_rate(t) = reported_rate(t-1) * K + rate(t)*(1-K)
   *
   * Sample every 20ms, aka 50 samples per second
   * K = exp (-1.0/20.0);
   * K = 0.95
   */
  vm->damping_constant = exp (-1.0 / 20.0);

  /* Sort per-thread init functions before we start threads */
  vlib_sort_init_exit_functions (&vgm->worker_init_function_registrations);
  1. 主循环和退出处理:
    • vlib_main_loop函数是主循环的入口,处理数据包和其他任务。
    • 主循环结束后,同步工作线程,调用所有主循环退出函数,并报告可能存在的错误。
代码语言:javascript
代码运行次数:0
复制
  /* Call all main loop enter functions. */
  {
    clib_error_t *sub_error;
    sub_error = vlib_call_all_main_loop_enter_functions (vm);
    if (sub_error)
      clib_error_report (sub_error);
  }

  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;
    }
  1. 返回值:
    • 函数返回主循环的退出状态。
代码语言:javascript
代码运行次数:0
复制
  vlib_main_loop (vm);

done:
  /* Stop worker threads, barrier will not be released */
  vlib_worker_thread_barrier_sync (vm);

  /* 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 vm->main_loop_exit_status;

03=本章小结

本章节我们主要介绍了VPP的核心函数vlib_main()的业务处理逻辑,其总结为思维导图的模式如下所示:

该函数负责初始化整个系统,设置必要的参数和回调函数,然后进入主循环处理数据包和其他任务。在主循环结束后,它会执行清理工作,释放资源,并报告错误(如果使能的条件下)。小伙伴们,你们学会了吗?

作者简介

作者:通信行业搬砖工

前深圳某大型通信设备厂商从业人员

前H3Com骨干网核心交换路由器人员

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

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

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

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

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