socket buffer套接字缓存
生活随笔
收集整理的這篇文章主要介紹了
socket buffer套接字缓存
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
最近公司在開發機器人與服務器調度端的通信時需要使用socket,因此找到了該文章作為深刻理解socket內部運作。
Linux網絡核心數據結構是套接字緩存(socket buffer),簡稱skb。它代表一個要發送或處理的報文,并貫穿于整個協議棧。
1、?? ?套接字緩存skb由兩部分組成:
(1)?? ?報文數據:它保存了實際在網絡中傳輸的數據;
(2)?? ?管理數據:供內核處理報文的額外數據,這些數據構成了協議之間交換的控制信息。
當應用程序向一個socket傳輸數據之后,該socket將創建相應的套接字緩存,并將用戶數據拷貝到緩存中。當報文在各協議層傳達輸的過程中,每一導的報文頭將插入到用戶數據之前。skb為報文頭申請了足夠的空間,所以避免了由于插入報文頭而對報文進行多次拷貝。用戶數據只拷貝了兩次:一是從用戶空間拷貝到內核;二是報文數據從內核傳送到網絡適配器。
1.1、sk_buff
套接字緩存結構:
[cpp]?view plaincopy
與sk_buff相關的函數涉及到網絡報文存儲結構和控制結構的分配、復制、釋放,以及控制結構里的各指針的操作,還有各種標志的檢查。重要的函數說明如下:?
struct sk_buff *alloc_skb(unsigned int size,int gfp_mask )?
分配大小為size的存儲空間存放網絡報文,同時分配它的控制結構。size的值是16字節對齊的,gfp_mask是內存分配的優先級。常見的內存分配優先級有GFP_ATOMIC,代表分配過程不能被中斷,一般用于中斷上下文中分配內存;GFP_KERNEL,代表分配過程可以被中斷,相應的分配請求被放到等待隊列中。分配成功之后,因為還沒有存放具體的網絡報文,所以sk_buff的 data,tail指針都指向存儲空間的起始地址,len的大小為0,而且 is_clone和cloned兩個標記的值都是0。?
struct sk_buff *skb_clone(struct sk_buff *skb, int gfp_mask )?
從控制結構skb中 clone出一個新的控制結構,它們都指向同一個網絡報文。clone成功之后,將新的控制結構和原來的控制結構的 is_clone,cloned兩個標記都置位。同時還增加網絡報文的引用計數(這個引用計數存放在存儲空間的結束地址的內存中,由函數atomic_t *skb_datarefp(struct sk_buff *skb)訪問,引用計數記錄了這個存儲空間有多少個控制結構)。由于存在多個控制結構指向同一個存儲空間的情況,所以在修改存儲空間里面的內容時,先要確定這個存儲空間的引用計數為1,或者用下面的拷貝函數復制一個新的存儲空間,然后才可以修改它里面的內容。?
struct sk_buff *skb_copy(struct sk_buff *skb, int gfp_mask )?
復制控制結構skb和它所指的存儲空間的內容。復制成功之后,新的控制結構和存儲空間與原來的控制結構和存儲空間相對獨立。所以新的控制結構里的is_clone,cloned兩個標記都是0,而且新的存儲空間的引用計數是1。
?
void kfree_skb(struct sk_buff *skb)
釋放控制結構skb和它所指的存儲空間。由于一個存儲空間可以有多個控制結構,所以只有在存儲空間的引用計數為1的情況下才釋放存儲空間,一般情況下,只釋放控制結構skb。?
unsigned char *skb_put(struct sk_buff *skb, unsigned int len )?
將tail指針下移,并增加skb的 len值。data和 tail之間的空間就是可以存放網絡報文的空間。這個操作增加了可以存儲網絡報文的空間,但是增加不能使tail的值大于end的值,skb的 len值大于truesize的值。?
unsigned char *skb_push(struct sk_buff *skb, unsigned int len )?
將data指針上移,并增加skb的 len值。這個操作在存儲空間的頭部增加了一段可以存儲網絡報文的空間,上一個操作在存儲空間的尾部增加了一段可以存儲網絡報文的空間。但是增加不能使data的值小于head的值,skb的 len值大于truesize的值。?
unsigned char * skb_pull(struct sk_buff *skb, unsigned int len )?
將data指針下移,并減小skb的 len值。這個操作使data指針指向下一層網絡報文的頭部。?
void skb_reserve(struct sk_buff *skb, unsigned int len )?
將data指針和tail指針同時下移。這個操作在存儲空間的頭部預留 len長度的空隙。
?
void skb_trim(struct sk_buff *skb, unsigned int len )?
將網絡報文的長度縮減到 len。這個操作丟棄了網絡報文尾部的填充值。?
int skb_cloned(struct sk_buff *skb )?
判斷skb是否是一個 clone的控制結構。如果是clone的,它的cloned標記是1,而且它指向的存儲空間的引用計數大于1。
2、?? ?套接字緩存隊列(Socket-Buffer Queues)
2.1、sk_buff_head
在網絡協議棧的實現中,有時需要把許多網絡報文放到一個隊列中做異步處
理。LINUX 為此定義了相關的數據結構 sk_buff_head。這是一個雙向鏈表的
頭,它把sk_buff鏈接成一個雙向鏈表。
void skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk )?
將newsk加到鏈表 list的頭部。?
void skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk)
將newsk加到鏈表 list的尾部。?
struct sk_buff *skb_dequeue(struct sk_buff_head *list )?
從鏈表 list的頭部取下一個 sk_buff。?
struct sk_buff *skb_dequeue_tail(struct sk_buff_head *list )?
從鏈表 list的尾部取下一個 sk_buff。
?
skb_insert(struct sk_buff *old, struct sk_buff *newsk )?
將newsk加到old所在的鏈表上,并且 newsk在old的前面。
?
void skb_append(struct sk_buff *old, struct sk_buff *newsk )?
將newsk加到old所在的鏈表上,并且 newsk在old的后面。
?
void skb_unlink(struct sk_buff *skb )?
將skb從它所在的鏈表上取下。?
以上的鏈表操作都是先關中斷的。這在中斷上下文中是不需要的,所以另外有一套與上面函數同名但是有前綴“__”的函數供運行在中斷上下文中的函數調用。
總結
以上是生活随笔為你收集整理的socket buffer套接字缓存的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php自定义表单程序,自定义流程goof
- 下一篇: 100个Python实战项目(十二)Py