3.4.4 數據預留和對齊(skb_reserve, skb_push, skb_put, skb_pull)

轉自:http://book.51cto.com/art/201206/345043.htm緩存

 

《Linux內核源碼剖析:TCP/IP實現》本書詳細論述了Linux內核2.6.20版本中TCP/IP的實現。書中給出了大量的源代碼,經過對源代碼的詳細註釋,幫助讀者掌握TCP/IP的實現。本節爲你們介紹數據預留和對齊。函數

AD:51CTO 網+ 第十二期沙龍:大話數據之美_如何用數據驅動用戶體驗spa

 

3.4.4  數據預留和對齊指針

數據預留和對齊主要由skb_reserve()、skb_put()、skb_push()以及skb_pull()這幾個函數來完成。htm

1.skb_reserve()blog

skb_reserve()在數據緩存區頭部預留必定的空間,一般被用來在數據緩存區中插入協議首部或者在某個邊界上對齊。它並無把數據移出或移入數據緩存區,而只是簡單地更新了數據緩存區的兩個指針-分別指向負載起始和結尾的data和tail指針,圖3-15 展現了調用skb_reserve()先後這兩個指針的變化。接口

請注意:skb_reserve()只能用於空的SKB,一般會在分配SKB以後就調用該函數,此時data和tail指針還一同指向數據區的起始位置,如圖3-15a所示。例如,某個以太網設備驅動的接收函數,在分配SKB以後,向數據緩存區填充數據以前,會有這樣的一條語句skb_reserve(skb, 2),這是由於以太網頭長度爲14B,再加上2B就正好16字節邊界對齊,因此大多數以太網設備都會在數據包以前保留2B。ci

當SKB在協議棧中向下傳遞時,每一層協議都把skb->data指針向上移動,而後複製本層首部,同時更新skb->len。這些操做都使用圖3-15 中所示的函數完成。get

 
圖3-15  在接收過程當中使用skb_reserve()
a) 空的SKB  b) 頭部預留2個字節  c) 複製以太網幀到SKB

2.skb_push()源碼

skb_push()在數據緩存區的前頭加入一塊數據,與skb_reserve()相似,也並無真正向數據緩存區中添加數據,而只是移動數據緩存區的頭指針data和尾指針tail。數據由其餘函數複製到數據緩存區中。

函數執行步驟以下:

1)當TCP發送數據時,會根據一些條件,如TCP最大分段長度MSS、是否支持聚合分散I/O等,分配一個SKB。

2)TCP需在數據緩存區的頭部預留足夠的空間,用來填充各層首部。MAX_TCP_HEADER是各層首部長度的總和,它考慮了最壞的狀況:因爲TCP層不知道將要用哪一個接口發送包,它爲每一層預留了最大的首部長度,甚至還考慮了出現多個IP首部的可能性,由於在內核編譯支持IP over IP的狀況下,會遇到多個IP首部。

3)把TCP負載複製到數據緩存區。須要注意的是,圖3-16 只是一個例子,TCP負載可能會被組織成其餘形式,例如分片,在後續章節中將會看到一個分片的數據緩存區是什麼樣的。

 
圖3-16  TCP層向鏈路層傳遞時數據的填充過程
a) 空的SKB  b) 在SKB的頭部預留足夠的空間  c) 複製TCP數據  
d) 添加TCP首部  e) 添加IP首部  f) 添加以太網幀首部

4)TCP層添加TCP首部。

5)SKB傳遞到IP層,IP層爲數據包添加IP首部。

6)SKB傳遞到鏈路層,鏈路層爲數據包添加鏈路層首部。

3.skb_put()

skb_put()修改指向數據區末尾的指針tail,使之往下移len字節,即便數據區向下擴大len字節,並更新數據區長度len。調用skb_put()先後,SKB結構變化如圖3-17所示。

 
圖3-17  skb_put()示意
a) 調用前  b) 調用後

4.skb_pull()

skb_pull()經過將data指針往下移動,在數據區首部忽略len字節長度的數據,一般用於接收到數據包後在各層間由下往上傳遞時,上層忽略下層的首部。調用skb_pull()先後,SKB結構變化如圖3-18所示。

 
圖3-18  skb_pull()示意a) 調用前  b) 調用後
相關文章
相關標籤/搜索