在2.6.24以後這個結構體有了較大的變化,此處先說一說2.6.16版本的sk_buff,以及解釋一些問題。面試
1、數組
先直觀的看一下這個結構體~~~~~~~~~~~~~~~~~~~~~~在下面解釋每一個字段的意義~~~~~~~~~~~安全
> : next和prev,這兩個域是用來鏈接相關的skb的(例如若是有分片,將這些分片鏈接在一塊兒能夠)網絡
> : sk,指向報文所屬的套接字指針tcp
> : tstamp,記錄接收或者傳輸報文的時間戳函數
> : dev和input_dev,記錄接收或者發送的設備atom
>: union u,對於一個層次,例如tcp層,可能有不少不一樣的協議,他們的協議頭不同,那麼這個聯合體就是記錄這些協議頭的。spa
此處u就是表明傳輸層.net
> : union nh,表明網絡層頭指針
> : union mac,表明鏈路層頭
> : dst,指向des_entry結構,記錄了到達目的地的路由信息,以及其餘的一些網絡特徵信息。
> : sp:安全路徑,用於xfrm
> : cb[],保存與協議相關的控制信息,每一個協議可能獨立使用這些信息。
> : 重要的字段 len 和 data_len:
len代: 表整個數據區域的長度!這裏要提早解釋幾個定義,skb的組成是有sk_buff控制 + 線性數據 + 非線性數據
(skb_shared_info) 組成!
後面會具體解釋是什麼意思!在sk_buff這個裏面沒有實際的數據,這裏僅僅是控制信息,數據是經過後面的data指針指向其餘內
存塊的!那個內存塊中是線性數據和
非線性數據!那麼len就是length(線性數據) + length(非線性數據)!!!
data_len: 指的是length(非線性數據)!!!那麼能夠知道:length(線性數據) = skb->len - skb->data_len
> : mac_len,指的是mac頭長度
> : csum,某時刻協議的校驗和
> : priority,報文排隊優先級,取決於ip中的tos域
> : local_df,容許在本地分配
> : cloned,保存當前的skb_buff是克隆的仍是原始數據
> : ip_summed,是否計算ip校驗和
> : nohdr,僅僅引用數據區域
> : pkt_type,報文類型,例如廣播,多播,迴環,本機,傳出...
> : fclone,skb_buff克隆狀態
> : ipvs_property,skb_buff是否屬於ipvs
> : protocal,協議信息
> : nfmark,用於鉤子之間通訊
> : nfct_reasm,netfilter的跟蹤鏈接從新組裝指針
> : nf_bridge,保存橋接信息
> : tc_index: Traffic control index,tc_verd: traffic control verdict
> : truesize,該緩衝區分配的全部總的內存,包括:skb_buff + 全部數據大小
> : users,保存引用skb_buff的數量
> : 重要數據字段:head,data,tail,end!!!
head:指向分配給的線性數據內存首地址( 創建起一個觀念:並非分配這麼多內存,就都能被使用做爲數據存儲,可能沒這麼多
數據也有可能!可是也不要認爲分配這麼多 就足夠了,也不必定(非線性數據就是例子) )
data:指向保存數據內容的首地址!咱們由head能夠知道,head和data不必定就是指在同一個位置!!!
tail:指向數據的結尾!
end:指向分配的內存塊的結尾! ( 由上面咱們知道數據結尾 != 分配的內存塊的結尾 )
下面還會具體分析!!!!!!!!!!!
2、
我以爲須要先了解一些對於一個數據skb到底有什麼,或者說由哪些元素組成!這就須要知道所謂的 「線性數據」 和 「非線性數據」。
基本的組成以下:
> : sk_buff : 這是一個sk_buff的控制結構
> : 線性數據區域
> : 非線性數據區域( 由skb_shared_info結構體管理 )
那麼下面經過一個圖來看看這個skb結構究竟是怎麼樣的!看(圖一)
(圖一)
藉助圖一,咱們先來分析兩個重要字段:len和data_len!
以前說過len表明的是整個數據的長度,data_len表明的是非線性數據長度。咱們由圖一能夠看到線性數據長度爲l1,再看看非線性數據,其實就是看frags[]和frag_list
ok...那麼咱們能夠知道非線性數據長度爲( l2 + ... + ln ) + ( l(n+1) + ... + lm )
即:len = l1 + ( l2 + ... + ln ) + ( l(n+1) + ... + lm )
data_len = ( l2 + ... + ln ) + ( l(n+1) + ... + lm )
ok...
如今從分配內存開始解釋這個圖的由來:
咱們使用skb_alloc給skb分配空間,那麼剛剛分配結束返回時候,是什麼樣的狀況呢?看下圖(圖二):
(圖二)
剛剛開始初始化的時候,預分配一個一塊線性數據區域,這個區域通常放入的是各個協議層次的不一樣的頭,還有一些實際數據,下面的非線性區域是爲了彌補當數據真的不少的時候,做爲數據區域的擴展!關於skb_shared_info具體意思下面會繼續說!注意在初始化的時候,head,data和tail都指向內存的開始位置,head在這個位置始終不變,它表示的是分配的內存的開始位置。end的位置也是不變的,表示的是分配的內存的結束位置。data和tail會隨着數據的加入和減小變化,總之表示的是放入數據的內存區域(由圖一)可知。
如今須要解釋一下skb_shared_info這個結構體,這個結構體真的是很頗有特點!主要是其中的兩個字段frags和frag_list,下面繼續解釋:
關於frags和frag_list沒有必然的聯繫!
> : 對於frags[]通常用在,當數據真的不少,並且在線性數據區域裝不下的時候,須要使用這個,skb_frag_t中是一頁一頁的數據,先看看結構體:
須要注意的是:只有在DMA支持物理分散頁的Scatter/Gather(SG,分散/彙集)操做時候纔可使用frags[]來保存剩下的數據,不然,只能擴展線性數據區域進行保存!!!
這些頁實際上是其實就是虛擬頁映射到物理頁的結構,看下圖(圖三):
(圖三)
> : 對於frag_list來講,通常咱們在分片的時候裏面裝入每一個片的信息,注意,每一個片最終也都是被封裝成一個小的skb,這個必須
的!
注意:具體怎麼分片的看上一篇博文:數據分片 ( 看其中的ip_fragment函數 )
那麼看一下其基本結構如圖四:
(圖四)
3、
最重要的是須要理解對於這個skb是怎麼操做的,在操做的過程當中,每一塊的內存分配是怎麼變化的,這才更重要!
看下面的函數們:
> : alloc_skb()函數
其實看__alloc_skb函數:
那麼alloc以後的圖就是(圖五):
(圖五)
其實和圖二是同樣的!咱們能夠看到,如今僅僅是分配了線束數據區域,可是如今尚未數據!必定要注意!因此前面三個指針指在一塊兒!由於沒有數據,那麼len和data_len的值就是0 !
> : skb_reserve函數
代碼其實很easy、就是移動兩個指針而已~
這個函數很重要,是爲「協議頭」預留空間!並且是盡最大的空間預留,由於不少頭都會有可選項,那麼咱們不知道可選項是多大,因此只能是按照最大的分配,那麼也說明了一點,預留的空間headroom也就是不必定都能使用完的!可能還有剩餘的,由上面的圖也能夠看出來!這也是爲何須要這麼多指針的問題!那麼這個函數直接致使head指針和tail、data指針分離,入下面圖六所示:
(圖六)
注意headroom就是用來存儲各個協議頭的足夠大的空間,tailroom就能夠認爲是存儲其餘線性數據的空間。( 這裏不要曲解協議頭不是線性數據,其實協議頭也是!!!因此當增長頭的時候,data指針向上移動,當增長其餘數據的時候,tail指針向下移動 )。如今data和tail指向一塊兒,那麼仍是說明數據沒有!!!
> : skb_put函數 ----> 用於操做線性數據區域(tailroom區域)的用戶數據
這函數其實就是從tailroom預留空間,至關因而移動tail指針,這樣若是從上圖(圖六)開始看,也就是tail開始向下移動,和data分離了。。。通常來講,這樣作都是爲了用戶數據再次處理,或者說爲TCP/IP的負載預留空間!
看圖七,當使用skb_put時候,由圖六---->圖七
(圖七)
咱們能夠看到指針的移動data仍是在headroom的下面,中間的是用戶數據預留的部分,由skb_put獲得,tail表示數據結尾!再看一下sk_buff中的len,變成了數據長度ld!!
> : skb_push函數:----------> 用於操做headroom區域的協議頭
和skb_put對應,上面試操做用戶數據的,這裏是操做協議頭的!其實就是data指針向上移動而已~注意len增大了哦~前面說了協議頭也是屬於數據!
以下面圖所示,由圖七---->圖八
(圖八)
咱們能夠知道,data向上移動了,同時注意len變成ld+lp了,其中lp是這個增長的協議頭的長度!
> : skb_pull函數:-----------> 其實這個函數纔是與skb_push函數對應的函數!由於這是去頭函數,而skb_push是增頭函數!因此這個函數通常用在解包的時候!
其實就是data指針向下移動,當前一個協議頭被去掉,headroom剩餘的空間增大了!看下圖:
由圖八---->圖九:
(圖九)
虛線是data以前的指針位置,如今移動到下面實線!!需注意:len的長度減少,減少的大小是剝去的頭的大小!!
4、
最後咱們從兩條線總體分析一下:
1:從應用層用戶數據開始,直到物理層發送出去
> 初始化的什麼就很少說了,和前面的差很少,如今也加入用戶數據已經在了,如圖七所示同樣!那麼到了TCP層,須要增長
TCP層的頭:
如圖10所示:
(圖10)
須要注意的是這裏是傳輸層,那麼傳輸層的結構u中的th表明的是tcp的頭,那麼tcp指向tcp頭OK!同時注意 len長度+=l1 哦~~~
> 再看到了IP層:如圖11
(圖11)
至於須要解釋什麼就沒什麼了,都是同樣的~
> 到鏈路層了:如圖12
(圖12)
OK!
2:第二個過程實際上是第一個逆過程,都差很少,因此很少說了~
5、
最後看一下操做skb的兩個函數pskb_copy和skb_copy
前者僅僅是將sk_buff的結構體和線性數據copy過來,對於非線性數據,是引用原始的skb的數據的!然後者是不只將sk_buff和線性數據拷貝,同時將非線性數據也copy了一份,看下面就明白了!這就在效率上就差了不少!因此若是不想修改數據,那麼仍是使用pskb_copy更好!
對於pskb_copy:
對於skb_copy:
OK 我以爲差很少了~~~~~結束~~~~~~~~~~~~~