skb(Struct sk_buffer)是TCP/IP堆棧中用於收發包的緩衝區域。它在接收數據的時候會進行2次拷貝,以提升性能:數據包進入網卡驅動後拷貝一次,從內核空間遞交給用戶空間的應用時再拷貝一次。網絡中所有數據包的封裝及解封都是通過這個結構進行的。
struct sk_buff { struct sk_buff *next; struct sk_buff *prev; struct sock *sk; struct skb_timeval tstamp; struct net_device *dev; struct net_device *input_dev; union { struct tcphdr *th; struct udphdr *uh; struct icmphdr *icmph; struct igmphdr *igmph; struct iphdr *ipiph; struct ipv6hdr *ipv6h; unsigned char *raw; } h; union { struct iphdr *iph; struct ipv6hdr *ipv6h; struct arphdr *arph; unsigned char *raw; } nh; union { unsigned char *raw; } mac; struct dst_entry *dst; struct sec_path *sp; char cb[40]; unsigned int len, data_len, mac_len, csum; __u32 priority; __u8 local_df:1, cloned:1, ip_summed:2, nohdr:1, nfctinfo:3; __u8 pkt_type:3, fclone:2; __be16 protocol; void (*destructor)(struct sk_buff *skb); /* These elements must be at the end, see alloc_skb() for details. */ unsigned int truesize; atomic_t users; unsigned char *head, *data, *tail, *end; };
一、成員變量解析
tstamp 可通過調用net_enable_timestamp(),用於記錄數據包send/recv時間。但是這個計算 會消耗一些時間。
*dev、*input_dev 這兩個值都是用來區分不同的設備,input_dev通常用來指向接收數據包優先級最高的網絡輸入設備。
cb[40] skb的控制塊, 用來保存重傳狀態或數據包的序列號等信息。
len 表示skb中數據緩衝的總長度
priority 定義數據包的優先級, 傳輸控制模塊根據這個值來實現對數據包的分類,以決定調度策略
cloned 當對一個已存在的skb進行復制後,新skb會共享已有的skb數據區,這時這兩個skb的clone值都會被設置成1
truesize 就是skb所消耗的內存(skb本身+databuffer)
rcvbuf 接收數據包的總大小
sndbuf 發送數據包的總大小
[*head, *data, *tail, *end]
這四個指正用來實現對databuffer的管理。head和end用於指向buffer, data和tail用來指向實際的數據。這四個指正將buffer劃分爲三個區
packet data:用來保存真正的數據
head room:packetdata上面的空閒空間,用於給協議頭分配內存
tail room: packetdata下面的空閒空間, 用於給數據分配內存
劃分了三個區,這樣當數據包需要增加協議頭的時候就只需要從上層空間拿一塊內存,當需要增加數據的時候就只需要從下層空間拿一塊內存。這樣skb就只需要申請一次內存,之後的處理只需通過指正就可完成。
二、關於skb的一些操作
創建一個skb結構
static inline struct sk_buff *alloc_skb( unsigned int size, gfp_t priority) { return __alloc_skb(size, priority, 0, -1) //fclone位,1表對當前skb克隆, 0表不克隆 }
size : 爲將要分配的緩存區的大小;
priority : 取值爲GFP_ATOMIC, GFP_KERNEL等(指對內存搶佔的優先級);
釋放skb結構
void kfree_skb(struct sk_buff *skb);這裏會進行一個判斷,如果skb->users > 1,則只進行atomic_dec(&skb->user)操作,然後返回。如果skb->users = 1,則纔會將struct sk_buff結構體所佔的內存還給系統。
這裏應該是判斷是否存在共享,沒咋搞懂。。。
三、內核接收數據包方式
中斷方式
2.4之前都是用中斷方式接收的。數據包到達網卡後產生一箇中斷,中斷服務程序將接收的數據拷貝到skb,然後將skb掛入軟中斷隊列上,同時將設備poll_list加入軟中斷的poll_list列表並標記,然後等待軟中斷處理。發生軟中斷的時候將skb從軟中斷隊列中取出,發送給網絡上層(netif_receive_skb())。
輪詢方式
2.6中增加了輪詢機制。當中斷來臨時,它首先直接將設備poll_list掛入軟中斷的poll_list,打上軟中斷標記,先不對數據包進行接收,把接收數據包的任務給軟中斷了。發生軟中斷時直接把網卡數據poll給skb, 然後交給上層(netif_receive_skb())。
netif_receive_skb()得到skb包之後就處理該數據包, 檢查正確性,打印時間戳。