Linux內(nèi)核網(wǎng)絡(luò)協(xié)議棧之套接字緩沖區(qū)(純干貨~)
Linux網(wǎng)絡(luò)協(xié)議棧是內(nèi)核中最大的組件之一,由于網(wǎng)絡(luò)部分應(yīng)用的范圍很廣,也相對(duì)較熱,該部分現(xiàn)有的資料很多,學(xué)起來(lái)也比較容易。首先,我們看看貫穿網(wǎng)絡(luò)協(xié)議棧各層的一個(gè)最關(guān)鍵數(shù)據(jù)結(jié)構(gòu)——套接字緩沖區(qū)(sk_buff結(jié)構(gòu))。
一個(gè)封包就存儲(chǔ)在這個(gè)數(shù)據(jù)結(jié)構(gòu)中。所有網(wǎng)絡(luò)分層都會(huì)使用這個(gè)結(jié)構(gòu)來(lái)存儲(chǔ)其報(bào)頭、有關(guān)數(shù)據(jù)的信息,以及用來(lái)協(xié)調(diào)工作的其他內(nèi)部信息。在內(nèi)核的進(jìn)化歷程中,這個(gè)結(jié)構(gòu)經(jīng)歷多次變動(dòng),本文及后面的文章都是基于2.6.20版本,在2.6.32中該結(jié)構(gòu)又變化了很多。該結(jié)構(gòu)字段可粗略劃分為集中類型:布局、通用、專用、可選(可用宏開(kāi)關(guān))。
SKB在不同網(wǎng)絡(luò)層之間傳遞,可用于不同的網(wǎng)絡(luò)協(xié)議。協(xié)議棧中的每一層往下一層傳遞SKB之前,首先就是調(diào)用skb_reserve函數(shù)在數(shù)據(jù)緩存區(qū)頭部預(yù)留出的一定空間以保證每一層都能把本層的協(xié)議首部添加到數(shù)據(jù)緩沖區(qū)中。如果是向上層協(xié)議傳遞skb,則下層協(xié)議層的首部信息就沒(méi)有用了,內(nèi)核實(shí)現(xiàn)上用指針改變指向來(lái)實(shí)現(xiàn)。
下面看看該結(jié)構(gòu)體中的字段,大部分都給了注釋,后面的方法與實(shí)現(xiàn)中我們將看到他的各個(gè)字段的應(yīng)用與實(shí)際意義。
【文章福利】小編推薦自己的Linux內(nèi)核技術(shù)交流群:【891587639】整理了一些個(gè)人覺(jué)得比較好的學(xué)習(xí)書(shū)籍、視頻資料共享在群文件里面,有需要的可以自行添加哦!?。。ê曨l教程、電子書(shū)、實(shí)戰(zhàn)項(xiàng)目及代碼)? ??


可選功能字段
在網(wǎng)絡(luò)模塊中同時(shí)也提供了很多有用的功能,雖然這些功能都不是必須的,但對(duì)現(xiàn)在的應(yīng)用來(lái)講是不可缺少的一部分,例如,防火墻、組播等。為了支持這些功能,一般都需要在內(nèi)核數(shù)據(jù)結(jié)構(gòu)sk_buff中添加相應(yīng)的成員變量。因此,sk_buff結(jié)構(gòu)中包含很多想#ifdef這樣的預(yù)編譯指令。如下面的兩個(gè)宏定義。
我們打開(kāi)內(nèi)核文件夾net->sched下面的Kconfig文件,發(fā)現(xiàn)有下面文字:
與上面數(shù)據(jù)結(jié)構(gòu)中的宏對(duì)應(yīng)就顯然了,如果需要了解內(nèi)核配置選項(xiàng)與對(duì)應(yīng)的宏,查看對(duì)應(yīng)的Kconfig文件就可以了。需要指出的是,內(nèi)核編譯之后,由某些選項(xiàng)所控制的數(shù)據(jù)結(jié)構(gòu)是固定的而不是動(dòng)態(tài)變化的。一般來(lái)說(shuō),如果某些選項(xiàng)修改了內(nèi)核數(shù)據(jù)結(jié)構(gòu),則包含該選項(xiàng)的組件就不能被編譯成內(nèi)核模塊。
數(shù)據(jù)定位與操作
head,end,data,tail四個(gè)字段用來(lái)指向線性數(shù)據(jù)緩存區(qū)及數(shù)據(jù)部分的邊界。Head和end分別指向緩存區(qū)的頭與尾;而data和tail則分別指向數(shù)據(jù)的頭與尾。在發(fā)送時(shí),每一層協(xié)議會(huì)在head與data之間填充協(xié)議首部,還可能在tail和end之間添加數(shù)據(jù)。

Skb初始化
網(wǎng)絡(luò)模塊中,有兩個(gè)用來(lái)分配SKB描述符的高速緩存,在SKB模塊初始化函數(shù)skb_init中被創(chuàng)建
分配skb
Alloc_skb()用來(lái)分配SKB,數(shù)據(jù)緩存區(qū)描述符是兩個(gè)不同的實(shí)體,這就意味著,在分配一個(gè)SKB時(shí),需要分配兩塊內(nèi)存,一塊是數(shù)據(jù)緩存區(qū),一塊是SKB描述符。
調(diào)用該函數(shù)后生成的圖如下所示:

對(duì)于skb數(shù)據(jù)結(jié)構(gòu)的其他操作主要放在skbuff.h文件中,主要有skb_reserve()、skb_put()、skb_push()、skb_pull()、skb_trim()等等,都是對(duì)skb的head、data、tail、end、len等字段進(jìn)行操作。代碼不難,都能看懂,后面涉及到具體的協(xié)議再來(lái)看這些。
鏈表管理
在對(duì)skb鏈表的操作中,為了防止被其他異步操作打斷,在操作前都必須現(xiàn)獲取SKB頭節(jié)點(diǎn)中(sk_buff_head結(jié)構(gòu))的自旋鎖,然后才能訪問(wèn)隊(duì)列中的元素。該鏈表頭結(jié)構(gòu)如下:

對(duì)鏈表操作也增加了很多函數(shù),包括初始化、入隊(duì)列、出隊(duì)列等等,也在skbuff.h中。
Skb_shared_info結(jié)構(gòu)
在alloc_skb()看到,其中中分配數(shù)據(jù)部分分配了一個(gè)該結(jié)構(gòu),在數(shù)據(jù)緩存區(qū)的末尾,保存了數(shù)據(jù)塊的附加信息。如下:
該結(jié)構(gòu)定義如下:
關(guān)于該結(jié)構(gòu)的操作在后面的協(xié)議分析中碰到后進(jìn)行閱讀。接下來(lái)依著協(xié)議棧的層次進(jìn)行分析和學(xué)習(xí),從驅(qū)動(dòng)一直到傳輸層。只會(huì)涉及最基本的幾個(gè)協(xié)議(TCP、IP、UDP、ICMP以及ARP等)。
