注:本文是转载,但不是100%的转载,可能稍微有些出入,原文地址点击这里
基本概念
这个只是辅助部分,大多数人不关心,我就随便写写。
核心函数
//主线程会调用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_config执行且只执行了一次
if ((error = vlib_call_config_function (vm, unix_config)))
return error;
if (um->flags & UNIX_FLAG_INTERACTIVE)
{
//支持交互模式
//非阻塞模式,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 (isatty (UNIX_CLI_STDIN_FD) && um->cli_line_mode == 0)
{
...
}
//一些欢迎信息拷贝到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 u32 unix_cli_file_add (unix_cli_main_t * cm, char *name, int fd)
{
...
//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 uword unix_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;
}