12.4.2 套接字緩衝區操作基本原理
在傳輸過程中,存在着多個套接字緩衝區,這些緩衝區組成一個鏈表,每個鏈表都有一個鏈表頭sk_buff_head ,鏈表中每個節點分別對應內存中一塊的數據區。因此對它的操作有兩種基本方式:第一種是對緩衝區鏈表進行操作;第二種是對緩衝區對應的數據區進行控制。
當我們向物理接口發送數據時或當我們從物理接口接收數據時,我們就利用鏈表操作;當我們要對數據區的內容進行處理時,我們就利用內存操作例程。這種操作機制對網絡傳輸是非常有效的。
前面我們講過,每個協議都要在發送數據時向緩衝區添加自己的協議頭和協議尾,而在接收數據時去掉協議頭和協議尾,那麼具體的操作是怎樣進行的呢?我們先看看對緩衝區操作的兩個基本的函數:
void append_frame(char *buf, int len){
struct sk_buff *skb=alloc_skb(len, GFP_ATOMIC); /*創建一個緩衝區*/
if(skb==NULL)
my_dropped++;
else {
kb_put(skb,len);
memcpy(skb->data,data,len); /*向緩衝區添加數據*/
skb_append(&my_list, skb); /*將該緩衝區加入緩衝區隊列*/
}
}
void process_frame(void){
struct sk_buff *skb;
while((skb=skb_dequeue(&my_list))!=NULL)
{
process_data(skb); /*將緩衝區的數據傳遞給協議層*/
kfree_skb(skb, FREE_READ); /*釋放緩衝區,緩衝區從此消失*/
}
}
這兩個非常簡單的程序片段,雖然它們不是源程序,但是它們恰當地描述了處理數據包的工作原理,append_frame( )描述了分配緩衝區。創建數據包過程process_frame( )描述了傳遞數據包,釋放緩衝區的的過程。關於它們的源程序,可以去參見 net/core/dev.c 中netif_rx( )函數和net_bh( )函數。你可以看出它們和上面我們提到的兩個函數非常相似。這兩個函數非常複雜,因爲他們必須保證數據能夠被正確的協議接收並且要負責流程的控制,但是他們最基本的操作是相同的。
讓我們再看看上面提到的函數--append_frame( )。當 alloc_skb( ) 函數獲得一個長度爲 len字節的緩衝區(如圖12.12 (a))後,該緩衝區包含以下內容:
◆緩衝區的頭部有零字節的頭部空間
◆零字節的數據空間
◆緩衝區的尾部有零字節的尾部空間
再看skb_put( )函數(如圖12.12 (d)),它的作用是從數據區的尾部向緩衝區尾部不斷擴大數據區大小,爲後面的memcpy( )函數分配空間。
當一個緩衝區創建以後,所有的可用空間都在緩衝區的尾部。在沒有向其中添加數據之前,首先被執行的函數調用是 skb_receive( )(如圖12.12 (b)),它使你在緩衝區頭部指定一定的空閒空間,因此許多發送數據的例程都是這樣開頭的:
skb=alloc_skb(len+headspace, GFP_KERNEL);
skb_reserve(skb, headspace);
skb_put(skb,len);
memcpy_fromfs(skb->data,data,len);
pass_to_m_protocol(skb);
圖12.12向我們展示了以上過程進行時,sk_buff 的變化情況:
圖12.12 sk_buff 的變化過程