vpp编程
vpp編程與部分編譯問題
- VPP Infra
- 1.1 基本數(shù)據(jù)類型(types.h)
- 1.2 vec.h
- 1.3 bitmap.h
- 1.4 pool.h
- 1.5 heap.h
- 1.6 bihash.h
- 1.7 format.h(格式轉(zhuǎn)換)
- 1.8 elog.h(事件記錄)
- 1.9 time.h
- 時間輪
- 初始化一個雙定時器、單個 2048 槽輪,具有 1 秒定時器粒度[]
- 啟動計時器[]
- 停止計時器[]
- vlib
- 2.1 vlib node
- 2.2 plugins
- 2.3 vlib buffers
- vpp-pc編譯問題
VPP 的整體框架如下圖所示:
由上圖分析,VPP源碼模塊主要包括以下幾個層次,且關(guān)系如下:
| VPP Infra | 提供一些基本的通用的功能函數(shù)庫:包括內(nèi)存管理,向量操作,hash, timer等 |
| VLIB | 主要提供基本的應(yīng)用管理庫:buffer管理,graph node管理,線程,CLI,trace等 |
| Vnet | 提供網(wǎng)絡(luò)資源能力:比如設(shè)備,L2,L3,L4功能,session管理,控制管理,流量管理等 |
| Plugins | 主要為實(shí)現(xiàn)一些功能,在程序啟動的時候加載,一般情況下會在插件中加入一些node節(jié)點(diǎn)去實(shí)現(xiàn)相關(guān)功能 |
VPP Infra
VPPinfra 是基本 c 庫服務(wù)的集合,足以構(gòu)建獨(dú)立程序以直接在裸機(jī)上運(yùn)行。它還提供高性能動態(tài)數(shù)組、散列、位圖、高精度實(shí)時時鐘支持、細(xì)粒度事件記錄和數(shù)據(jù)結(jié)構(gòu)序列化(指的是數(shù)據(jù)存儲類似數(shù)組的形式,而不是通過指針尋找next)。
我們先來看看vppinfra 給我們提供的structures:
1.1 基本數(shù)據(jù)類型(types.h)
u8, u16, u32, u64, u32x4 (intrinsics)
i8, …
f32, f64
uword
這個uword類型如下:
1.2 vec.h
Vppinfra 向量是無處不在的動態(tài)調(diào)整大小的數(shù)組,由用戶定義“header”。許多 vpppinfra 數(shù)據(jù)結(jié)構(gòu)(例如hash、heap、pool)是具有各種不同header的向量。
內(nèi)存布局如下所示:
User header (optional, uword aligned)Alignment padding (if needed)Vector length in elementsUser's pointer -> Vector element 0Vector element 1...Vector element N-1vector 這個概念很重要,vpp基本就是圍繞vector和node兩大概念展開的。下面這篇的作者寫的很好,原創(chuàng)連接:
vpp之vec學(xué)習(xí)
向量是一個帶有一些原數(shù)據(jù)自動調(diào)整大小的C數(shù)組:參數(shù):類型T,頭部大小,元素對齊。
內(nèi)存分配只會增加。
向量原點(diǎn)指針可能改變!
存儲索引(不是指針)!
為了避免內(nèi)存分配器的混亂,人們經(jīng)常將向量的長度重置為零,同時保留內(nèi)存分配。通過 vec_reset_length(v) 宏將向量長度字段設(shè)置為零。向量元素可以是任何 C 類型,例如(int、double、struct bar)。對于建立在向量之上的數(shù)據(jù)類型(例如堆、池等)也是如此。
1.3 bitmap.h
操作和獲取位圖信息,可以根據(jù)需要分配任意bit的位圖。
從上面可以知道,申請的bitmap其實(shí)就是一個vector類型,這個vector的成員為uword類型,如果設(shè)備CPU是64位處理器,那么一個uword類型就是一個64bit的整數(shù)類型。
1.4 pool.h
Vppinfra pool結(jié)合了向量和位圖,以快速分配和釋放具有獨(dú)立生命周期的固定大小的數(shù)據(jù)結(jié)構(gòu)。pool非常適合分配每個會話的結(jié)構(gòu)。
池基于動態(tài)數(shù)組和位圖構(gòu)建。主要用于需要頻繁申請和釋放固定大小內(nèi)存的場合。先釋放到池里面,再次申請的時候,從池里面獲取,提升效率。這樣做,也能有效的避免內(nèi)存碎片的產(chǎn)生。
| free_bitmap | 當(dāng)前池里面,哪些內(nèi)存塊是空閑的(未被使用的,置1),哪些是正在被使用的(置0)。 |
| free_indices | 一個動態(tài)數(shù)組,每一個元素是一個空閑塊的編號,數(shù)組長度與上述位圖中置1的bit數(shù)目相同。 |
| max_elts | 當(dāng)前池最多支持的元素的個數(shù) |
| mmap_base | 當(dāng)前池的內(nèi)存首地址 |
| mmap_size | 當(dāng)前池的內(nèi)存大小,字節(jié)數(shù)目。 |
池可以使用預(yù)分配的內(nèi)存構(gòu)建,也可以使用動態(tài)數(shù)組構(gòu)建。max_elts 、mmap_base 、mmap_size 這3個元素僅用于預(yù)分配內(nèi)存構(gòu)建的池。
詳細(xì)介紹可以查看這篇文章:VPP代碼閱讀中文注解–pool.h
代碼中常見的幾個pool api記錄一下
| pool_alloc | 內(nèi)存池擴(kuò)容N個內(nèi)存塊。這個時候,空閑內(nèi)存塊向量也一并擴(kuò)容,但len不變,只是把內(nèi)存空間先申請到,免得后面申請不到空間。其實(shí)位圖內(nèi)存塊也可以先把內(nèi)存占到,但是這里并沒有這樣做,畢竟位圖占用的內(nèi)存還是比較少,后面使用到時再申請。 |
| pool_elt_at_index | 返回第 i 號內(nèi)存塊的地址。 |
| pool_elts | B3 |
| pool_foreach | 遍歷內(nèi)存池中內(nèi)存塊,執(zhí)行用戶指定的操作BODY。主要利用了位圖的特性,一個uword為0,則uword內(nèi)所有bit都為0。找出連續(xù)的0 bit, 分別對他們進(jìn)行遍歷。 |
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-Sawo4VtQ-1624928025083)($res/Snipaste_2021-06-02_15-04-49.png)]
分析一下上面的代碼:先調(diào)用pool_alloc保證向量有足夠的內(nèi)存塊,pool_get 從向量表lru_pool申請一塊內(nèi)存塊,返回值head指向這個內(nèi)存塊,head - tsm->lru_pool;獲取這個內(nèi)存塊在向量表lru_pool的位置信息index,類似于數(shù)組的下標(biāo),這個index用于記錄申請的內(nèi)存塊,從上面的信息可以知道index是不變的,head每次都是變化的,head指向當(dāng)前申請的內(nèi)存塊。
1.5 heap.h
很少使用,這個可以跳過
1.6 bihash.h
Bihashes 是線程安全的。不需要讀鎖定。一個簡單的自旋鎖確保一次只有一個線程寫入一個條目。
VPP里的Bihash全名為Bounded-index extensible hash。它的最大特點(diǎn)是,在查找時是無鎖并且線程安全的。修改操作之間會有互斥,但是修改操作時仍然可以進(jìn)行查找操作。
vpp里的Bihash優(yōu)化成了兩種,bihash_kv_8_8和bihash_kv_24_8,區(qū)別在于hash key是8字節(jié)還是24字 節(jié)。最大限度的利用SSE4.2指令集中的_mm_crc32_u64來進(jìn)行hash計算。如果CPU不支持英特爾SSE4指令集,那么就利用軟件或者編譯器計算。
核心函數(shù)在bihash_template.c中。根據(jù)包含的頭文件是bihash_8_8.h還是bihash_24_8.h,BV宏和BTV 宏將把名字做出對應(yīng)擴(kuò)展。例如:BV (clib_bihash_init)擴(kuò)展為clib_bihash_init_8_8()或者 clib_bihash_init_24_8()。BVT (clib_bihash)擴(kuò)展為clib_bihash_8_8_t或者為 clib_bihash_24_8_t。
這個vpp的bihash可以參照這篇文章:VPP-BIHASH實(shí)現(xiàn)分析
文章中指出BVT (clib_bihash_shared_header)這個結(jié)構(gòu)定義的變量在多個CPU是共享的,我的理解是在這個數(shù)據(jù)在CPU多核之間是共享的,查看源代碼發(fā)現(xiàn),這個數(shù)據(jù)BVT (clib_bihash_shared_header) * sh; 所指向的內(nèi)存地址是通過linux mmap內(nèi)存映射的方式實(shí)現(xiàn)的,進(jìn)程1和進(jìn)程2通過mmap的方式把內(nèi)存的一塊物理地址(文件系統(tǒng)從磁盤讀取文件,把該文件放置在這個物理內(nèi)存塊中)映射到各自的進(jìn)程虛擬地址表中,從而實(shí)現(xiàn)共享文件的機(jī)制。具體描述可以參照下面這篇文章:mmap實(shí)現(xiàn)原理淺析
在任何一種情況下,人們幾乎總是在哈希表中查找鍵以獲得相關(guān)向量或池中的索引。API 非常簡單,但在使用非托管的任意大小的密鑰結(jié)構(gòu)體時必須小心。Hash_set_mem(hash_table, key_pointer, value) 記憶key_pointer。將向量元素的地址作為第二個參數(shù)傳遞給 hash_set_mem 通常是一個嚴(yán)重的錯誤。記住文本段中的常量字符串地址是完全沒問題的之前做cfm和tpoam時,匹配不同的tpoam報文與cfm報文字段生成兩個key,這兩個key指向同一個session的index這個時候需要注意,key與value是一一對應(yīng)的,后面的key會覆蓋之前的,遇到這種情況,key必須自己申請內(nèi)存,自己管理,當(dāng)然也可以修改value + 一個偏移量來存放新的key與value。存儲結(jié)構(gòu)體的key需要特別小心,因?yàn)閗ey是指針,這個密鑰結(jié)構(gòu)體必須申請內(nèi)存。
1.7 format.h(格式轉(zhuǎn)換)
這個是把你想格式的任何數(shù)據(jù)類型都轉(zhuǎn)為字符串的格式,便于查看打印信息。
大部分的數(shù)據(jù)轉(zhuǎn)化格式都有,如下所示:
unformat 相關(guān)的函數(shù)正好相反,用來把字符串轉(zhuǎn)化為數(shù)據(jù)。
下面是一個解析l2tp命令行的例子:
1.8 elog.h(事件記錄)
vppinfra 事件記錄器提供非常輕量級(低于 100 納秒)、精確時間戳的事件記錄服務(wù)。網(wǎng)址介紹:https://wiki.fd.io/view/VPP/elog
- Event-logging enabled in …/vlib/vlib/main.c:vlib_main(…)
- Use the elog_main_t in vlib_global_main, aka &vlib_global_main.elog_main
-
Default ring size 128K events
+Thread safe—lock-free atomic increment to dole out event slots -
Each event-slot is 32 bytes: u64 time-in-cpu-clocks, u16 event-id, u16 track, 20 bytes of data
每個記錄的事件槽32bytes,其中8個字節(jié)的cpu clocks,2字節(jié)的event-id和2字節(jié)的track,20個字節(jié)數(shù)據(jù) -
Logging an event costs less than 100ns
-
每個節(jié)點(diǎn)、每個幀最多有幾個事件(這是說不讓隨便用這個event log嗎?)
從官方的資料知道,這個事件記錄器應(yīng)該主要用于調(diào)試一些速率比較快的處理流程時,用來查看相關(guān)信息。命令行如下:
vpp# event-logger clear vpp# event-logger save <filename> # 為了安全,寫入/tmp/<filename>。# <文件名> 不能包含“.” 或 '/' 字符 vpp# show event-logger [all] [<nnn>] # 顯示事件日志# 默認(rèn)情況下,最后 250 個條目此外,VPP 不光有event-logger,還有syslog、logging等調(diào)試信息,后續(xù)再研究他們用法的區(qū)別。
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-Dp5RFecL-1624928025098)($res/Snipaste_2021-06-03_11-05-58.png)]
后面的vppinfra官方居然不補(bǔ)充了,emm。。。,最重要的內(nèi)存管理、線程調(diào)度、時間輪居然不講了,哎后面自己慢慢看吧
先記錄一下。
總結(jié):
1.9 time.h
vlib/clib_time_now(…) 返回一個 64 位浮點(diǎn)值;自 vpp 啟動以來的秒數(shù)。vlib/clib_time_now_不_提供超過 1e-6 秒的精度。也就是說定時器最多精確到us級。
clib_cpu_time_now() 用ARM匯編指令獲取當(dāng)前CPU clocks。
時間輪
Vppinfra 包括可配置的時間輪支持。參見…/src/vppinfra/tw_timer_template.[ch]中的源代碼,以及…/src/vppinfra/tw_timer_中定義的大量模板實(shí)例.[ch]。
tw_timer_template.h 的實(shí)例化生成命名結(jié)構(gòu)以實(shí)現(xiàn)特定的時間輪幾何形狀。選項包括:時間輪的數(shù)量(當(dāng)前為 1 或 2)、每個環(huán)的插槽數(shù)量(2 的冪)以及每個“對象句柄”的計時器數(shù)量。
在內(nèi)部,用戶對象/定時器句柄是 32 位整數(shù),因此如果選擇 16 個定時器/對象(4 位),則生成的定時器輪句柄限制為 2**28 個對象。
以下是生成單個 2048 插槽輪所需的特定設(shè)置,該輪支持每個對象 2 個計時器:
tw_timer_template.h 不打算直接#included??蛻舳舜a可以包含多個計時器幾何頭文件,盡管在這種情況下使用 TW 和 TWT 宏需要格外小心。比如bfd模塊用的時間輪如下:
…/src/vppinfra/test_tw_timer.c 中的單元測試代碼提供了一個具體的 API 使用示例。它使用合成時鐘快速運(yùn)行底層 tw_timer_expire_timers(…) 模板。
要調(diào)用的 API 例程并不多。
初始化一個雙定時器、單個 2048 槽輪,具有 1 秒定時器粒度[]
tw_timer_wheel_init_2t_1w_2048sl (&tm->single_wheel,expired_timer_single_callback,1.0 / * timer interval * / );啟動計時器[]
handle = tw_timer_start_2t_1w_2048sl (&tm->single_wheel, elt_index,[0 | 1] / * timer id * / ,expiration_time_in_u32_ticks);停止計時器[]
tw_timer_stop_2t_1w_2048sl (&tm->single_wheel, handle);vppinfra 總結(jié):
- 存儲索引,而不存儲指針(調(diào)整數(shù)組大小)。
- 宏確實(shí)會更改參數(shù)(不僅是指針)。
- format和unformat功能很有用。
- 對齊對于多線程效率很重要。
vlib
VPP基于2個主要思想:
1.向量(捆綁在一起的0到256個緩沖區(qū))
2.Node執(zhí)行相關(guān)操作并傳遞向量(vectors)的node。
vlib是定義所有這些抽象模型的地方。不考慮任何網(wǎng)絡(luò)因素(無IP,無以太網(wǎng)等)。
它還包括:
- Counters(計數(shù)器)
- CLI(命令行)
- Scheduler(調(diào)度器)(main loop).
- 其他
2.1 vlib node
- VLIB_NODE TYPE_INTERNAL : 最典型的節(jié)點(diǎn)接收緩沖區(qū)向量,執(zhí)行操作,包括TX節(jié)點(diǎn)。
- VLIB_NODE_TYPE_INPUT: 通常是設(shè)備輸入節(jié)點(diǎn),從頭開始創(chuàng)建幀并將其分發(fā)到內(nèi)部節(jié)點(diǎn)
- VLIB_ NODE_ TYPE_PRE_INPUT: input node之前被調(diào)用,幾乎不怎么用
- VLIB_NODE_ TYPE_ PROCESS:
類似線程一樣。
可以掛起,等待事件,恢復(fù)…
(基于setjump / longjump)。
2.2 plugins
2.3 vlib buffers
vlib_buffer_t是VPP包的基本數(shù)據(jù)結(jié)構(gòu)(相當(dāng)于DPDK的rte_mbuf)。
當(dāng)DPDK==1時,vlib_buffer_t被封裝在rte_mbuf中。
#define vlib_buffer_from_rte_mbuf(x)((vlib_buffer_t*)(x+1))
#define rte_mbuf_from_vlib_buffer(x)(((struct rte_mbuf *)x)-1)
vpp-pc編譯問題
1.
sudo apt-get install libssl1.0-dev
總結(jié)
- 上一篇: EasyRecovery14免费激活码序
- 下一篇: EasyRecovery 15win/m