乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      linux內(nèi)核sk_buff的結(jié)構(gòu)分析 - 但行好事 莫問前程 - JavaEye技術(shù)網(wǎng)站

       omcc 2011-01-21
      我看的內(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.我們先來看sk_buff_head的結(jié)構(gòu)。它也就是所有sk_buff的頭。

      Java代碼 復(fù)制代碼
      1. struct sk_buff_head {   
      2.     /* These two members must be first. */  
      3.     struct sk_buff  *next;   
      4.     struct sk_buff  *prev;   
      5.   
      6.     __u32       qlen;   
      7.     spinlock_t  lock;   
      8. };  


      這里可以看到前兩個(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域是自旋鎖。

      然后我們來看sk_buff,下面就是skb的結(jié)構(gòu):

      我這里注釋了一些簡單的域,復(fù)雜的域下面會(huì)單獨(dú)解釋。
      Java代碼 復(fù)制代碼
      1.   
      2. struct sk_buff {   
      3.     /* These two members must be first. */  
      4.     struct sk_buff      *next;   
      5.     struct sk_buff      *prev;   
      6.   
      7. //表示從屬于那個(gè)socket,主要是被4層用到。   
      8.     struct sock     *sk;   
      9. //表示這個(gè)skb被接收的時(shí)間。   
      10.     ktime_t         tstamp;   
      11. //這個(gè)表示一個(gè)網(wǎng)絡(luò)設(shè)備,當(dāng)skb為輸出時(shí)它表示skb將要輸出的設(shè)備,當(dāng)接收時(shí),它表示輸入設(shè)備。要注意,這個(gè)設(shè)備有可能會(huì)是虛擬設(shè)備(在3層以上看來)   
      12.     struct net_device   *dev;   
      13. ///這里其實(shí)應(yīng)該是dst_entry類型,不知道為什么內(nèi)核要改為ul。這個(gè)域主要用于路由子系統(tǒng)。這個(gè)數(shù)據(jù)結(jié)構(gòu)保存了一些路由相關(guān)信息   
      14.     unsigned long       _skb_dst;   
      15. #ifdef CONFIG_XFRM   
      16.     struct  sec_path    *sp;   
      17. #endif   
      18. ///這個(gè)域很重要,我們下面會(huì)詳細(xì)說明。這里只需要知道這個(gè)域是保存每層的控制信息的就夠了。   
      19.     char            cb[48];   
      20. ///這個(gè)長度表示當(dāng)前的skb中的數(shù)據(jù)的長度,這個(gè)長度即包括buf中的數(shù)據(jù)也包括切片的數(shù)據(jù),也就是保存在skb_shared_info中的數(shù)據(jù)。這個(gè)值是會(huì)隨著從一層到另一層而改變的。下面我們會(huì)對(duì)比這幾個(gè)長度的。   
      21.     unsigned int        len,   
      22. ///這個(gè)長度只表示切片數(shù)據(jù)的長度,也就是skb_shared_info中的長度。   
      23.                 data_len;   
      24. ///這個(gè)長度表示mac頭的長度(2層的頭的長度)   
      25.     __u16           mac_len,   
      26. ///這個(gè)主要用于clone的時(shí)候,它表示clone的skb的頭的長度。   
      27.                 hdr_len;   
      28.   
      29. ///接下來是校驗(yàn)相關(guān)的域。   
      30.     union {   
      31.         __wsum      csum;   
      32.         struct {   
      33.             __u16   csum_start;   
      34.             __u16   csum_offset;   
      35.         };   
      36.     };   
      37. ///優(yōu)先級(jí),主要用于QOS。   
      38.     __u32           priority;   
      39.     kmemcheck_bitfield_begin(flags1);   
      40. ///接下來是一些標(biāo)志位。   
      41. //首先是是否可以本地切片的標(biāo)志。   
      42.     __u8            local_df:1,   
      43. ///為1說明頭可能被clone。   
      44.                 cloned:1,   
      45. ///這個(gè)表示校驗(yàn)相關(guān)的一個(gè)標(biāo)記,表示硬件驅(qū)動(dòng)是否為我們已經(jīng)進(jìn)行了校驗(yàn)(前面的blog有介紹)   
      46.                 ip_summed:2,   
      47. ///這個(gè)域如果為1,則說明這個(gè)skb的頭域指針已經(jīng)分配完畢,因此這個(gè)時(shí)候計(jì)算頭的長度只需要head和data的差就可以了。   
      48.                 nohdr:1,   
      49. ///這個(gè)域不太理解什么意思。   
      50.                 nfctinfo:3;   
      51.   
      52. ///pkt_type主要是表示數(shù)據(jù)包的類型,比如多播,單播,回環(huán)等等。   
      53.     __u8            pkt_type:3,   
      54. ///這個(gè)域是一個(gè)clone標(biāo)記。主要是在fast clone中被設(shè)置,我們后面講到fast clone時(shí)會(huì)詳細(xì)介紹這個(gè)域。   
      55.                 fclone:2,   
      56. ///ipvs擁有的域。   
      57.                 ipvs_property:1,   
      58. ///這個(gè)域應(yīng)該是udp使用的一個(gè)域。表示只是查看數(shù)據(jù)。   
      59.                 peeked:1,   
      60. ///netfilter使用的域。是一個(gè)trace 標(biāo)記   
      61.                 nf_trace:1;   
      62. ///這個(gè)表示L3層的協(xié)議。比如IP,IPV6等等。   
      63.     __be16          protocol:16;   
      64.     kmemcheck_bitfield_end(flags1);   
      65. ///skb的析構(gòu)函數(shù),一般都是設(shè)置為sock_rfree或者sock_wfree.   
      66.     void            (*destructor)(struct sk_buff *skb);   
      67.   
      68. ///netfilter相關(guān)的域。   
      69. #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)   
      70.     struct nf_conntrack *nfct;   
      71.     struct sk_buff      *nfct_reasm;   
      72. #endif   
      73. #ifdef CONFIG_BRIDGE_NETFILTER   
      74.     struct nf_bridge_info   *nf_bridge;   
      75. #endif   
      76.   
      77. ///接收設(shè)備的index。   
      78.     int         iif;   
      79.   
      80. ///流量控制的相關(guān)域。   
      81. #ifdef CONFIG_NET_SCHED   
      82.     __u16           tc_index;   /* traffic control index */  
      83. #ifdef CONFIG_NET_CLS_ACT   
      84.     __u16           tc_verd;    /* traffic control verdict */  
      85. #endif   
      86. #endif   
      87.   
      88.     kmemcheck_bitfield_begin(flags2);   
      89. ///多隊(duì)列設(shè)備的映射,也就是說映射到那個(gè)隊(duì)列。   
      90.     __u16           queue_mapping:16;   
      91. #ifdef CONFIG_IPV6_NDISC_NODETYPE   
      92.     __u8            ndisc_nodetype:2;   
      93. #endif   
      94.     kmemcheck_bitfield_end(flags2);   
      95.   
      96.     /* 0/14 bit hole */  
      97.   
      98. #ifdef CONFIG_NET_DMA   
      99.     dma_cookie_t        dma_cookie;   
      100. #endif   
      101. #ifdef CONFIG_NETWORK_SECMARK   
      102.     __u32           secmark;   
      103. #endif   
      104. ///skb的標(biāo)記。   
      105.     __u32           mark;   
      106.   
      107. ///vlan的控制tag。   
      108.     __u16           vlan_tci;   
      109.   
      110. ///傳輸層的頭   
      111.     sk_buff_data_t      transport_header;   
      112. ///網(wǎng)絡(luò)層的頭   
      113.     sk_buff_data_t      network_header;   
      114. ///鏈路層的頭。   
      115.     sk_buff_data_t      mac_header;   
      116. ///接下來就是幾個(gè)操作skb數(shù)據(jù)的指針。下面會(huì)詳細(xì)介紹。   
      117.     sk_buff_data_t      tail;   
      118.     sk_buff_data_t      end;   
      119.     unsigned char       *head,   
      120.                 *data;   
      121. ///這個(gè)表示整個(gè)skb的大小,包括skb本身,以及數(shù)據(jù)。   
      122.     unsigned int        truesize;   
      123. ///skb的引用計(jì)數(shù)   
      124.     atomic_t        users;   
      125. };  


      我們來看前面沒有解釋的那些域。

      先來看cb域,他保存了每層所獨(dú)自需要的內(nèi)部數(shù)據(jù)。我們來看tcp的例子。

      我們知道tcp層的控制信息保存在tcp_skb_cb中,因此來看內(nèi)核提供的宏來存取這個(gè)數(shù)據(jù)結(jié)構(gòu):

      Java代碼 復(fù)制代碼
      1. #define TCP_SKB_CB(__skb)  ((struct tcp_skb_cb *)&((__skb)->cb[0]))  


      在ip層的話,我們可能會(huì)用cb來存取切片好的幀。

      Java代碼 復(fù)制代碼
      1. #define FRAG_CB(skb)    ((struct ipfrag_skb_cb *)((skb)->cb))  


      到這里你可能會(huì)問如果我們想要在到達(dá)下一層后,還想保存當(dāng)前層的私有信息怎么辦。這個(gè)時(shí)候我們就可以使用skb的clone了。也就是之只復(fù)制sk_buff結(jié)構(gòu)。

      然后我們來看幾個(gè)比較比較重要的域 len,data,tail,head,end。

      這幾個(gè)域都很簡單,下面這張圖表示了buffer從tcp層到鏈路層的過程中l(wèi)en,head,data,tail以及end的變化,通過這個(gè)圖我們可以非常清晰的了解到這幾個(gè)域的區(qū)別。




      可以很清楚的看到head指針為分配的buffer的起始位置,end為結(jié)束位置,而data為當(dāng)前數(shù)據(jù)的起始位置,tail為當(dāng)前數(shù)據(jù)的結(jié)束位置。len就是數(shù)據(jù)區(qū)的長度。

      然后來看transport_header,network_header以及mac_header的變化,這幾個(gè)指針都是隨著數(shù)據(jù)包到達(dá)不同的層次才會(huì)有對(duì)應(yīng)的值,我們來看下面的圖,這個(gè)圖表示了當(dāng)從2層到達(dá)3層對(duì)應(yīng)的指針的變化。




      這里可以看到data指針會(huì)由于數(shù)據(jù)包到了三層,而跳過2層的頭。這里我們就可以得到data起始真正指的是本層的頭以及數(shù)據(jù)的起始位置。

      然后我們來看skb的幾個(gè)重要操作函數(shù)。

      首先是skb_put,skb_push,skb_pull以及skb_reserve這幾個(gè)最長用的操作data指針的函數(shù)。

      這里可以看到內(nèi)核skb_XXX都還有一個(gè)__skb_XXX函數(shù),這是因?yàn)榍耙粋€(gè)只是將后一個(gè)函數(shù)進(jìn)行了一個(gè)包裝,加了一些校驗(yàn)。

      先來看__skb_put函數(shù)。
      可以看到它只是將tail指針移動(dòng)len個(gè)位置,然后len也相應(yīng)的增加len個(gè)大小。

      Java代碼 復(fù)制代碼
      1. static inline unsigned char *__skb_put(struct sk_buff *skb, unsigned int len)   
      2. {   
      3.     unsigned char *tmp = skb_tail_pointer(skb);   
      4.     SKB_LINEAR_ASSERT(skb);   
      5. ///改變相應(yīng)的域。   
      6.     skb->tail += len;   
      7.     skb->len  += len;   
      8.     return tmp;   
      9. }  


      然后是__skb_push,它是將data指針向上移動(dòng)len個(gè)位置,對(duì)應(yīng)的len肯定也是增加len大小。

      Java代碼 復(fù)制代碼
      1. static inline unsigned char *__skb_push(struct sk_buff *skb, unsigned int len)   
      2. {   
      3.     skb->data -= len;   
      4.     skb->len  += len;   
      5.     return skb->data;   
      6. }  


      剩下的兩個(gè)就不貼代碼了,都是很簡單的函數(shù),__skb_pull是將data指針向下移動(dòng)len個(gè)位置,然后len減小len大小。__skb_reserve是將整個(gè)數(shù)據(jù)區(qū),也就是data以及tail指針一起向下移動(dòng)len大小。這個(gè)函數(shù)一般是用來對(duì)齊地址用的。

      看下面的圖,描述了4個(gè)函數(shù)的操作:




      接著是skb的alloc函數(shù)。

      在內(nèi)核中分配一個(gè)skb是在__alloc_skb中實(shí)現(xiàn)的,接下來我們就來看這個(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)來的第三個(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之后,通過調(diào)用alloc_skb_fclone函數(shù)來分配一塊大于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的效率,不過調(diào)用flcone 一般都是我們確定接下來這個(gè)skb會(huì)被clone很多次。

      更詳細(xì)的fclone的介紹可以看這里:

      http:///Articles/140552/

      這樣我們先來看_alloc_skb,然后緊接著看skb_clone,這樣就能更好的理解這些。

      這里fclone的多分配的內(nèi)存部分,沒太弄懂從那里多分配的,自己對(duì)內(nèi)核的內(nèi)存子系統(tǒng)還是不太熟悉。覺得應(yīng)該是skbuff_fclone_cache中會(huì)自動(dòng)多分配些內(nèi)存。

      Java代碼 復(fù)制代碼
      1.   
      2. struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask,   
      3.                 int fclone, int node)   
      4. {   
      5.     struct kmem_cache *cache;   
      6.     struct skb_shared_info *shinfo;   
      7.     struct sk_buff *skb;   
      8.     u8 *data;   
      9.   
      10. ///這里通過fclone的值來判斷是要從fclone cache還是說從head cache中取。   
      11.     cache = fclone ? skbuff_fclone_cache : skbuff_head_cache;   
      12.   
      13. ///首先是分配skb,也就是包頭。   
      14.     skb = kmem_cache_alloc_node(cache, gfp_mask & ~__GFP_DMA, node);   
      15.     if (!skb)   
      16.         goto out;   
      17. ///首先將size對(duì)齊,這里是按一級(jí)緩存的大小來對(duì)齊。   
      18.     size = SKB_DATA_ALIGN(size);   
      19. ///然后是數(shù)據(jù)區(qū)的大小,大小為size+ sizeof(struct skb_shared_info的大小。   
      20.     data = kmalloc_node_track_caller(size + sizeof(struct skb_shared_info),   
      21.             gfp_mask, node);   
      22.     if (!data)   
      23.         goto nodata;   
      24.   
      25. ///初始化相關(guān)域。   
      26.     memset(skb, 0, offsetof(struct sk_buff, tail));   
      27. ///這里truesize可以看到就是我們分配的整個(gè)skb+data的大小   
      28.     skb->truesize = size + sizeof(struct sk_buff);   
      29. ///users加一。   
      30.     atomic_set(&skb->users, 1);   
      31. ///一開始head和data是一樣大的。   
      32.     skb->head = data;   
      33.     skb->data = data;   
      34. ///設(shè)置tail指針   
      35.     skb_reset_tail_pointer(skb);   
      36. ///一開始tail也就是和data是相同的。   
      37.     skb->end = skb->tail + size;   
      38.     kmemcheck_annotate_bitfield(skb, flags1);   
      39.     kmemcheck_annotate_bitfield(skb, flags2);   
      40. #ifdef NET_SKBUFF_DATA_USES_OFFSET   
      41.     skb->mac_header = ~0U;   
      42. #endif   
      43.   
      44. ///初始化shinfo,這個(gè)我就不介紹了,前面的blog分析切片時(shí),這個(gè)結(jié)構(gòu)很詳細(xì)的分析過了。   
      45.     shinfo = skb_shinfo(skb);   
      46.     atomic_set(&shinfo->dataref, 1);   
      47.     shinfo->nr_frags  = 0;   
      48.     shinfo->gso_size = 0;   
      49.     shinfo->gso_segs = 0;   
      50.     shinfo->gso_type = 0;   
      51.     shinfo->ip6_frag_id = 0;   
      52.     shinfo->tx_flags.flags = 0;   
      53.     skb_frag_list_init(skb);   
      54.     memset(&shinfo->hwtstamps, 0, sizeof(shinfo->hwtstamps));   
      55.   
      56. ///fclone為1,說明多分配了一塊內(nèi)存,因此需要設(shè)置對(duì)應(yīng)的fclone域。   
      57.     if (fclone) {   
      58. ///可以看到多分配的內(nèi)存剛好在當(dāng)前的skb的下方。   
      59.         struct sk_buff *child = skb + 1;   
      60.         atomic_t *fclone_ref = (atomic_t *) (child + 1);   
      61.   
      62.         kmemcheck_annotate_bitfield(child, flags1);   
      63.         kmemcheck_annotate_bitfield(child, flags2);   
      64. ///設(shè)置標(biāo)記。這里要注意,當(dāng)前的skb和多分配的skb設(shè)置的fclone是不同的。   
      65.         skb->fclone = SKB_FCLONE_ORIG;   
      66.         atomic_set(fclone_ref, 1);   
      67.   
      68.         child->fclone = SKB_FCLONE_UNAVAILABLE;   
      69.     }   
      70. out:   
      71.     return skb;   
      72. nodata:   
      73.     kmem_cache_free(cache, skb);   
      74.     skb = NULL;   
      75.     goto out;   
      76. }  


      下圖就是alloc_skb之后的skb的指針的狀態(tài)。這里忽略了fclone。




      然后我們來看skb_clone函數(shù),clone的意思就是只復(fù)制skb而不復(fù)制data域。

      這里它會(huì)先判斷將要被clone的skb的fclone段,以便與決定是否重新分配一塊內(nèi)存來保存skb。

      然后調(diào)用__skb_clone來初始化相關(guān)的域。

      Java代碼 復(fù)制代碼
      1.   
      2. struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask)   
      3. {   
      4.     struct sk_buff *n;   
      5.   
      6. ///n為skb緊跟著那塊內(nèi)存,這里如果skb是通過skb_fclone分配的,那么n就是一個(gè)skb。   
      7.     n = skb + 1;   
      8. ///skb和n的fclone都要符合要求,可以看到這里的值就是我們?cè)赺_alloc_skb中設(shè)置的值。   
      9.     if (skb->fclone == SKB_FCLONE_ORIG &&   
      10.         n->fclone == SKB_FCLONE_UNAVAILABLE) {   
      11. ///到這里,就說明我們不需要alloc一個(gè)skb,直接取n就可以了,并且設(shè)置fclone的標(biāo)記。并修改引用計(jì)數(shù)。   
      12.         atomic_t *fclone_ref = (atomic_t *) (n + 1);   
      13.         n->fclone = SKB_FCLONE_CLONE;   
      14.         atomic_inc(fclone_ref);   
      15.     } else {   
      16.   
      17. ///這里就需要從cache中取得一塊內(nèi)存。   
      18.         n = kmem_cache_alloc(skbuff_head_cache, gfp_mask);   
      19.         if (!n)   
      20.             return NULL;   
      21.   
      22.         kmemcheck_annotate_bitfield(n, flags1);   
      23.         kmemcheck_annotate_bitfield(n, flags2);   
      24. ///設(shè)置新的skb的fclone域。這里我們新建的skb,沒有被fclone的都是這個(gè)標(biāo)記。   
      25.         n->fclone = SKB_FCLONE_UNAVAILABLE;   
      26.     }   
      27.   
      28.     return __skb_clone(n, skb);   
      29. }  


      這里__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.

      我們先來看pskb_copy,函數(shù)先alloc一個(gè)新的skb,然后調(diào)用skb_copy_from_linear_data來復(fù)制線性區(qū)的數(shù)據(jù)。

      Java代碼 復(fù)制代碼
      1.   
      2. struct sk_buff *pskb_copy(struct sk_buff *skb, gfp_t gfp_mask)   
      3. {   
      4.     /*  
      5.      *  Allocate the copy buffer  
      6.      */  
      7.     struct sk_buff *n;   
      8. #ifdef NET_SKBUFF_DATA_USES_OFFSET   
      9.     n = alloc_skb(skb->end, gfp_mask);   
      10. #else  
      11.     n = alloc_skb(skb->end - skb->head, gfp_mask);   
      12. #endif   
      13.     if (!n)   
      14.         goto out;   
      15.   
      16.     /* Set the data pointer */  
      17.     skb_reserve(n, skb->data - skb->head);   
      18.     /* Set the tail pointer and length */  
      19.     skb_put(n, skb_headlen(skb));   
      20. ///復(fù)制線性數(shù)據(jù)段。   
      21.     skb_copy_from_linear_data(skb, n->data, n->len);   
      22. ///更新相關(guān)域   
      23.     n->truesize += skb->data_len;   
      24.     n->data_len  = skb->data_len;   
      25.     n->len        = skb->len;   
      26.   
      27. ///下面只是復(fù)制切片數(shù)據(jù)的指針   
      28. if (skb_shinfo(skb)->nr_frags) {   
      29.         int i;   
      30.   
      31.         for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {   
      32.             skb_shinfo(n)->frags[i] = skb_shinfo(skb)->frags[i];   
      33.             get_page(skb_shinfo(n)->frags[i].page);   
      34.         }   
      35.         skb_shinfo(n)->nr_frags = i;   
      36.     }   
      37.   
      38. ...............................   
      39.     copy_skb_header(n, skb);   
      40. out:   
      41.     return n;   
      42. }  


      然后是skb_copy,它是復(fù)制skb的所有數(shù)據(jù)段,包括切片數(shù)據(jù):

      Java代碼 復(fù)制代碼
      1. struct sk_buff *skb_copy(const struct sk_buff *skb, gfp_t gfp_mask)   
      2. {   
      3.     int headerlen = skb->data - skb->head;   
      4.     /*  
      5.      *  Allocate the copy buffer  
      6.      */  
      7. //先alloc一個(gè)新的skb   
      8.     struct sk_buff *n;   
      9. #ifdef NET_SKBUFF_DATA_USES_OFFSET   
      10.     n = alloc_skb(skb->end + skb->data_len, gfp_mask);   
      11. #else  
      12.     n = alloc_skb(skb->end - skb->head + skb->data_len, gfp_mask);   
      13. #endif   
      14.     if (!n)   
      15.         return NULL;   
      16.   
      17.     /* Set the data pointer */  
      18.     skb_reserve(n, headerlen);   
      19.     /* Set the tail pointer and length */  
      20.     skb_put(n, skb->len);   
      21. ///然后復(fù)制所有的數(shù)據(jù)。   
      22.     if (skb_copy_bits(skb, -headerlen, n->head, headerlen + skb->len))   
      23.         BUG();   
      24.   
      25.     copy_skb_header(n, skb);   
      26.     return n;   
      27. }  


      下面這張圖就表示了psb_copy和skb_copy調(diào)用后的內(nèi)存模型,其中a是pskb_copy,b是skb_copy:





      最后來看skb的釋放:
      這里主要是判斷一個(gè)引用標(biāo)記位users,將它減一,如果大于0則直接返回,否則釋放skb。

      Java代碼 復(fù)制代碼
      1. void kfree_skb(struct sk_buff *skb)   
      2. {   
      3.     if (unlikely(!skb))   
      4.         return;   
      5.     if (likely(atomic_read(&skb->users) == 1))   
      6.         smp_rmb();   
      7. ///減一,然后判斷。   
      8.     else if (likely(!atomic_dec_and_test(&skb->users)))   
      9.         return;   
      10.     trace_kfree_skb(skb, __builtin_return_address(0));   
      11.     __kfree_skb(skb);   
      12. }  

        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶 評(píng)論公約

        類似文章 更多