Pt.1 Cisco VPP启动流程
Step.0 自定义上电时配置·文件读取
1 VLIB_CONFIG_FUNCTION // 2 or 3 VLIB_EARLY_CONFIG_FUNCTION //
配置文件:
/etc/vpp/startup.conf
e.g.
VLIB_EARLY_CONFIG_FUNCTION (unix_config, "unix");
从配置文件/etc/vpp/startup.conf 中 添加你的参数列表,格式如下:
unix { arg1 arg2 arg3 … }
Step.1 初始化
1 VLIB_INIT_FUNCTION //用来定义"插件/节点、API、CLI等,运行环境的初始化函数"
注册函数到vlib_main_t->init_function_registrations,这个链表在main()函数之前创建。
vlib_main()->vlib_call_all_init_functions()注册的函数在这里被调用初始化,最后执行函数vlib_main_loop()。
像这样由 宏定义和构造函数创建的全局链表 的方式还有如下几个:
1 ·VLIB_API_INIT_FUNCTION 2 3 ·VLIB_CLI_COMMAND //用来初始化“插件CLI” 4 5 ·VLIB_CONFIG_FUNCTION 6 7 ·VLIB_EARLY_CONFIG_FUNCTION 8 9 ·VLIB_MAIN_LOOP_ENTER_FUNCTION 10 11 ·VLIB_MAIN_LOOP_EXIT_FUNCTION 12 13 ·VLIB_REGISTER_NODE 14 15 ·VLIB_PLUGIN_REGISTER //用来初始化"插件描述": 版本与名称
Step.2 vpp/vnet/main.c的main()函数:主main线程
程序的入口,设置vlib_plugin_main.handoff_structure_get_cb函数指针,指向vpp/vnet/main.c中的函数vnet_get_handoff_structure。
vlib/unix/plugin.c中的vnet_get_handoff_structure()函数调用上面的的函数指针handoff_structure_get_cb。
vnet_get_handoff_structure() 定义了一个静态变量,这个静态变量包含了主要数据指针,比如vlib_main,vnet_main和ethernet_main(看代码没有vlib_main)。每个插件注册的时候,都会通过vlib_plugin_register()将以上数据传递给插件。
Step.3 vlib/unix/main.c的vlib_unix_main()函数:插件加载
vlib_plugin_early_init()函数会通过dlopen加载插件目录下的所有插件,这个目录可以通过命令行指定。默认的插件路径是/usr/lib/vpp_plugins。
dlopen每个插件后,VPP会获取函数vlib_plugin_register的符号地址,所以每个插件都要求实现该函数,之前说过这个函数会传递非常重要的数据。
vlib_call_all_config_functions()函数解析所有的命令行选项,并且针对前期需求配置。
为以下线程创建线程栈,主要有三种类型的线程需要实现:
普通线程:比如统计采集。
EAL线程:处理包的工作。
Processes:这些都是定期执行、相互协作的多线程。比如DHCP租期续订的线程等。VPP主线程的超时到期之后会执行这些。
最后,该函数跳转到thread0()函数。
Step.4 vlib/unix/main.c的thread0()函数
调用vlib/main.c的vlib_main()函数
Step.5 vlib/main.c的vlib_main()函数
VLIB_REGISTER_NODE定义图节点,注册到vlib_main_t->node_registrations,vlib_register_all_static_nodes()遍历这个链表,创建图结点(不是连接,是创建)。
VLIB_INIT_FUNCTION声明的函数,由vlib_call_all_init_functions()调用初始化。
如果结点被创建,vlib/node.c的vlib_node_main_init()会对图结点进行初始化。
VLIB_MAIN_LOOP_ENTER_FUNCTION 注册一个链表,vlib_call_all_main_loop_enter_functions()函数遍历该链表。
调用vlib_main_loop()
Step.6 vlib/main.c的vlib_main_loop()函数
创建前面提到的相互协作的多线程,在while(1)循环中处理不同类型的图结点。
·VLIB_NODE_TYPE_PRE_INPUT:类似DBG_CLI的结点
·VLIB_NODE_TYPE_INPUT:这些是主要结点,主要从网卡或者硬件加速器获取数据包
·进程等待信号,这个很重要,因为所有的客户端都要通过共享内存和VPP通信。客户端向共享内存发送一些API消息,并且向VPP发送信号(SIGUSR1)。
输入结点组织数据包,并且将他们发送到合适的中间结点。由dispatch_pending_node()进一步处理这些数据包。
Pt. 2 新建插件plugin
本质是“.so的动态库制品”
参考:https://developer.aliyun.com/article/674304
1 VLIB_INIT_FUNCTION(xxx_init) //插件运行环境,初始化
2 VLIB_PLUGIN_REGISTER //插件描述, 初始化
3 VLIB_CLI_COMMAND //插件CLI命令,初始化
2.1 编译流程
autoreconf -i -f //生成 configure
./configure //生成 makefile
make //
make clean //
Pt. 3 节点NODE
本质是“逻辑功能单元”
3.1 全局结构体
vlib_main_t:每个线程一份,记录着线程使用到的全局数据信息.
比如
|
3.2 Node相关结构体
vlib_node_t: 结点的主结构,包括结点的处理功能函数,名称,结点类型等,主要保存一些相对静态信息,几乎不怎么修改的参数,状态信息保存在这里 vlib_process_t: VLIB_NODE_TYPE_PROCESS类型node专用结构,记录用于模拟task的基础结构:heap上的运行时栈,2种返回时寄存器备份,等
|
3.3 vlib_node_type_t
VLIB_NODE_TYPE_INTERNAL :对数据包真正处理的业务node。 |
VLIB_NODE_TYPE_INTERNAL
内部节点,最典型的节点, 仅在要处理的pending frame时运行;接收缓冲向量,执行操作。
vpp大部分节点是这个角色,主要对数据流做内部处理,比如ip4-input-no-checksum/ip4-icmp-input等内部功能节点
VLIB_NODE_TYPE_INPUT
输入节点,通常是设备输入节点。从零开始创建框架并分派到内部节点(internal), 比如dpdk-input/af-packet-input节点,
input节点收包模式分为轮询和中断两种模式vlib_node_state_t.
VLIB_NODE_TYPE_PRE_INPUT
在其他所有node之前运行。目前只有一个epoll node,对socket相关逻辑提供服务,主要使用在控制业务上。
VLIB_NODE_TYPE_PROCESS
该类型的node可以被挂起也可以被恢复,有独立的分配在heap上的运行栈。类似与在一个线程中实现了多任务的调度机制,主要用来修改vpp node内部参数。
线程节点,和线程一样,可以可以暂停、等待事件、恢复,不同于pthread_thread,他是基于setjump/longjump实现的 “协程”.
等待一个事件:always_inline f64 vlib_process_wait_for_event_or_clock (vlib_main_t * vm, f64 dt)
发送一个事件: always_inline void vlib_process_signal_event (vlib_main_t * vm, uword node_index, uword type_opaque, uword data)
3.4 验证处理结果,决定下一处理流程
vlib_validate_buffer_enqueue_x1 // 决定下一走向。
Pt. 4 feature
/* Hook up input features */
VNET_FEATURE_INIT (ip4_snat_in2out, static) =
{
.arc_name = "ip4-unicast",
.node_name = "nat44-in2out", /* 必须存在nat44-in2out节点 */
.runs_after = VNET_FEATURES ("acl-plugin-in-ip4-fa"),
};
VNET_FEATURE_ARC_INIT (ip4_unicast, static) =
{
.arc_name = "ip4-unicast",
.start_nodes = VNET_FEATURES ("ip4-input", "ip4-input-no-checksum"),/* 起始节点,必须存在node */
.last_in_arc = "ip4-lookup", /* 整个arc的最后一个feature必须是ip4-lookup,否则不能初始化成功 */
.arc_index_ptr = &ip4_main.lookup_main.ucast_feature_arc_index,/* 需要将arc索引写入到ucast_feature_arc_index中,供对应模块使用 */
};
Pt. 4 CLI 命令
ref: https://blog.csdn.net/qq_29044159/article/details/108136177
插件CLI初始化注册:
插件CLI处理过程:
插件CLI