簡單講解sk_buff結(jié)構(gòu)分析
前言:
以下是根據(jù)《深入理解Linux網(wǎng)絡(luò)技術(shù)內(nèi)幕》對sk_buff的相關(guān)總結(jié),由于是剛剛看這本書(太厚了),不免在前期出現(xiàn)錯誤,隨著對此書的深入我會在修改前面的錯誤,也希望各位牛人給予指點(diǎn)。幫助我成長。
sk_buff分析:
sk_buff是Linux網(wǎng)絡(luò)代碼中最重要的結(jié)構(gòu)體之一。它是Linux在其協(xié)議棧里傳送的結(jié)構(gòu)體,也就是所謂的“包”,在他里面包含了各層協(xié)議的頭部,比如ethernet, ip ,tcp ,udp等等。也有相關(guān)的操作等。熟悉他是進(jìn)一步了解Linux網(wǎng)絡(luò)協(xié)議棧的基礎(chǔ)。
此結(jié)構(gòu)定義在<include/linux/skbuff.h>頭文件中,結(jié)構(gòu)體布局大致可分為以下四部分: l 布局(layout)
l 通用(general)
l 功能專用(feature-specific)
l 管理函數(shù)(management functions)
網(wǎng)絡(luò)選項(xiàng)以及內(nèi)核結(jié)構(gòu)
我們可以看到在此結(jié)構(gòu)體里有很多預(yù)處理,他是在需要指定相應(yīng)功能時(shí)才起作用,我們在這里先對通用的作出分析。 布局字段:
sk_buff是一個(gè)復(fù)雜的雙向鏈表,在他結(jié)構(gòu)中有next和prev指針,分別指向鏈表的下一個(gè)節(jié)點(diǎn)和前一個(gè)節(jié)點(diǎn)。并且為了某些需求(不知道是哪些目前)需要很快定位到鏈表頭部,所以還有一個(gè)指向鏈表頭部的指針list(我在2.6.25內(nèi)核沒有發(fā)現(xiàn)這個(gè)指針)。 sk_buff_head結(jié)構(gòu)是:

【文章福利】小編推薦自己的Linux內(nèi)核技術(shù)交流群:【891587639】整理了一些個(gè)人覺得比較好的學(xué)習(xí)書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦!?。。ê曨l教程、電子書、實(shí)戰(zhàn)項(xiàng)目及代碼)? ?


struct sock *sk
這個(gè)指針指向一個(gè)套接字sock數(shù)據(jù)結(jié)構(gòu)。當(dāng)數(shù)據(jù)在本地產(chǎn)生或者本地進(jìn)程接受時(shí),需要這個(gè)指針;里面的數(shù)據(jù)會有tcp/udp和用戶態(tài)程序使用。如果是轉(zhuǎn)發(fā)此指針為NULL
unsigned int len
緩沖區(qū)中數(shù)據(jù)塊大小。長度包括:主要緩沖區(qū)(head所指)的數(shù)據(jù)以及一些片斷(fragment)的數(shù)據(jù)。當(dāng)包在協(xié)議棧向上或向下走時(shí),其大小會變,因?yàn)橛蓄^部的丟棄和添加。
unsigned int data_len
片段中數(shù)據(jù)大小
unsigned int mac_len
mac包頭大小
atomic_t users
引用計(jì)數(shù),使用這個(gè)sk_buff的使用者的數(shù)目,可能有多個(gè)函數(shù)要使用同一個(gè)sk_buff所以防止提前釋放掉,設(shè)置此計(jì)數(shù) unsigned int truesize
此緩沖區(qū)總大小,包括sk_buff。sk_buff只不過是個(gè)指針的集合,他所指的才是真正的數(shù)據(jù)區(qū),所以是兩部分。(見下圖) sk_buff_data_t tail;
sk_buff_data_t end;
unsigned char *head, *data;
這些指針很重要,他們指向的是真正的數(shù)據(jù)區(qū),他們的邊界。head和end指向的是數(shù)據(jù)區(qū)的開端和尾端(注意和data,tail區(qū)別)如下圖,data和tail指向的是實(shí)際數(shù)據(jù)的開頭和結(jié)尾。
因?yàn)閿?shù)據(jù)區(qū)在協(xié)議棧走的時(shí)候要一層層添加或去掉一些數(shù)據(jù)(比如報(bào)頭)所以申請一塊大的足夠的內(nèi)存,然后在往里放東西。真實(shí)的實(shí)際數(shù)據(jù)可能用不了這么多,所以用data,tail指向真實(shí)的,head,tail指向邊界。剛開始沒填充數(shù)據(jù)時(shí)前三個(gè)指針指向的是一個(gè)地方。

void (*destructor) (…….)
此函數(shù)指針被初始化一個(gè)函數(shù),當(dāng)此緩沖區(qū)刪除時(shí),完成某些工作。 通用字段
struct timeval stamp(2.6.25沒有,估計(jì)是ktime_t tstamp)
時(shí)間戳,表示何時(shí)被接受或有時(shí)表示包預(yù)定的傳輸時(shí)間 struct net_device *dev
描述一個(gè)網(wǎng)絡(luò)設(shè)備,我會以后分析他。 sk_buff_data_t transport_header; //L4
sk_buff_data_t network_header; //L3
sk_buff_data_t mac_header; //L2
這些指針分別指向報(bào)文頭部,和2.4版本比較有了變化,不再是聯(lián)合體,使用更加方便了,Linux給出了很方便的函數(shù)直接定位到各層的頭部。下圖是2.4版本的,只是說明一下。

struct dst_entry dst
路由子系統(tǒng)使用。目前不知道怎么回事呢。據(jù)說比較復(fù)雜。 char cb[40]
緩沖控制區(qū),用來存儲私有信息的空間。比如tcp用這個(gè)空間存儲一個(gè)結(jié)構(gòu)體tcp_skb_cb ,可以用宏TCP_SKB_CB(__skb)定位到他,然后使用里面的變量。 ip_summed:2
__wsum csum;
校驗(yàn)和 unsigned char pkt_type
根據(jù)L2層幀的目的地址進(jìn)行類型劃分。 unsigned char cloned
表示該結(jié)構(gòu)是另一個(gè)sk_buff克隆的。 __u32 priority;
QoS等級 __be16 protocol;
從L2層設(shè)備驅(qū)動看使用在下一個(gè)較高層的協(xié)議。 功能專用字段
Linux是模塊化的,你編譯時(shí)可以帶上特定功能,比如netfilter等,相應(yīng)的字段才會生效。應(yīng)該是那些預(yù)定義控制的。
管理函數(shù)
下面這個(gè)圖是:(a*)skb_put; (b*) skb_push; (c*) skb_pull (d*) skb_reserve的使用,主要是對skb_buf所指向的數(shù)據(jù)區(qū)的指針移動。(數(shù)據(jù)預(yù)留以及對齊)

下圖是用skb_reserve函數(shù),把一個(gè)14字節(jié)的ethernet幀拷貝到緩沖區(qū)。skb_reserve(skb, 2), 2表示16字節(jié)對齊。14+2=16

分配內(nèi)存:
alloc_skb 分配緩沖區(qū)和一個(gè)sk_buff結(jié)構(gòu)
dev_alloc_skb 設(shè)備驅(qū)動程序使用的緩沖區(qū)分配函數(shù)
釋放內(nèi)存:
kfree_skb 只有skb->users計(jì)數(shù)器為1時(shí)才釋放
dev_kfree_skb
緩沖區(qū)克隆函數(shù) skb_clone
列表管理函數(shù):
skb_queue_head_init
隊(duì)列初始化 skb_queue_head , skb_queue_tail
把一個(gè)緩沖區(qū)添加到隊(duì)列頭或尾 skb_dequeue, skb_dequeue_tail
從頭或尾去掉 skb_queue_purge
把隊(duì)列變空 skb_queue_walk
循環(huán)隊(duì)列每個(gè)元素
