我看的內(nèi)核版本是2.6.32.
在內(nèi)核中sk_buff表示一個(gè)網(wǎng)絡(luò)數(shù)據(jù)包,它是一個(gè)雙向鏈表,而鏈表頭就是sk_buff_head,在老的內(nèi)核里面sk_buff會(huì)有一個(gè)list域直接指向sk_buff_head也就是鏈表頭,現(xiàn)在在2.6.32里面這個(gè)域已經(jīng)被刪除了。
而sk_buff的內(nèi)存布局可以分作3個(gè)段,第一個(gè)就是sk_buff自身,第二個(gè)是linear-data buff,第三個(gè)是paged-data buff(也就是skb_shared_info)。
ok.我們先來(lái)看sk_buff_head的結(jié)構(gòu)。它也就是所有sk_buff的頭。
- struct sk_buff_head {
-
- struct sk_buff *next;
- struct sk_buff *prev;
-
- __u32 qlen;
- spinlock_t lock;
- };
struct sk_buff_head {/* These two members must be first. */struct sk_buff *next;struct sk_buff *prev;__u32 qlen;spinlock_t lock;};
這里可以看到前兩個(gè)域是和sk_buff一致的,而且內(nèi)核的注釋是必須放到最前面。這里的原因是:
這使得兩個(gè)不同的結(jié)構(gòu)可以放到同一個(gè)鏈表中,盡管sk_buff_head要比sk_buff小巧的多。另外,相同的函數(shù)可以同樣應(yīng)用于sk_buff和sk_buff_head。
然后qlen域表示了當(dāng)前的sk_buff鏈上包含多少個(gè)skb。
lock域是自旋鎖。
然后我們來(lái)看sk_buff,下面就是skb的結(jié)構(gòu):
我這里注釋了一些簡(jiǎn)單的域,復(fù)雜的域下面會(huì)單獨(dú)解釋。
-
- struct sk_buff {
-
- struct sk_buff *next;
- struct sk_buff *prev;
-
-
- struct sock *sk;
-
- ktime_t tstamp;
-
- struct net_device *dev;
-
- unsigned long _skb_dst;
- #ifdef CONFIG_XFRM
- struct sec_path *sp;
- #endif
-
- char cb[48];
-
- unsigned int len,
-
- data_len;
-
- __u16 mac_len,
-
- hdr_len;
-
-
- union {
- __wsum csum;
- struct {
- __u16 csum_start;
- __u16 csum_offset;
- };
- };
-
- __u32 priority;
- kmemcheck_bitfield_begin(flags1);
-
-
- __u8 local_df:1,
-
- cloned:1,
-
- ip_summed:2,
-
- nohdr:1,
-
- nfctinfo:3;
-
-
- __u8 pkt_type:3,
-
- fclone:2,
-
- ipvs_property:1,
-
- peeked:1,
-
- nf_trace:1;
-
- __be16 protocol:16;
- kmemcheck_bitfield_end(flags1);
-
- void (*destructor)(struct sk_buff *skb);
-
-
- #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
- struct nf_conntrack *nfct;
- struct sk_buff *nfct_reasm;
- #endif
- #ifdef CONFIG_BRIDGE_NETFILTER
- struct nf_bridge_info *nf_bridge;
- #endif
-
-
- int iif;
-
-
- #ifdef CONFIG_NET_SCHED
- __u16 tc_index;
- #ifdef CONFIG_NET_CLS_ACT
- __u16 tc_verd;
- #endif
- #endif
-
- kmemcheck_bitfield_begin(flags2);
-
- __u16 queue_mapping:16;
- #ifdef CONFIG_IPV6_NDISC_NODETYPE
- __u8 ndisc_nodetype:2;
- #endif
- kmemcheck_bitfield_end(flags2);
-
-
-
- #ifdef CONFIG_NET_DMA
- dma_cookie_t dma_cookie;
- #endif
- #ifdef CONFIG_NETWORK_SECMARK
- __u32 secmark;
- #endif
-
- __u32 mark;
-
-
- __u16 vlan_tci;
-
-
- sk_buff_data_t transport_header;
-
- sk_buff_data_t network_header;
-
- sk_buff_data_t mac_header;
-
- sk_buff_data_t tail;
- sk_buff_data_t end;
- unsigned char *head,
- *data;
-
- unsigned int truesize;
-
- atomic_t users;
- };
struct sk_buff {/* These two members must be first. */struct sk_buff *next;struct sk_buff *prev;//表示從屬于那個(gè)socket,主要是被4層用到。struct sock *sk;//表示這個(gè)skb被接收的時(shí)間。ktime_t tstamp;//這個(gè)表示一個(gè)網(wǎng)絡(luò)設(shè)備,當(dāng)skb為輸出時(shí)它表示skb將要輸出的設(shè)備,當(dāng)接收時(shí),它表示輸入設(shè)備。要注意,這個(gè)設(shè)備有可能會(huì)是虛擬設(shè)備(在3層以上看來(lái))struct net_device *dev;///這里其實(shí)應(yīng)該是dst_entry類型,不知道為什么內(nèi)核要改為ul。這個(gè)域主要用于路由子系統(tǒng)。這個(gè)數(shù)據(jù)結(jié)構(gòu)保存了一些路由相關(guān)信息unsigned long _skb_dst;#ifdef CONFIG_XFRMstruct sec_path *sp;#endif///這個(gè)域很重要,我們下面會(huì)詳細(xì)說(shuō)明。這里只需要知道這個(gè)域是保存每層的控制信息的就夠了。char cb[48];///這個(gè)長(zhǎng)度表示當(dāng)前的skb中的數(shù)據(jù)的長(zhǎng)度,這個(gè)長(zhǎng)度即包括buf中的數(shù)據(jù)也包括切片的數(shù)據(jù),也就是保存在skb_shared_info中的數(shù)據(jù)。這個(gè)值是會(huì)隨著從一層到另一層而改變的。下面我們會(huì)對(duì)比這幾個(gè)長(zhǎng)度的。unsigned int len,///這個(gè)長(zhǎng)度只表示切片數(shù)據(jù)的長(zhǎng)度,也就是skb_shared_info中的長(zhǎng)度。data_len;///這個(gè)長(zhǎng)度表示mac頭的長(zhǎng)度(2層的頭的長(zhǎng)度)__u16 mac_len,///這個(gè)主要用于clone的時(shí)候,它表示clone的skb的頭的長(zhǎng)度。hdr_len;///接下來(lái)是校驗(yàn)相關(guān)的域。union {__wsum csum;struct {__u16 csum_start;__u16 csum_offset;};};///優(yōu)先級(jí),主要用于QOS。__u32 priority;kmemcheck_bitfield_begin(flags1);///接下來(lái)是一些標(biāo)志位。//首先是是否可以本地切片的標(biāo)志。__u8 local_df:1,///為1說(shuō)明頭可能被clone。cloned:1,///這個(gè)表示校驗(yàn)相關(guān)的一個(gè)標(biāo)記,表示硬件驅(qū)動(dòng)是否為我們已經(jīng)進(jìn)行了校驗(yàn)(前面的blog有介紹)ip_summed:2,///這個(gè)域如果為1,則說(shuō)明這個(gè)skb的頭域指針已經(jīng)分配完畢,因此這個(gè)時(shí)候計(jì)算頭的長(zhǎng)度只需要head和data的差就可以了。nohdr:1,///這個(gè)域不太理解什么意思。nfctinfo:3;///pkt_type主要是表示數(shù)據(jù)包的類型,比如多播,單播,回環(huán)等等。__u8 pkt_type:3,///這個(gè)域是一個(gè)clone標(biāo)記。主要是在fast clone中被設(shè)置,我們后面講到fast clone時(shí)會(huì)詳細(xì)介紹這個(gè)域。fclone:2,///ipvs擁有的域。ipvs_property:1,///這個(gè)域應(yīng)該是udp使用的一個(gè)域。表示只是查看數(shù)據(jù)。peeked:1,///netfilter使用的域。是一個(gè)trace 標(biāo)記nf_trace:1;///這個(gè)表示L3層的協(xié)議。比如IP,IPV6等等。__be16 protocol:16;kmemcheck_bitfield_end(flags1);///skb的析構(gòu)函數(shù),一般都是設(shè)置為sock_rfree或者sock_wfree.void (*destructor)(struct sk_buff *skb);///netfilter相關(guān)的域。#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)struct nf_conntrack *nfct;struct sk_buff *nfct_reasm;#endif#ifdef CONFIG_BRIDGE_NETFILTERstruct nf_bridge_info *nf_bridge;#endif///接收設(shè)備的index。int iif;///流量控制的相關(guān)域。#ifdef CONFIG_NET_SCHED__u16 tc_index; /* traffic control index */#ifdef CONFIG_NET_CLS_ACT__u16 tc_verd; /* traffic control verdict */#endif#endifkmemcheck_bitfield_begin(flags2);///多隊(duì)列設(shè)備的映射,也就是說(shuō)映射到那個(gè)隊(duì)列。__u16 queue_mapping:16;#ifdef CONFIG_IPV6_NDISC_NODETYPE__u8 ndisc_nodetype:2;#endifkmemcheck_bitfield_end(flags2);/* 0/14 bit hole */#ifdef CONFIG_NET_DMAdma_cookie_t dma_cookie;#endif#ifdef CONFIG_NETWORK_SECMARK__u32 secmark;#endif///skb的標(biāo)記。__u32 mark;///vlan的控制tag。__u16 vlan_tci;///傳輸層的頭sk_buff_data_t transport_header;///網(wǎng)絡(luò)層的頭sk_buff_data_t network_header;///鏈路層的頭。sk_buff_data_t mac_header;///接下來(lái)就是幾個(gè)操作skb數(shù)據(jù)的指針。下面會(huì)詳細(xì)介紹。sk_buff_data_t tail;sk_buff_data_t end;unsigned char *head,*data;///這個(gè)表示整個(gè)skb的大小,包括skb本身,以及數(shù)據(jù)。unsigned int truesize;///skb的引用計(jì)數(shù)atomic_t users;};
我們來(lái)看前面沒(méi)有解釋的那些域。
先來(lái)看cb域,他保存了每層所獨(dú)自需要的內(nèi)部數(shù)據(jù)。我們來(lái)看tcp的例子。
我們知道tcp層的控制信息保存在tcp_skb_cb中,因此來(lái)看內(nèi)核提供的宏來(lái)存取這個(gè)數(shù)據(jù)結(jié)構(gòu):
- #define TCP_SKB_CB(__skb) ((struct tcp_skb_cb *)&((__skb)->cb[0]))
#define TCP_SKB_CB(__skb) ((struct tcp_skb_cb *)&((__skb)->cb[0]))
在ip層的話,我們可能會(huì)用cb來(lái)存取切片好的幀。
- #define FRAG_CB(skb) ((struct ipfrag_skb_cb *)((skb)->cb))
#define FRAG_CB(skb) ((struct ipfrag_skb_cb *)((skb)->cb))
到這里你可能會(huì)問(wèn)如果我們想要在到達(dá)下一層后,還想保存當(dāng)前層的私有信息怎么辦。這個(gè)時(shí)候我們就可以使用skb的clone了。也就是之只復(fù)制sk_buff結(jié)構(gòu)。
然后我們來(lái)看幾個(gè)比較比較重要的域 len,data,tail,head,end。
這幾個(gè)域都很簡(jiǎn)單,下面這張圖表示了buffer從tcp層到鏈路層的過(guò)程中l(wèi)en,head,data,tail以及end的變化,通過(guò)這個(gè)圖我們可以非常清晰的了解到這幾個(gè)域的區(qū)別。
可以很清楚的看到head指針為分配的buffer的起始位置,end為結(jié)束位置,而data為當(dāng)前數(shù)據(jù)的起始位置,tail為當(dāng)前數(shù)據(jù)的結(jié)束位置。len就是數(shù)據(jù)區(qū)的長(zhǎng)度。
然后來(lái)看transport_header,network_header以及mac_header的變化,這幾個(gè)指針都是隨著數(shù)據(jù)包到達(dá)不同的層次才會(huì)有對(duì)應(yīng)的值,我們來(lái)看下面的圖,這個(gè)圖表示了當(dāng)從2層到達(dá)3層對(duì)應(yīng)的指針的變化。
這里可以看到data指針會(huì)由于數(shù)據(jù)包到了三層,而跳過(guò)2層的頭。這里我們就可以得到data起始真正指的是本層的頭以及數(shù)據(jù)的起始位置。
然后我們來(lái)看skb的幾個(gè)重要操作函數(shù)。
首先是skb_put,skb_push,skb_pull以及skb_reserve這幾個(gè)最長(zhǎng)用的操作data指針的函數(shù)。
這里可以看到內(nèi)核skb_XXX都還有一個(gè)__skb_XXX函數(shù),這是因?yàn)榍耙粋€(gè)只是將后一個(gè)函數(shù)進(jìn)行了一個(gè)包裝,加了一些校驗(yàn)。
先來(lái)看__skb_put函數(shù)。
可以看到它只是將tail指針移動(dòng)len個(gè)位置,然后len也相應(yīng)的增加len個(gè)大小。
- static inline unsigned char *__skb_put(struct sk_buff *skb, unsigned int len)
- {
- unsigned char *tmp = skb_tail_pointer(skb);
- SKB_LINEAR_ASSERT(skb);
-
- skb->tail += len;
- skb->len += len;
- return tmp;
- }
static inline unsigned char *__skb_put(struct sk_buff *skb, unsigned int len){unsigned char *tmp = skb_tail_pointer(skb);SKB_LINEAR_ASSERT(skb);///改變相應(yīng)的域。skb->tail += len;skb->len += len;return tmp;}
然后是__skb_push,它是將data指針向上移動(dòng)len個(gè)位置,對(duì)應(yīng)的len肯定也是增加len大小。
- static inline unsigned char *__skb_push(struct sk_buff *skb, unsigned int len)
- {
- skb->data -= len;
- skb->len += len;
- return skb->data;
- }
static inline unsigned char *__skb_push(struct sk_buff *skb, unsigned int len){skb->data -= len;skb->len += len;return skb->data;}
剩下的兩個(gè)就不貼代碼了,都是很簡(jiǎn)單的函數(shù),__skb_pull是將data指針向下移動(dòng)len個(gè)位置,然后len減小len大小。__skb_reserve是將整個(gè)數(shù)據(jù)區(qū),也就是data以及tail指針一起向下移動(dòng)len大小。這個(gè)函數(shù)一般是用來(lái)對(duì)齊地址用的。
看下面的圖,描述了4個(gè)函數(shù)的操作:
接著是skb的alloc函數(shù)。
在內(nèi)核中分配一個(gè)skb是在__alloc_skb中實(shí)現(xiàn)的,接下來(lái)我們就來(lái)看這個(gè)函數(shù)的具體實(shí)現(xiàn)。
這個(gè)函數(shù)起始可以看作三部分,第一部分是從cache中分配內(nèi)存,第二部分是初始化分配的skb的相關(guān)域。第三部分是處理fclone。
還有一個(gè)要注意的就是這里__alloc_skb是被三個(gè)函數(shù)包裝后才能直接使用的,我們只看前兩個(gè),一個(gè)是skb_alloc_skb,一個(gè)是alloc_skb_fclone函數(shù),這兩個(gè)函數(shù)傳遞進(jìn)來(lái)的第三個(gè)參數(shù),也就是fclone前一個(gè)是0,后一個(gè)是1.
那么這個(gè)函數(shù)是什么意思呢,它和alloc_skb有什么區(qū)別的。
這個(gè)函數(shù)可以叫做Fast SKB cloning函數(shù),這個(gè)函數(shù)存在的主要原因是,以前我們每次skb_clone一個(gè)skb的時(shí)候,都是要調(diào)用kmem_cache_alloc從cache中alloc一塊新的內(nèi)存。而現(xiàn)在當(dāng)我們擁有了fast clone之后,通過(guò)調(diào)用alloc_skb_fclone函數(shù)來(lái)分配一塊大于sizeof(struct sk_buff)的內(nèi)存,也就是在這次請(qǐng)求的skb的下方多申請(qǐng)了一些內(nèi)存,然后返回的時(shí)候設(shè)置返回的skb的fclone標(biāo)記為SKB_FCLONE_ORIG,而多申請(qǐng)的那塊內(nèi)存的sk_buff的fclone為SKB_FCLONE_UNAVAILABLE,這樣當(dāng)我們調(diào)用skb_clone克隆這個(gè)skb的時(shí)候看到fclone的標(biāo)記就可以直接將skb的指針+1,而不需要從cache中取了。這樣的話節(jié)省了一次內(nèi)存存取,提高了clone的效率,不過(guò)調(diào)用flcone 一般都是我們確定接下來(lái)這個(gè)skb會(huì)被clone很多次。
更詳細(xì)的fclone的介紹可以看這里:
http://lwn.net/Articles/140552/
這樣我們先來(lái)看_alloc_skb,然后緊接著看skb_clone,這樣就能更好的理解這些。
這里fclone的多分配的內(nèi)存部分,沒(méi)太弄懂從那里多分配的,自己對(duì)內(nèi)核的內(nèi)存子系統(tǒng)還是不太熟悉。覺(jué)得應(yīng)該是skbuff_fclone_cache中會(huì)自動(dòng)多分配些內(nèi)存。
-
- struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask,
- int fclone, int node)
- {
- struct kmem_cache *cache;
- struct skb_shared_info *shinfo;
- struct sk_buff *skb;
- u8 *data;
-
-
- cache = fclone ? skbuff_fclone_cache : skbuff_head_cache;
-
-
- skb = kmem_cache_alloc_node(cache, gfp_mask & ~__GFP_DMA, node);
- if (!skb)
- goto out;
-
- size = SKB_DATA_ALIGN(size);
-
- data = kmalloc_node_track_caller(size + sizeof(struct skb_shared_info),
- gfp_mask, node);
- if (!data)
- goto nodata;
-
-
- memset(skb, 0, offsetof(struct sk_buff, tail));
-
- skb->truesize = size + sizeof(struct sk_buff);
-
- atomic_set(&skb->users, 1);
-
- skb->head = data;
- skb->data = data;
-
- skb_reset_tail_pointer(skb);
-
- skb->end = skb->tail + size;
- kmemcheck_annotate_bitfield(skb, flags1);
- kmemcheck_annotate_bitfield(skb, flags2);
- #ifdef NET_SKBUFF_DATA_USES_OFFSET
- skb->mac_header = ~0U;
- #endif
-
-
- shinfo = skb_shinfo(skb);
- atomic_set(&shinfo->dataref, 1);
- shinfo->nr_frags = 0;
- shinfo->gso_size = 0;
- shinfo->gso_segs = 0;
- shinfo->gso_type = 0;
- shinfo->ip6_frag_id = 0;
- shinfo->tx_flags.flags = 0;
- skb_frag_list_init(skb);
- memset(&shinfo->hwtstamps, 0, sizeof(shinfo->hwtstamps));
-
-
- if (fclone) {
-
- struct sk_buff *child = skb + 1;
- atomic_t *fclone_ref = (atomic_t *) (child + 1);
-
- kmemcheck_annotate_bitfield(child, flags1);
- kmemcheck_annotate_bitfield(child, flags2);
-
- skb->fclone = SKB_FCLONE_ORIG;
- atomic_set(fclone_ref, 1);
-
- child->fclone = SKB_FCLONE_UNAVAILABLE;
- }
- out:
- return skb;
- nodata:
- kmem_cache_free(cache, skb);
- skb = NULL;
- goto out;
- }
struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask,int fclone, int node){struct kmem_cache *cache;struct skb_shared_info *shinfo;struct sk_buff *skb;u8 *data;///這里通過(guò)fclone的值來(lái)判斷是要從fclone cache還是說(shuō)從head cache中取。cache = fclone ? skbuff_fclone_cache : skbuff_head_cache;///首先是分配skb,也就是包頭。skb = kmem_cache_alloc_node(cache, gfp_mask & ~__GFP_DMA, node);if (!skb)goto out;///首先將size對(duì)齊,這里是按一級(jí)緩存的大小來(lái)對(duì)齊。size = SKB_DATA_ALIGN(size);///然后是數(shù)據(jù)區(qū)的大小,大小為size+ sizeof(struct skb_shared_info的大小。data = kmalloc_node_track_caller(size + sizeof(struct skb_shared_info),gfp_mask, node);if (!data)goto nodata;///初始化相關(guān)域。memset(skb, 0, offsetof(struct sk_buff, tail));///這里truesize可以看到就是我們分配的整個(gè)skb+data的大小skb->truesize = size + sizeof(struct sk_buff);///users加一。atomic_set(&skb->users, 1);///一開(kāi)始head和data是一樣大的。skb->head = data;skb->data = data;///設(shè)置tail指針skb_reset_tail_pointer(skb);///一開(kāi)始tail也就是和data是相同的。skb->end = skb->tail + size;kmemcheck_annotate_bitfield(skb, flags1);kmemcheck_annotate_bitfield(skb, flags2);#ifdef NET_SKBUFF_DATA_USES_OFFSETskb->mac_header = ~0U;#endif///初始化shinfo,這個(gè)我就不介紹了,前面的blog分析切片時(shí),這個(gè)結(jié)構(gòu)很詳細(xì)的分析過(guò)了。shinfo = skb_shinfo(skb);atomic_set(&shinfo->dataref, 1);shinfo->nr_frags = 0;shinfo->gso_size = 0;shinfo->gso_segs = 0;shinfo->gso_type = 0;shinfo->ip6_frag_id = 0;shinfo->tx_flags.flags = 0;skb_frag_list_init(skb);memset(&shinfo->hwtstamps, 0, sizeof(shinfo->hwtstamps));///fclone為1,說(shuō)明多分配了一塊內(nèi)存,因此需要設(shè)置對(duì)應(yīng)的fclone域。if (fclone) {///可以看到多分配的內(nèi)存剛好在當(dāng)前的skb的下方。struct sk_buff *child = skb + 1;atomic_t *fclone_ref = (atomic_t *) (child + 1);kmemcheck_annotate_bitfield(child, flags1);kmemcheck_annotate_bitfield(child, flags2);///設(shè)置標(biāo)記。這里要注意,當(dāng)前的skb和多分配的skb設(shè)置的fclone是不同的。skb->fclone = SKB_FCLONE_ORIG;atomic_set(fclone_ref, 1);child->fclone = SKB_FCLONE_UNAVAILABLE;}out:return skb;nodata:kmem_cache_free(cache, skb);skb = NULL;goto out;}
下圖就是alloc_skb之后的skb的指針的狀態(tài)。這里忽略了fclone。
然后我們來(lái)看skb_clone函數(shù),clone的意思就是只復(fù)制skb而不復(fù)制data域。
這里它會(huì)先判斷將要被clone的skb的fclone段,以便與決定是否重新分配一塊內(nèi)存來(lái)保存skb。
然后調(diào)用__skb_clone來(lái)初始化相關(guān)的域。
-
- struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask)
- {
- struct sk_buff *n;
-
-
- n = skb + 1;
-
- if (skb->fclone == SKB_FCLONE_ORIG &&
- n->fclone == SKB_FCLONE_UNAVAILABLE) {
-
- atomic_t *fclone_ref = (atomic_t *) (n + 1);
- n->fclone = SKB_FCLONE_CLONE;
- atomic_inc(fclone_ref);
- } else {
-
-
- n = kmem_cache_alloc(skbuff_head_cache, gfp_mask);
- if (!n)
- return NULL;
-
- kmemcheck_annotate_bitfield(n, flags1);
- kmemcheck_annotate_bitfield(n, flags2);
-
- n->fclone = SKB_FCLONE_UNAVAILABLE;
- }
-
- return __skb_clone(n, skb);
- }
struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask){struct sk_buff *n;///n為skb緊跟著那塊內(nèi)存,這里如果skb是通過(guò)skb_fclone分配的,那么n就是一個(gè)skb。n = skb + 1;///skb和n的fclone都要符合要求,可以看到這里的值就是我們?cè)赺_alloc_skb中設(shè)置的值。if (skb->fclone == SKB_FCLONE_ORIG &&n->fclone == SKB_FCLONE_UNAVAILABLE) {///到這里,就說(shuō)明我們不需要alloc一個(gè)skb,直接取n就可以了,并且設(shè)置fclone的標(biāo)記。并修改引用計(jì)數(shù)。atomic_t *fclone_ref = (atomic_t *) (n + 1);n->fclone = SKB_FCLONE_CLONE;atomic_inc(fclone_ref);} else {///這里就需要從cache中取得一塊內(nèi)存。n = kmem_cache_alloc(skbuff_head_cache, gfp_mask);if (!n)return NULL;kmemcheck_annotate_bitfield(n, flags1);kmemcheck_annotate_bitfield(n, flags2);///設(shè)置新的skb的fclone域。這里我們新建的skb,沒(méi)有被fclone的都是這個(gè)標(biāo)記。n->fclone = SKB_FCLONE_UNAVAILABLE;}return __skb_clone(n, skb);}
這里__skb_clone就不介紹了,函數(shù)就是將要被clone的skb的域賦值給clone的skb。
下圖就是skb_clone之后的兩個(gè)skb的結(jié)構(gòu)圖:
當(dāng)一個(gè)skb被clone之后,這個(gè)skb的數(shù)據(jù)區(qū)是不能被修改的,這就意為著,我們存取數(shù)據(jù)不需要任何鎖??墒怯袝r(shí)我們需要修改數(shù)據(jù)區(qū),這個(gè)時(shí)候會(huì)有兩個(gè)選擇,一個(gè)是我們只修改linear段,也就是head和end之間的段,一種是我們還要修改切片數(shù)據(jù),也就是skb_shared_info.
這樣就有兩個(gè)函數(shù)供我們選擇,第一個(gè)是pskb_copy,第二個(gè)是skb_copy.
我們先來(lái)看pskb_copy,函數(shù)先alloc一個(gè)新的skb,然后調(diào)用skb_copy_from_linear_data來(lái)復(fù)制線性區(qū)的數(shù)據(jù)。
-
- struct sk_buff *pskb_copy(struct sk_buff *skb, gfp_t gfp_mask)
- {
-
-
-
- struct sk_buff *n;
- #ifdef NET_SKBUFF_DATA_USES_OFFSET
- n = alloc_skb(skb->end, gfp_mask);
- #else
- n = alloc_skb(skb->end - skb->head, gfp_mask);
- #endif
- if (!n)
- goto out;
-
-
- skb_reserve(n, skb->data - skb->head);
-
- skb_put(n, skb_headlen(skb));
-
- skb_copy_from_linear_data(skb, n->data, n->len);
-
- n->truesize += skb->data_len;
- n->data_len = skb->data_len;
- n->len = skb->len;
-
-
- if (skb_shinfo(skb)->nr_frags) {
- int i;
-
- for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
- skb_shinfo(n)->frags[i] = skb_shinfo(skb)->frags[i];
- get_page(skb_shinfo(n)->frags[i].page);
- }
- skb_shinfo(n)->nr_frags = i;
- }
-
- ...............................
- copy_skb_header(n, skb);
- out:
- return n;
- }
struct sk_buff *pskb_copy(struct sk_buff *skb, gfp_t gfp_mask){/** Allocate the copy buffer*/struct sk_buff *n;#ifdef NET_SKBUFF_DATA_USES_OFFSETn = alloc_skb(skb->end, gfp_mask);#elsen = alloc_skb(skb->end - skb->head, gfp_mask);#endifif (!n)goto out;/* Set the data pointer */skb_reserve(n, skb->data - skb->head);/* Set the tail pointer and length */skb_put(n, skb_headlen(skb));///復(fù)制線性數(shù)據(jù)段。skb_copy_from_linear_data(skb, n->data, n->len);///更新相關(guān)域n->truesize += skb->data_len;n->data_len = skb->data_len;n->len = skb->len;///下面只是復(fù)制切片數(shù)據(jù)的指針if (skb_shinfo(skb)->nr_frags) {int i;for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {skb_shinfo(n)->frags[i] = skb_shinfo(skb)->frags[i];get_page(skb_shinfo(n)->frags[i].page);}skb_shinfo(n)->nr_frags = i;}...............................copy_skb_header(n, skb);out:return n;}
然后是skb_copy,它是復(fù)制skb的所有數(shù)據(jù)段,包括切片數(shù)據(jù):
- struct sk_buff *skb_copy(const struct sk_buff *skb, gfp_t gfp_mask)
- {
- int headerlen = skb->data - skb->head;
-
-
-
-
- struct sk_buff *n;
- #ifdef NET_SKBUFF_DATA_USES_OFFSET
- n = alloc_skb(skb->end + skb->data_len, gfp_mask);
- #else
- n = alloc_skb(skb->end - skb->head + skb->data_len, gfp_mask);
- #endif
- if (!n)
- return NULL;
-
-
- skb_reserve(n, headerlen);
-
- skb_put(n, skb->len);
-
- if (skb_copy_bits(skb, -headerlen, n->head, headerlen + skb->len))
- BUG();
-
- copy_skb_header(n, skb);
- return n;
- }
struct sk_buff *skb_copy(const struct sk_buff *skb, gfp_t gfp_mask){int headerlen = skb->data - skb->head;/** Allocate the copy buffer*///先alloc一個(gè)新的skbstruct sk_buff *n;#ifdef NET_SKBUFF_DATA_USES_OFFSETn = alloc_skb(skb->end + skb->data_len, gfp_mask);#elsen = alloc_skb(skb->end - skb->head + skb->data_len, gfp_mask);#endifif (!n)return NULL;/* Set the data pointer */skb_reserve(n, headerlen);/* Set the tail pointer and length */skb_put(n, skb->len);///然后復(fù)制所有的數(shù)據(jù)。if (skb_copy_bits(skb, -headerlen, n->head, headerlen + skb->len))BUG();copy_skb_header(n, skb);return n;}
下面這張圖就表示了psb_copy和skb_copy調(diào)用后的內(nèi)存模型,其中a是pskb_copy,b是skb_copy:
最后來(lái)看skb的釋放:
這里主要是判斷一個(gè)引用標(biāo)記位users,將它減一,如果大于0則直接返回,否則釋放skb。
- void kfree_skb(struct sk_buff *skb)
- {
- if (unlikely(!skb))
- return;
- if (likely(atomic_read(&skb->users) == 1))
- smp_rmb();
-
- else if (likely(!atomic_dec_and_test(&skb->users)))
- return;
- trace_kfree_skb(skb, __builtin_return_address(0));
- __kfree_skb(skb);
- }
void kfree_skb(struct sk_buff *skb){if (unlikely(!skb))return;if (likely(atomic_read(&skb->users) == 1))smp_rmb();///減一,然后判斷。else if (likely(!atomic_dec_and_test(&skb->users)))return;trace_kfree_skb(skb, __builtin_return_address(0));__kfree_skb(skb);}