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