思科VPP源码分析(CLI支持分析)

来源:互联网 发布:wps是什么软件下载 编辑:程序博客网 时间:2024/06/05 09:03

基本概念

这个只是辅助部分,大多数人不关系,我就随便写写

核心函数

//主线程会调用unix_cli_config,开始cli部分的初始化工作VLIB_CONFIG_FUNCTION (unix_cli_config, "unix-cli");
/** Handle configuration directives in the @em unix section. */static clib_error_t *unix_cli_config (vlib_main_t * vm, unformat_input_t * input){  unix_main_t *um = &unix_main;  unix_cli_main_t *cm = &unix_cli_main;  int flags;  clib_error_t *error = 0;  unix_cli_file_t *cf;  u32 cf_index;  struct termios tio;  struct sigaction sa;  struct winsize ws;  u8 *term;  /* We depend on unix flags being set. */  //确保unix_config执行且只执行了一次  if ((error = vlib_call_config_function (vm, unix_config)))    return error;  if (um->flags & UNIX_FLAG_INTERACTIVE)    {      //支持交互模式      /* Set stdin to be non-blocking. */      /*非阻塞模式,UNIX_CLI_STDIN_FD会交由VLIB_NODE_TYPE_PRE_INPUT类型的      unix-epoll-input node来监视*/      if ((flags = fcntl (UNIX_CLI_STDIN_FD, F_GETFL, 0)) < 0)    flags = 0;      (void) fcntl (UNIX_CLI_STDIN_FD, F_SETFL, flags | O_NONBLOCK);      /*生成或者复用一个VLIB_NODE_TYPE_PROCESS类型node,并调度激活node,等待来      处理该cli session*/      cf_index = unix_cli_file_add (cm, "stdin", UNIX_CLI_STDIN_FD);      cf = pool_elt_at_index (cm->cli_file_pool, cf_index);      cm->stdin_cli_file_index = cf_index;      //下面是调整cli界面参数的,暂时不分析了。      /* If stdin is a tty and we are using chacracter mode, enable       * history on the CLI and set the tty line discipline accordingly. */      if (isatty (UNIX_CLI_STDIN_FD) && um->cli_line_mode == 0)    {      /* Capture terminal resize events */      memset (&sa, 0, sizeof (sa));      sa.sa_handler = unix_cli_resize_interrupt;      if (sigaction (SIGWINCH, &sa, 0) < 0)        clib_panic ("sigaction");      /* Retrieve the current terminal size */      ioctl (UNIX_CLI_STDIN_FD, TIOCGWINSZ, &ws);      cf->width = ws.ws_col;      cf->height = ws.ws_row;      if (cf->width == 0 || cf->height == 0)        /* We have a tty, but no size. Stick to line mode. */        goto notty;      /* Setup the history */      cf->history_limit = um->cli_history_limit;      cf->has_history = cf->history_limit != 0;      /* Setup the pager */      cf->no_pager = um->cli_no_pager;      /* We're going to be in char by char mode */      cf->line_mode = 0;      /* Save the original tty state so we can restore it later */      tcgetattr (UNIX_CLI_STDIN_FD, &um->tio_stdin);      um->tio_isset = 1;      /* Tweak the tty settings */      tio = um->tio_stdin;      /* echo off, canonical mode off, ext'd input processing off */      tio.c_lflag &= ~(ECHO | ICANON | IEXTEN);      tio.c_cc[VMIN] = 1;   /* 1 byte at a time */      tio.c_cc[VTIME] = 0;  /* no timer */      tcsetattr (UNIX_CLI_STDIN_FD, TCSAFLUSH, &tio);      /* See if we can do ANSI/VT100 output */      term = (u8 *) getenv ("TERM");      if (term != NULL)        cf->ansi_capable = unix_cli_terminal_type (term,                               strlen ((char *)                                   term));    }      else    {    notty:      /* No tty, so make sure these things are off */      cf->no_pager = 1;      cf->history_limit = 0;      cf->has_history = 0;      cf->line_mode = 1;    }      /* Send banner and initial prompt */      //一些欢迎信息拷贝到cf->output_vector中,更新epoll状态,等待时机输出。      unix_cli_file_welcome (cm, cf);    }  //下面是CLI socket支持  /* If we have socket config, LISTEN, otherwise, don't */  clib_socket_t *s = &um->cli_listen_socket;  if (s->config && s->config[0] != 0)    {      /* CLI listen. */      unix_file_t template = { 0 };      s->flags = SOCKET_IS_SERVER;  /* listen, don't connect */      //socket初始化,监听模式,代码不难      error = clib_socket_init (s);      if (error)    return error;      template.read_function = unix_cli_listen_read_ready;      template.file_descriptor = s->fd;      //socket加入到epoll      unix_file_add (um, &template);    }  /* Set CLI prompt. */  if (!cm->cli_prompt)    cm->cli_prompt = format (0, "VLIB: ");  return 0;}
//该函数启动一个CLI session,关键是要提前理解VLIB_NODE_TYPE_PROCESS类型node运行机制/** Store a new CLI session. * @param name The name of the session. * @param fd   The file descriptor for the session I/O. * @return The session ID. */static u32unix_cli_file_add (unix_cli_main_t * cm, char *name, int fd){  unix_main_t *um = &unix_main;  unix_cli_file_t *cf;  unix_file_t template = { 0 };  vlib_main_t *vm = um->vlib_main;  vlib_node_t *n;  name = (char *) format (0, "unix-cli-%s", name);  //CLI退出时,会把处理该CLI的node放入这个复用链表池,以待继续使用  if (vec_len (cm->unused_cli_process_node_indices) > 0)    {      uword l = vec_len (cm->unused_cli_process_node_indices);      /* Find node and give it new name. */      n = vlib_get_node (vm, cm->unused_cli_process_node_indices[l - 1]);      vec_free (n->name);      n->name = (u8 *) name;      vlib_node_set_state (vm, n->index, VLIB_NODE_STATE_POLLING);      _vec_len (cm->unused_cli_process_node_indices) = l - 1;    }  else    {    //池子里没有,就新生成一个      static vlib_node_registration_t r = {    .function = unix_cli_process,    .type = VLIB_NODE_TYPE_PROCESS,    .process_log2_n_stack_bytes = 16,      };      r.name = name;      vlib_register_node (vm, &r);      vec_free (name);      n = vlib_get_node (vm, r.index);    }  pool_get (cm->cli_file_pool, cf);  memset (cf, 0, sizeof (*cf));  template.read_function = unix_cli_read_ready;  template.write_function = unix_cli_write_ready;  template.file_descriptor = fd;  template.private_data = cf - cm->cli_file_pool;  cf->process_node_index = n->index;  //该文件fd加入epoll  cf->unix_file_index = unix_file_add (um, &template);  cf->output_vector = 0;  cf->input_vector = 0;  //启动该VLIB_NODE_TYPE_PROCESS类型node,它会进入“睡眠”状态,等待外部命令行输入来激活node  vlib_start_process (vm, n->runtime_index);  vlib_process_t *p = vlib_get_process_from_node (vm, n);  p->output_function = unix_vlib_cli_output;  p->output_function_arg = cf - cm->cli_file_pool;  return cf - cm->cli_file_pool;}

/*当VLIB_NODE_TYPE_PROCESS类型node被CLI输入激活时,会从vlib_process_wait_for_event (vm);
下一行开始执行,理解这里需要VLIB_NODE_TYPE_PROCESS类型node调度的前置知识*/

/** Handle system events. */static uwordunix_cli_process (vlib_main_t * vm,          vlib_node_runtime_t * rt, vlib_frame_t * f){  unix_cli_main_t *cm = &unix_cli_main;  uword i, *data = 0;  while (1)    {      unix_cli_process_event_type_t event_type;      vlib_process_wait_for_event (vm);      //激活该node的事件类型      event_type = vlib_process_get_events (vm, &data);      switch (event_type)    {    //有CLI 命令来了,开始解析    case UNIX_CLI_PROCESS_EVENT_READ_READY:      for (i = 0; i < vec_len (data); i++)        //解析命令行        unix_cli_process_input (cm, data[i]);      break;    //CLI要退出了    case UNIX_CLI_PROCESS_EVENT_QUIT:      /* Kill this process. */      for (i = 0; i < vec_len (data); i++)        //如果是标准输入上来的退出命令,那就是退出进程,否则只需要退出该CLI session        unix_cli_kill (cm, data[i]);      goto done;    }      if (data)    _vec_len (data) = 0;    }done:  vec_free (data);  vlib_node_set_state (vm, rt->node_index, VLIB_NODE_STATE_DISABLED);  /* Add node index so we can re-use this process later. */  //该node完成使命,不需要在主循环上“睡眠”了,丢给复用池  vec_add1 (cm->unused_cli_process_node_indices, rt->node_index);  return 0;}
//该函数用在epoll监听到有数据到来时,调用它接受数据。逻辑不难。/** Called when a CLI session file descriptor has data to be read. */static clib_error_t *unix_cli_read_ready (unix_file_t * uf){  unix_main_t *um = &unix_main;  unix_cli_main_t *cm = &unix_cli_main;  unix_cli_file_t *cf;  uword l;  int n, n_read, n_try;  cf = pool_elt_at_index (cm->cli_file_pool, uf->private_data);  n = n_try = 4096;  while (n == n_try)    {      l = vec_len (cf->input_vector);      vec_resize (cf->input_vector, l + n_try);      n = read (uf->file_descriptor, cf->input_vector + l, n_try);      /* Error? */      if (n < 0 && errno != EAGAIN)    return clib_error_return_unix (0, "read");      n_read = n < 0 ? 0 : n;      _vec_len (cf->input_vector) = l + n_read;    }  if (!(n < 0))    vlib_process_signal_event (um->vlib_main,                   cf->process_node_index,                   (n_read == 0                ? UNIX_CLI_PROCESS_EVENT_QUIT                : UNIX_CLI_PROCESS_EVENT_READ_READY),                   /* event data */ uf->private_data);  return /* no error */ 0;}//epoll把需要回馈给用户的CLI输出,写入。/** Called when a CLI session file descriptor can be written to without * blocking. */static clib_error_t *unix_cli_write_ready (unix_file_t * uf){  unix_cli_main_t *cm = &unix_cli_main;  unix_cli_file_t *cf;  int n;  cf = pool_elt_at_index (cm->cli_file_pool, uf->private_data);  /* Flush output vector. */  n = write (uf->file_descriptor,         cf->output_vector, vec_len (cf->output_vector));  if (n < 0 && errno != EAGAIN)    return clib_error_return_unix (0, "write");  else if (n > 0)    unix_cli_del_pending_output (uf, cf, n);  return /* no error */ 0;}
0 0
原创粉丝点击