Linux網路子系統中存在一些很重要的資料結構,貫穿整個子系統,主要有以下兩個:
struct sk_buff:資料封包結構。所有的網路分層都會使用這個結構來儲存其前序、有關使用者資料,以及協調其他工作的其他內部資訊。 struct net_device:在Linux核心中,每種網路裝置都用這個資料結構表示,包括軟硬體的配置資訊。
一、通訊端緩衝區:sk_buff結構 這可能是Linux網路代碼中最重要的資料結構,表示資料報文。這個結構定義在<include/linux/skbuff.h>標頭檔中,由巨大的變數堆組成,試圖滿足所有人的所有需求。 這個結構的欄位大致分為以下幾個類型: 網路層次 通用欄位 功能專用 管理函數 在網路系統的不同網路層都會使用這個結構,而當這個結構從一個分層傳到另一個分層時,其不同的欄位會隨之發生改變。如L4層在傳遞給L3之前會附加一個前序,通用L3到L2之前也會加上自己的前序。附加前序比把資料從一個分層拷貝到另一個分層更有效率。 由於要在一個緩衝區開端新增空間(也就是修改指向緩衝區頭部的指標),核心提供了skb_reserve函數來執行這個操作。所以,當緩衝區往下傳遞給每個網路層時,每層的協議首先要做的就是調用skb_reserve函數為該協議的前序預留空間。 而在緩衝區向上傳遞給上層網路時,並沒有本層前序從緩衝區中刪除,二是將直線有效資料的指標向前移到上層的前序位置。 由於網路代碼提供了大量的選項性功能,不一定總是需要,如防火牆、多播、串連跟蹤等,這些功能都會在sk_buff結構豬附加上欄位。因此,sk_buff結構中有許多由C預先處理#ifdef指令附加的欄位。一般而言,任何引起核心資料結構改變的選項,都不適合編譯成一個模組進行動態載入。 sk_buff中的某些欄位是為了組織資料結構本身:
- struct sk_buff {
- /* These two members must be
first. */
- struct sk_buff *next;
- struct sk_buff *prev;
同時為了迅速找到整個表的頭,在表的開端額外增加一個sk_buff_head結構作為一種啞元元素,sk_buff_head結構是:
- struct sk_buff_head {
- /* These two members must be first. */
- struct sk_buff *next;
- struct sk_buff *prev;
- __u32 qlen;
- spinlock_t lock;
- };
qlen是表中元素的數目,lock是用於防止對錶的並發訪問。 sk_buff和sk_buff_head結構的前兩個元素是相同的,所以同樣的函數也可用於操作sk_buff和sk_buff_head二者。 sk_buff結構中的list欄位指向表頭:sk_buff中的其他欄位:
struct sock *sk:指向擁有此緩衝區的通訊端的sock資料結構。當資料在本地產生或者正在由本地進程接收時,就需要這個指標,因為該資料以及通訊端相關的資訊會由L4層(TCP或UDP)以及使用者應用程式使用。當緩衝區只是被轉寄時,該指標就是NULL。
unsigned int len:這是指緩衝區豬資料區塊的大小。這個長度包括主要緩衝區(由head所指)的資料以及一些片段(fragment)的資料。當緩衝區從一個網路層傳遞給下一個網路層時,其值會發生變化。因為在協議棧中往上移動時,前序會被丟棄。但是往下移動時,前序會被添加進來,len會將協議前序長度算在裡面。
unsigned int data_len:與len不同,data_len只計算片段中的資料大小
unsigned int mac_len:MAC前序的大小
atomic_t users:引用計數,或者使用這個sk_buff緩衝區的執行個體的數目。這個參數的的主要用途是避免這個結構仍在使用時,被另一個執行個體釋放掉。users有時直接使用atimic_inc和atomic_dec函數遞增和遞減,但在大多數時候,採用skb_get和kfree_skb進行處理。
unsigned int truesize:表示此緩衝區的總大小,包括sk_buff結構本身。當此緩衝區得到所分配的len個位元組的資料請求空間時,此欄位的初始化由alloc_skb函數設定為len+sizeof(sk_buff)。每當skb->len的值增加時,此欄位就會得到更新。
sk_buff_data_t tailsk_buff_data_t endunsigned char *headunsigned char *data這些欄位代表緩衝區的邊界以及其中的資料。當每一層為其工作而準備緩衝區時,可能會為了一個前序或更多的資料分配更多的空間。head和end指向已指派空間的開端和尾端,而data和tail指向實際資料的開端和尾端。因此,可以再head和data直接填充報文頭,在tail和end之間增加新的資料。其中tail和end根據系統是否使用NET_SKBUFF_DATA_USES_OFFSET來決定使用位移地址還是指標
- #ifdef NET_SKBUFF_DATA_USES_OFFSET
- typedef unsigned int sk_buff_data_t;
- #else
- typedef unsigned char *sk_buff_data_t;
- #endif
void (*destructor)(struct sk_buff *skb):此函數指標所指的函數在緩衝區被刪除時,完成某些工作。當此緩衝區不屬於一個通訊端時,destructor通常不會被初始化。但若屬於一個通訊端時,通常被設定為sock_rfree或sock_wfree。這兩個函數可用於更新通訊端隊列豬所持有的記憶體。