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

深入浅出思科VPP24.02系列:统计模块vlib_stats_init逻辑介绍

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

01=上期内容回顾

在往期的内容中,我们介绍了思科VPP软件初始化内存的核心函数vlib_log_init()函数的业务逻辑介绍,

Ubuntu系统编译思科VPP24.02演示

Ubuntu系统运行VPP24.02系列:startup.conf配置文件解读

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

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

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

深入浅出思科VPP24.02系列:内存初始化vlib_physmem_init逻辑介绍

深入浅出思科VPP24.02系列:日志模块vlib_log_init逻辑介绍

本期我们将继续深入浅出思科vpp24.02系列专题,介绍VPP的统计功能初始化的函数的业务逻辑介绍。

02=vlib_stats_init函数介绍

在往期的内容中,我们介绍了思科VPP软件对日志功能的初始化的函数vlib_stats_init()的业务逻辑介绍,其在vlib_main()中备调用的。

统计模块初始化流程如下所示:

代码语言:javascript
代码运行次数:0
复制
clib_error_t *vlib_stats_init (vlib_main_t *vm)
{
  vlib_stats_segment_t *sm = vlib_stats_get_segment ();
  vlib_stats_shared_header_t *shared_header;
  vlib_stats_collector_reg_t reg = {};

  uword memory_size, sys_page_sz;
  int mfd;
  char *mem_name = "stat segment";
  void *heap, *memaddr;

  memory_size = sm->memory_size;
  if (memory_size == 0)
    memory_size = STAT_SEGMENT_DEFAULT_SIZE;

  if (sm->log2_page_sz == CLIB_MEM_PAGE_SZ_UNKNOWN)
    sm->log2_page_sz = CLIB_MEM_PAGE_SZ_DEFAULT;

  mfd = clib_mem_vm_create_fd (sm->log2_page_sz, mem_name);

  if (mfd == -1)
    return clib_error_return (0, "stat segment memory fd failure: %U",
            format_clib_error, clib_mem_get_last_error ());
  /* Set size */
  if ((ftruncate (mfd, memory_size)) == -1)
    {
      close (mfd);
      return clib_error_return (0, "stat segment ftruncate failure");
    }

  memaddr = clib_mem_vm_map_shared (0, memory_size, mfd, 0, mem_name);

  if (memaddr == CLIB_MEM_VM_MAP_FAILED)
    return clib_error_return (0, "stat segment mmap failure");

  sys_page_sz = clib_mem_get_page_size ();

  heap =
    clib_mem_create_heap (((u8 *) memaddr) + sys_page_sz,
        memory_size - sys_page_sz, 1 /* locked */, mem_name);
  sm->heap = heap;
  sm->memfd = mfd;

  sm->directory_vector_by_name = hash_create_string (0, sizeof (uword));
  sm->shared_header = shared_header = memaddr;

  shared_header->version = STAT_SEGMENT_VERSION;
  shared_header->base = memaddr;

  sm->stat_segment_lockp = clib_mem_alloc (sizeof (clib_spinlock_t));
  sm->locking_thread_index = ~0;
  sm->n_locks = 0;
  clib_spinlock_init (sm->stat_segment_lockp);

  /* Set up the name to counter-vector hash table */
  sm->directory_vector =
    vec_new_heap (typeof (sm->directory_vector[0]), STAT_COUNTERS, heap);
  sm->dir_vector_first_free_elt = CLIB_U32_MAX;

  shared_header->epoch = 1;

  /* Scalar stats and node counters */
#define _(E, t, n, p)                                                         \
  strcpy (sm->directory_vector[STAT_COUNTER_##E].name, p "/" #n);             \
  sm->directory_vector[STAT_COUNTER_##E].type = STAT_DIR_TYPE_##t;
  foreach_stat_segment_counter_name
#undef _
    /* Save the vector in the shared segment, for clients */
    shared_header->directory_vector = sm->directory_vector;

  vlib_stats_register_mem_heap (heap);

  reg.collect_fn = vector_rate_collector_fn;
  reg.private_data = vlib_stats_add_gauge ("/sys/vector_rate");
  reg.entry_index =
    vlib_stats_add_counter_vector ("/sys/vector_rate_per_worker");
  vlib_loops_stats_counter_index =
    vlib_stats_add_counter_vector ("/sys/loops_per_worker");
  vlib_stats_register_collector_fn (&reg);
  vlib_stats_validate (reg.entry_index, 0, vlib_get_n_threads ());
  vlib_stats_validate (vlib_loops_stats_counter_index, 0,
           vlib_get_n_threads ());

  return 0;
}

函数声明:clib_error_t *vlib_stats_init (vlib_main_t *vm);

返回值:此处应该返回clib_error_t 类型的更佳。

03=函数工程意义分析

1、统计功能的主要结构体:首先,通过vlib_stats_get_segment()获取统计信息段的引用(vlib_stats_segment_t *sm)。这个段用于存储统计信息。

代码语言:javascript
代码运行次数:0
复制
  vlib_stats_segment_t *sm = vlib_stats_get_segment ();
  vlib_stats_shared_header_t *shared_header;
  vlib_stats_collector_reg_t reg = {};
代码语言:javascript
代码运行次数:0
复制
//结构体释义
typedef struct
{
  /* 这是一个指向 vlib_stats_collector_t 类型,
   用于收集和管理统计数据的采集器。*/
  vlib_stats_collector_t *collectors;

  /* 通过名称快速查找统计信息目录项 */
  uword *directory_vector_by_name;
  /* 表示统计信息目录向量*/
  vlib_stats_entry_t *directory_vector;
  /* 表示目录向量中第一个空闲元素的位置*/
  u32 dir_vector_first_free_elt;

  /* 表示统计信息更新的时间间隔*/
  f64 update_interval;
  /* 用于统计信息段的自旋锁,用于多线程环境下的同步*/
  clib_spinlock_t *stat_segment_lockp;
  u32 locking_thread_index;/* 表示当前持有锁的线程索引*/
  u32 n_locks; /* 表示锁的数量*/
  clib_socket_t *socket;  /* socket*/
  u8 *socket_name;  /* socket name*/
  ssize_t memory_size; /* 分配的空间大小*/
  clib_mem_page_sz_t log2_page_sz; /* 内存page的大小*/
  u8 node_counters_enabled; /* 使能开关标识*/
  void *heap;  /* 指向堆内存区域的指针 */
  vlib_stats_shared_header_t
    *shared_header; /* pointer to shared memory segment */
  int memfd; /* 内存文件描述符*/

} vlib_stats_segment_t;

2、设置内存大小和页面大小:如果统计信息段的内存大小,在未设置时,则使用默认值。同样,如果页面大小未知,则使用默认页面大小。

代码语言:javascript
代码运行次数:0
复制
uword memory_size, sys_page_sz;
int mfd;
char *mem_name = "stat segment";
void *heap, *memaddr;

memory_size = sm->memory_size;
if (memory_size == 0)
  memory_size = STAT_SEGMENT_DEFAULT_SIZE;

if (sm->log2_page_sz == CLIB_MEM_PAGE_SZ_UNKNOWN)
  sm->log2_page_sz = CLIB_MEM_PAGE_SZ_DEFAULT;

3、创建内存文件描述符:通过clib_mem_vm_create_fd()创建一个内存文件描述符(mfd),用于映射共享内存。如果创建失败,则返回错误。

代码语言:javascript
代码运行次数:0
复制
mfd = clib_mem_vm_create_fd (sm->log2_page_sz, mem_name);

if (mfd == -1)
  return clib_error_return (0, "stat segment memory fd failure: %U",
            format_clib_error, clib_mem_get_last_error ());

4、设置内存大小:使用ftruncate()设置文件描述符对应文件的大小为memory_size。如果失败,则关闭文件描述符并返回错误。

代码语言:javascript
代码运行次数:0
复制
/* Set size */
if ((ftruncate (mfd, memory_size)) == -1)
{
  close (mfd);
  return clib_error_return (0, "stat segment ftruncate failure");
}

函数ftruncate 注释:

ftruncate()会将参数fd指定的文件大小改为参数length指定的大小。

参数fd为已打开的文件描述词,而且必须是以写入模式打开的文件。

如果原来的文件大小比参数length大,则超过的部分会被删去。

5、映射共享内存:通过clib_mem_vm_map_shared()将共享内存映射到进程的地址空间。如果映射失败,则返回错误。

代码语言:javascript
代码运行次数:0
复制
memaddr = clib_mem_vm_map_shared (0, memory_size, mfd, 0, mem_name);

if (memaddr == CLIB_MEM_VM_MAP_FAILED)
  return clib_error_return (0, "stat segment mmap failure");

6、创建内存堆:在共享内存的基础上,创建一个内存堆(heap),用于动态内存分配。内存堆的起始地址跳过了一个系统页面大小(sys_page_sz),以避免潜在的地址空间对齐问题。

代码语言:javascript
代码运行次数:0
复制
sys_page_sz = clib_mem_get_page_size ();
heap = clib_mem_create_heap (((u8 *) memaddr) + sys_page_sz,
        memory_size - sys_page_sz, 1 /* locked */, mem_name);

7、初始化统计信息段:设置统计信息段的各种参数,包括内存堆、文件描述符、锁、目录向量等。

代码语言:javascript
代码运行次数:0
复制
sm->heap = heap;
sm->memfd = mfd;

sm->directory_vector_by_name = hash_create_string (0, sizeof (uword));
sm->shared_header = shared_header = memaddr;

shared_header->version = STAT_SEGMENT_VERSION;
shared_header->base = memaddr;
sm->stat_segment_lockp = clib_mem_alloc (sizeof (clib_spinlock_t));
sm->locking_thread_index = ~0;
sm->n_locks = 0;
clib_spinlock_init (sm->stat_segment_lockp);

8、初始化目录向量:创建一个字符串哈希表(directory_vector_by_name),用于根据名称快速查找统计信息。同时,初始化一个数组(directory_vector),用于存储统计信息的元数据。

代码语言:javascript
代码运行次数:0
复制
/* Set up the name to counter-vector hash table */
sm->directory_vector =
    vec_new_heap (typeof (sm->directory_vector[0]), STAT_COUNTERS, heap);
sm->dir_vector_first_free_elt = CLIB_U32_MAX;

shared_header->epoch = 1;

9、设置共享头部:在共享内存的起始位置设置共享头部(shared_header),包括版本号、基地址等信息。

代码语言:javascript
代码运行次数:0
复制
/* Scalar stats and node counters */
#define _(E, t, n, p)                                                         \
  strcpy (sm->directory_vector[STAT_COUNTER_##E].name, p "/" #n);             \
  sm->directory_vector[STAT_COUNTER_##E].type = STAT_DIR_TYPE_##t;
  foreach_stat_segment_counter_name
#undef _
/* Save the vector in the shared segment, for clients */
shared_header->directory_vector = sm->directory_vector;

10、注册统计信息收集器:通过vlib_stats_register_collector_fn()注册一个统计信息收集器,该收集器定期收集并更新统计信息。

代码语言:javascript
代码运行次数:0
复制
vlib_stats_register_mem_heap (heap);
reg.collect_fn = vector_rate_collector_fn;

11、添加统计项:使用宏foreach_stat_segment_counter_name遍历并添加所有预定义的统计项到目录向量中。这些统计项包括各种性能指标,如向量处理速率、循环次数等。

代码语言:javascript
代码运行次数:0
复制
reg.collect_fn = vector_rate_collector_fn;
reg.private_data = vlib_stats_add_gauge ("/sys/vector_rate");
reg.entry_index =
    vlib_stats_add_counter_vector ("/sys/vector_rate_per_worker");
vlib_loops_stats_counter_index =
    vlib_stats_add_counter_vector ("/sys/loops_per_worker");
vlib_stats_register_collector_fn (&reg);
vlib_stats_validate (reg.entry_index, 0, vlib_get_n_threads ());
vlib_stats_validate (vlib_loops_stats_counter_index, 0, vlib_get_n_threads ());

12、注册内存堆:通过vlib_stats_register_mem_heap()注册内存堆,以便统计信息收集系统可以监控内存使用情况。

13返回值:如果所有步骤都成功完成,函数返回0表示成功。

建议修改为返回clib_error_t 里面的参数

04=文章总结介绍

本文章介绍了VPP统计信息收集系统的核心初始化过程,其逻辑函数vlib_stats_init()函数,它设置了所需的内存结构、初始化了相关的数据结构和变量,并注册了统计信息收集器,为后续的章节我们会继续介绍相关功能的使用场景。

好了,本次介绍就到此为止。小伙伴们,你们学会了吗?

作者简介

作者:通信行业搬砖工

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

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

前某全球500强通信公司通信砖家

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

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

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

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

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