VPP源码分析及流程图
VPP——可编程网络
众多产业正在提供更加软件化的网络服务,在这个环境中提供网络服务是亟待解决的问题。必需但是不足的是需要更多关注非本地的网络控制平面,需要更灵活、高效的数据以及路径交互平面。
VPP所具有的重要特点:灵活性,可维护性和高度的调试性,可提供高性能扩展性和高效IO
- vpp节点调度
vpp的函数调用更像是一种各个节点之间相互连接,通过决定下一跳节点的路径在确定整个代码的执行路径。同样这种方式的函数调用提供了很低的耦合性,所以基于这种方式的二次开发不用太多考虑各个模块之间的相互影响,甚至可以完全不用考虑,自己定义的节点根据格式给出相应的回调函数来插入自定义的功能,下面是一个简单转发的node调用图。
可以看到,在转发的过程中如果要加入自己的上层业务只需要定义新的节点,将其连接到对应位置,并不会影响到其他部分的封装。
- VPP NODE类型
VLIB_NODE_TYPE_PRE_INPUT:目前只有一个epoll node,对socket相关逻辑提供服务,主要使用在控制业务上。
VLIB_NODE_TYPE_INTERNAL:对数据包真正处理业务的node。
VLIB_NODE_TYPE_INPUT:收包逻辑node,比如:dpdk,pcap等
VLIB_NODE_TYPE_PROCESS:该类型的node可以被挂起也可以被恢复,有独立的分配在heap上的运行栈。类似与在一个线程中实现了多任务的调度机制,主要用来修改vpp node内部参数。
- VPP多线程支持
1.单线程
所有的控制和矢量数据包的运行都在单线程中完成。
2.只有worker的多线程(使用RSS更方便)
main线程负责控制信息(API,CLI)。
矢量数据包运行在一个或者多个线程上。
3.有io和worker的多线程
main线程负责控制信息(API,CLI)。
IO线程处理接口输入并调度数据包到worker线程。
worker线程做包括TX接口在内的真正的业务。
4.有worker的多线程并且main线程做io
main线程同时做了IO线程所做的事(管理调度worker线程)。
对于线程来说初始化时按照配置文件循环绑定到指定的核上并且分配其rx和tx队列vnet_hw_interface_assign_rx_thread,可以通过show threads查看状态,show dpdk interface placement查看线程绑定网卡的队列,同样也可以使用set dpdk interface placement 网卡 队列 线程,来改变其默认的绑定状态。
关于多线程的代码主要分布在两个部分:
1.在vlib/threads.c中,这部分主要是线程创建,复制一些结构体并且初始化。
2.在vnet/device/dpdk/thread.c中,定义线程名字、功能。
所有的线程都是调用的同一个函数,只需要改变它的结构体中的内容就可以区分,所以基于vpp的开发也不需要考虑线程关系问题。
- VPP代码主体流程分析
vpp/vnet main.c
1.main 首先解析参数,再需要初始化堆,插件的初始化将由他提供。
2.vpe_main_init 初始化各种插件,通过宏函数VLIB_INIT_FUNCTION(X),可以通过遍历单链表、动态链接的方式指定不同的初始化类型如早期的配置、功能等
3.调用 vilb_unix_main
vilb_unix_mian
- vlib_plugin_early_init (vlib_load_one_plugins) 从配置文件中读取插件的路径而不用重新编译.
- vlib_thread_stack_init 创建主线程的线程栈,对于线程的管理,通过了数组的形式,所以每次使用地址可以直接通过偏移量来找到他
- clib_call_jmp 这里执行了main线程(thread0)的回调函数。
thread0->vlib_main
- cli_time_init 用于多线程时间轮调度
- vlib_register_all_static nodes 同样也是通过遍历单链表的方式对所有的节点分配内存、初始化等等。
- vlib_call_all_init_fountions 这里的初始化不同于最开始时的初始化,这里是建立节点图,通过函数指针计算矢量,也就是在节点图的下一跳(对于不同类型的数据包所形成的路径也是不同的)。
- vlib_buffer_get_or_create_free_list 创建默认的缓冲区,dpdk使用了特定的缓冲区的格式,vpp在其头部添加信息使两者相对隔离,给网络栈和空间存储提供了便利。
- vlib_call_all_config_functions 进入主循环前最后一次进行配置
- 进入 vlib_main_loop
vlib_main_loop
- dispatch_process
- dispatch_node(PRE_INPUT) 目前只有一个epoll node,对socket相关逻辑提供服务,主要使用在控制业务上。可以处理CLI命令以及可以在中断模式和轮询模式中切换。
- dispatch_node(INPUT) 需要从其他容器中获得input方法(dpdk_input),由之前构建好的节点图进行矢量跳转,
- queue_signal_pending 用户可以自行定义信号后会调用回调函数
- 中断模式和时间轮计算
- dispatch_pending_node 由于我们之前已经定义好了数据包的矢量,现在要做的就是跳转到我收到包后现在要做的事情。(p.s.有对于trace版本的优化以及debug版本显示更多信息,同时也可以在gdb中看出数据包的流
- dispatch_node(INTERNAL)
- node->function 调用节点指定的动作(对于dpdk来说这里就是发包)
- VPP代码流程图