前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >learning:vpp unix cli 处理流程1

learning:vpp unix cli 处理流程1

作者头像
dpdk-vpp源码解读
发布2023-03-07 17:29:11
1.1K0
发布2023-03-07 17:29:11
举报
文章被收录于专栏:DPDK VPP源码分析DPDK VPP源码分析

初始化

1、vpp 配置及资源初始化流程简单说明:

这里需要了解vpp启动过程中存在初始化宏函数的执行顺序。当前unix cli相关资源的使用就依赖这个顺序来保证的。下面先来了解一下:

1、early config function 宏注册函数首先执行,主要获取startup配置文件中配置信息,init函数或其他初始化资源需要使用的配置,需要提前读取到。 比如buffers(获取报文缓存区资源大小,需要在vlib_buffer_main_init函数前)、cpu(cpu核绑定数据,需要在vlib_thread_init函数之前)等。

代码语言:javascript
复制
VLIB_EARLY_CONFIG_FUNCTION (unix_config, "buffers");

2、init funtion类函数执行初始化资源,大部分函数都使用此函数进行初始化,主要是一些报文转发过程中需要的一些内存资源。有些资源必须在config function配置之前进行初始化。必须vpp cli相关的资源。

代码语言:javascript
复制
VLIB_INIT_FUNCTION (linux_epoll_input_init);

3、confg function类函数,读取非early startup.conf 文件配置资源信息。

代码语言:javascript
复制
VLIB_CONFIG_FUNCTION (dpdk_config, "dpdk");

4、worker init 类函数在start worker线程前,有些node节点的running数据需要在创建线程前进行初始化,主要是因为在创建worker线程时,需要对node节点进行copy(每个worker核都有自己独立一套转发node资源,避免加锁。)。比如下面l2_input_classify_worker举例

代码语言:javascript
复制
VLIB_WORKER_INIT_FUNCTION (l2_input_classify_worker_init);/*以l2_input_classify_worker为例*/

5、执行main loop 报文调度处理前一些函数。

比如下面start_workers 用来创建worker及相应的资源。详细的可以看一下公众号以前的文章--创建自己的核绑定线程

代码语言:javascript
复制
VLIB_MAIN_LOOP_ENTER_FUNCTION (start_workers);
2、unix cli 相关资源初始化
2.1 创建epool资源,并设置file_main->file_update文件操作函数

vpp最新版本中epool已经支持了多核模式,但是在linux_epoll_input_init初始化时只创建了main核的epool_fd套接字。具体代码如下:

代码语言:javascript
复制
clib_error_t *
linux_epoll_input_init (vlib_main_t * vm)
{
  linux_epoll_main_t *em;
  clib_file_main_t *fm = &file_main;
  vlib_thread_main_t *tm = vlib_get_thread_main ();
  /*每个worker线程对应一个linux_epoll_mains,这里cache一致性问题,使用64字节d对齐 */
  vec_validate_aligned (linux_epoll_mains, tm->n_vlib_mains,
            CLIB_CACHE_LINE_BYTES);

  vec_foreach (em, linux_epoll_mains)
  {
    /* Allocate some events.申请epool_wait 返回的事件集合 */
    vec_resize (em->epoll_events, VLIB_FRAME_SIZE);

    if (linux_epoll_mains == em)
      {/*当前只为main核创建epool文件描述符*/
    em->epoll_fd = epoll_create (1);
    if (em->epoll_fd < 0)
      return clib_error_return_unix (0, "epoll_create");
      }
    else/*其他workers核未创建*/
      em->epoll_fd = -1;
  }
  /*设置全局file_main file_update函数*/
  fm->file_update = linux_epoll_file_update;

  return 0;
}
/*main函数执行前注册linux_epoll_input_init函数*/
VLIB_INIT_FUNCTION (linux_epoll_input_init);
2.2 unix cli server 创建并加入epool监听中

由unix_cli_config 函数完成unix cli server 端socket创建并使用vppinfra/file模块相关api加入到epool监听中。

代码语言:javascript
复制
VLIB_CONFIG_FUNCTION (unix_cli_config, "unix-cli");
static clib_error_t *
unix_cli_config (vlib_main_t * vm, unformat_input_t * input)
{
         ......
         /*设置server及PF_local套接字*/
         s->flags = CLIB_SOCKET_F_IS_SERVER |    /* listen, don't connect */
    CLIB_SOCKET_F_ALLOW_GROUP_WRITE;    /* PF_LOCAL socket only */
      /*socket 初始化*/
      error = clib_socket_init (s);
      /*socket 设置回调处理函数、文件描述符、描述信息*/
      template.read_function = unix_cli_listen_read_ready;
      template.file_descriptor = s->fd;
      template.description = format (0, "cli listener %s", s->config);
      /*添加到file_main中,会调用file_update函数加入到epool中*/
      clib_file_add (fm, &template);
}

这里需要注意:clib_file_add 返回值是在file_mian->file_pool 内存池中的索引,因为unix cli server是不无需释放的,所以并没有报错返回数值。在vppsb代码在enable tap-inject后,会创建一个套接字接收内核-》vpp的报文。但是并没有存储clib_file_add 的返回值,会导致pool资源泄漏问题。下面是enable/disable/enable tap-inject操作后信息:很明显已经存在资源泄漏。

代码语言:javascript
复制
learning_vpp# show unix files 
 FD Thread         Read        Write        Error File Name                        Description
 11      0            0            0            0 socket:[49423]                   stats segment listener /run/vpp/stats.sock
 13      0            1            0            0 pipe:[49426]                     DPDK logging pipe
 33      0            2            0            0 socket:[49438]                   cli listener 0.0.0.0:5002
 34      0            0            0            0 socket:[49440]                   socksvr /run/vpp/api.sock
 35      0          156            1            0 socket:[49668]                   127.0.0.1:45350
 37      0            0            0            0 /dev/net/tun                     
 38      0            0            0            0 /dev/net/tun                     
 39      0            0            0            0 /dev/net/tun                     
 40      0           36            0            0 socket:[49705]                   
 37      0            0            0            0 /dev/net/tun                     
 38      0            0            0            0 /dev/net/tun                     
 39      0            0            0            0 /dev/net/tun  

cli listener 0.0.0.0:5002:就是创建后cli server监听描述符

3.3 main loop循环调度模块轮询方式启动epool_wait监听套接字

由linux_epoll_input_node节点轮询方式执行epool_wait等待unix cli客户端的连接请求。

代码语言:javascript
复制
/* *epool node节点未pre input类型* */
VLIB_REGISTER_NODE (linux_epoll_input_node,static) = {
  .function = linux_epoll_input,
  .type = VLIB_NODE_TYPE_PRE_INPUT,
  .name = "unix-epoll-input",
};
static_always_inline uword
linux_epoll_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
              vlib_frame_t * frame, u32 thread_index)
{
     .....
     /*epoll_pwait 用于轮询I/O事件的发生*/
     static sigset_t unblock_all_signals;
     n_fds_ready = epoll_pwait (em->epoll_fd,
       em->epoll_events,vec_len (em->epoll_events),
       timeout_ms, &unblock_all_signals);
    for (e = em->epoll_events; e < em->epoll_events + n_fds_ready; e++)
    {
      u32 i = e->data.u32;
      clib_file_t *f;
      clib_error_t *errors[4];
      int n_errors = 0;

      /*
       * Under rare scenarios, epoll may still post us events for the
       * deleted file descriptor. We just deal with it and throw away the
       * events for the corresponding file descriptor.
       */
      f = fm->file_pool + i;
      if (PREDICT_FALSE (pool_is_free (fm->file_pool, f)))
    {/*异常判断 pool资源已被释放无法处理信息*/
      .....
    }
      else if (PREDICT_TRUE (!(e->events & EPOLLERR)))
    {
      if (e->events & EPOLLIN)
        {/*处理读事件*/
          f->read_events++;
          errors[n_errors] = f->read_function (f);
          /* Make sure f is valid if the file pool moves */
          if (pool_is_free_index (fm->file_pool, i))
        continue;
          f = pool_elt_at_index (fm->file_pool, i);
          n_errors += errors[n_errors] != 0;
        }
      if (e->events & EPOLLOUT)
        {/*处理写事件*/
          f->write_events++;
          errors[n_errors] = f->write_function (f);
          n_errors += errors[n_errors] != 0;
        }
    }
        else /*处理异常记录*/
    {
      if (f->error_function)
        {
          f->error_events++;
          errors[n_errors] = f->error_function (f);
          n_errors += errors[n_errors] != 0;
        }
      else
        close (f->file_descriptor);
    }
}

我们可以通过vpp cli show unix errors来查询异常信息记录

代码语言:javascript
复制
learning_vpp# show unix errors 
no Unix errors so far #表示当前没有异常发生

至此,unix cli server已经建立完成等待cli连接。

相关结构体关系:如下图所示

相关原图放在github上,有兴趣的自己下载观看:https://github.com/jin13417/dpdk-vpp-learning/tree/main/new_map

处理cli连接请求

vpp unix cli-listen中提供了三种配置方式

代码语言:javascript
复制
unix {
  #cli-listen /run/vpp/cli.sock 
  #unix域套接字。只支持使用vppctl命令登录
  #cli-listen localhost:5002 
  #INADDR_LOOPBACK, 也就是绑定地址LOOPBAC, 往往是127.0.0.1, 只能收到127.0.0.1上面的连接请求。
  #只支持本地Telnet 0 5002 登录
  cli-listen 0.0.0.0:5002 
  #INADDR_ANY是ANY,是绑定地址0.0.0.0上的监听, 能收到任意一块网卡的连接。支持远程telnet 登录
}

下面用例中使用第二种配置方式,当在Telnet 0 5002 登录,在linux_epool_input中epool_wait监听到unix cli listen fd可读事件后调用unix_cli_listen_read_ready进行相应的处理。如下所示:

代码语言:javascript
复制
/*epool监听到unix cli listen fd可读事件后调用unix_cli_listen_read_ready处理函数*/
unix_cli_listen_read_ready()
|   |---clib_socket_accept (s, &client)
|      |---client->fd = accept (server->fd, 0, 0);/*接收telnet连接请求*/
|      |---设置客户端fd非阻塞模式,保存客户端地址信息,设置client->flag客
|      |.   户端标识并设置对应的处理函数。
|   |----cf_index = unix_cli_file_add (cm, client_name, client.fd);
|        |---关键处理函数,下一节再详细分析,创建一个porcess类型node的节点为
|       |   处理cli会话的输入信息。
|  |---默认要以字符模式运行Telnet会话,需要设置telnet一些基本信息。

总结

本文主要介绍了unix cli命令行server端初始化流程及处理cli链接请求流程。cli会话建立以后的动作后续再详细介绍。在阅读源码中发现vpp在代码在使用vppinfra基础库中函数还是不够安全,所以我们不能直接copy相关的处理逻辑,有时间的话还是要用心阅读一下底层的实现逻辑,了解其底层内存使用分布情况。

相关vppinfra文章请阅读:vppinfra--- file.h: unix file handling

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

本文分享自 DPDK VPP源码分析 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、vpp 配置及资源初始化流程简单说明:
  • 2、unix cli 相关资源初始化
    • 2.1 创建epool资源,并设置file_main->file_update文件操作函数
      • 2.2 unix cli server 创建并加入epool监听中
      • 3.3 main loop循环调度模块轮询方式启动epool_wait监听套接字
      • 处理cli连接请求
      • 总结
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档