Linux 內核網絡協議棧 ------sk_buff 結構體 以及 完全解釋 (2.6.16)

在2.6.24之后這個結構體有了較大的變化,此處先說一說2.6.16版本的sk_buff,以及解釋一些問題。


一、

先直觀的看一下這個結構體~~~~~~~~~~~~~~~~~~~~~~在下面解釋每個字段的意義~~~~~~~~~~~

[cpp]?view plain?copy
?print?
  1. struct?sk_buff?{??
  2. ?????????/*?These?two?members?must?be?first.?*/??
  3. ?????????struct?sk_buff??????????*next;??
  4. ?????????struct?sk_buff??????????*prev;??
  5. ???
  6. ?????????struct?sock?????????????*sk;??
  7. ?????????struct?skb_timeval??????tstamp;??
  8. ?????????struct?net_device???????*dev;??
  9. ?????????struct?net_device???????*input_dev;??
  10. ???
  11. ?????????union?{??
  12. ?????????????????struct?tcphdr???*th;??
  13. ?????????????????struct?udphdr???*uh;??
  14. ?????????????????struct?icmphdr??*icmph;??
  15. ?????????????????struct?igmphdr??*igmph;??
  16. ?????????????????struct?iphdr????*ipiph;??
  17. ?????????????????struct?ipv6hdr??*ipv6h;??
  18. ?????????????????unsigned?char???*raw;??
  19. ?????????}?h;??
  20. ???
  21. ?????????union?{??
  22. ?????????????????struct?iphdr????*iph;??
  23. ?????????????????struct?ipv6hdr??*ipv6h;??
  24. ?????????????????struct?arphdr???*arph;??
  25. ?????????????????unsigned?char???*raw;??
  26. ?????????}?nh;??
  27. ???
  28. ?????????union?{??
  29. ?????????????????unsigned?char???*raw;??
  30. ?????????}?mac;??
  31. ???
  32. ?????????struct??dst_entry???????*dst;??
  33. ?????????struct??sec_path????????*sp;??
  34. ???
  35. ?????????/*?
  36. ??????????*?This?is?the?control?buffer.?It?is?free?to?use?for?every?
  37. ??????????*?layer.?Please?put?your?private?variables?there.?If?you?
  38. ??????????*?want?to?keep?them?across?layers?you?have?to?do?a?skb_clone()?
  39. ??????????*?first.?This?is?owned?by?whoever?has?the?skb?queued?ATM.?
  40. ??????????*/??
  41. ?????????char????????????????????cb[48];??
  42. ???
  43. ?????????unsigned?int????????????len,??
  44. ?????????????????????????????????data_len,??
  45. ?????????????????????????????????mac_len,??
  46. ?????????????????????????????????csum;??
  47. ?????????__u32???????????????????priority;??
  48. ?????????__u8????????????????????local_df:1,??
  49. ?????????????????????????????????cloned:1,??
  50. ?????????????????????????????????ip_summed:2,??
  51. ?????????????????????????????????nohdr:1,??
  52. ?????????????????????????????????nfctinfo:3;??
  53. ?????????__u8????????????????????pkt_type:3,??
  54. ?????????????????????????????????fclone:2,??
  55. ?????????????????????????????????ipvs_property:1;??
  56. ?????????__be16??????????????????protocol;??
  57. ???
  58. ?????????void????????????????????(*destructor)(struct?sk_buff?*skb);??
  59. #ifdef?CONFIG_NETFILTER??
  60. ?????????__u32???????????????????nfmark;??
  61. ?????????struct?nf_conntrack?????*nfct;??
  62. #if?defined(CONFIG_NF_CONNTRACK)?||?defined(CONFIG_NF_CONNTRACK_MODULE)??
  63. ?????????struct?sk_buff??????????*nfct_reasm;??
  64. #endif??
  65. #ifdef?CONFIG_BRIDGE_NETFILTER??
  66. ?????????struct?nf_bridge_info???*nf_bridge;??
  67. #endif??
  68. #endif?/*?CONFIG_NETFILTER?*/??
  69. #ifdef?CONFIG_NET_SCHED??
  70. ?????????__u16???????????????????tc_index;???????/*?traffic?control?index?*/??
  71. #ifdef?CONFIG_NET_CLS_ACT??
  72. ?????????__u16???????????????????tc_verd;????????/*?traffic?control?verdict?*/??
  73. #endif??
  74. #endif??
  75. ???
  76. ???
  77. ?????????/*?These?elements?must?be?at?the?end,?see?alloc_skb()?for?details.??*/??
  78. ?????????unsigned?int????????????truesize;??
  79. ?????????atomic_t????????????????users;??
  80. ?????????unsigned?char???????????*head,??
  81. ?????????????????????????????????*data,??
  82. ?????????????????????????????????*tail,??
  83. ?????????????????????????????????*end;??
  84. };??

> : next和prev,這兩個域是用來連接相關的skb的(例如如果有分片,將這些分片連接在一起可以)

> : sk,指向報文所屬的套接字指針

> : tstamp,記錄接收或者傳輸報文的時間戳

> : dev和input_dev,記錄接收或者發送的設備

>: union u,對于一個層次,例如tcp層,可能有很多不同的協議,他們的協議頭不一樣,那么這個聯合體就是記錄這些協議頭的。

? ? ?此處u就是代表傳輸層

> : union nh,代表網絡層頭

> : union mac,代表鏈路層頭

> : dst,指向des_entry結構,記錄了到達目的地的路由信息,以及其他的一些網絡特征信息。

> : sp:安全路徑,用于xfrm

> : cb[],保存與協議相關的控制信息,每個協議可能獨立使用這些信息。

> :?重要的字段 len 和 data_len:

? ? ? len代: 表整個數據區域的長度!這里要提前解釋幾個定義,skb的組成是有sk_buff控制 + 線性數據 + 非線性數據?

? ? ? (skb_shared_info)?組成!

? ? ?后面會具體解釋是什么意思!在sk_buff這個里面沒有實際的數據,這里僅僅是控制信息,數據是通過后面的data指針指向其他內

? ? ?存塊的!那個內存塊中是線性數據和

? ? ?非線性數據!那么len就是length(線性數據) + length(非線性數據)!!!

? ? ?data_len: 指的是length(非線性數據)!!!那么可以知道:length(線性數據) = ?skb->len - skb->data_len

> : mac_len,指的是mac頭長度

> : csum,某時刻協議的校驗和

> : priority,報文排隊優先級,取決于ip中的tos域

> : local_df,允許在本地分配

> : cloned,保存當前的skb_buff是克隆的還是原始數據

> : ip_summed,是否計算ip校驗和

> : nohdr,僅僅引用數據區域

> :?pkt_type,報文類型,例如廣播,多播,回環,本機,傳出...

> : fclone,skb_buff克隆狀態

> :?ipvs_property,skb_buff是否屬于ipvs

> : protocal,協議信息

> :?nfmark,用于鉤子之間通信

> :?nfct_reasm,netfilter的跟蹤連接重新組裝指針

> :?nf_bridge,保存橋接信息

> : tc_index: Traffic control index,tc_verd: traffic control verdict

> : truesize,該緩沖區分配的所有總的內存,包括:skb_buff + 所有數據大小

> : users,保存引用skb_buff的數量

> : 重要數據字段:head,data,tail,end!!!

? ? head:指向分配給的線性數據內存首地址( 建立起一個觀念:并不是分配這么多內存,就都能被使用作為數據存儲,可能沒這么多

? ? 數據也有可能!但是也不要認為分配這么多 就足夠了,也不一定(非線性數據就是例子) )

? ? data:指向保存數據內容的首地址!我們由head可以知道,head和data不一定就是指在同一個位置!!!

? ? tail:指向數據的結尾!

? ? end:指向分配的內存塊的結尾! ( 由上面我們知道數據結尾 != 分配的內存塊的結尾 )

? ? 下面還會具體分析!!!!!!!!!!!


二、

我覺得需要先了解一些對于一個數據skb到底有什么,或者說由哪些元素組成!這就需要知道所謂的 “線性數據” 和 “非線性數據”。

基本的組成如下:

> : sk_buff : 這是一個sk_buff的控制結構

> : 線性數據區域

> : 非線性數據區域( 由skb_shared_info結構體管理 )


那么下面通過一個圖來看看這個skb結構到底是怎么樣的!看(圖一)


? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ????(圖一)

借助圖一,我們先來分析兩個重要字段:len和data_len

之前說過len代表的是整個數據的長度,data_len代表的是非線性數據長度。我們由圖一可以看到線性數據長度為l1,再看看非線性數據,其實就是看frags[]和frag_list

ok...那么我們可以知道非線性數據長度為( l2 + ... + ln ) + ( l(n+1) + ... + lm )

即:len = l1 +?( l2 + ... + ln ) + ( l(n+1) + ... + lm )

? ? ? ? data_len =?( l2 + ... + ln ) + ( l(n+1) + ... + lm )


ok...


現在從分配內存開始解釋這個圖的由來:

我們使用skb_alloc給skb分配空間,那么剛剛分配結束返回時候,是什么樣的情況呢?看下圖(圖二):

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? (圖二)


剛剛開始初始化的時候,預分配一個一塊線性數據區域,這個區域一般放入的是各個協議層次的不同的頭,還有一些實際數據,下面的非線性區域是為了彌補當數據真的很多的時候,作為數據區域的擴展!關于skb_shared_info具體意思下面會繼續說!注意在初始化的時候,head,data和tail都指向內存的開始位置,head在這個位置始終不變,它表示的是分配的內存的開始位置。end的位置也是不變的,表示的是分配的內存的結束位置。data和tail會隨著數據的加入和減少變化,總之表示的是放入數據的內存區域(由圖一)可知。


現在需要解釋一下skb_shared_info這個結構體,這個結構體真的是很很有特色!主要是其中的兩個字段frags和frag_list,下面繼續解釋:

[cpp]?view plain?copy
?print?
  1. struct?skb_shared_info?{??
  2. ?????????atomic_t????????dataref;????????//?對象被引用次數??
  3. ?????????unsigned?short??nr_frags;???????//?分頁段數目,即frags數組元素個數??
  4. ?????????unsigned?short??tso_size;?????????
  5. ?????????unsigned?short??tso_segs;??
  6. ?????????unsigned?short??ufo_size;??
  7. ?????????unsigned?int????ip6_frag_id;??
  8. ?????????struct?sk_buff??*frag_list;????//?一般用于分段(還沒有非常清楚的理解)??
  9. ?????????skb_frag_t??????frags[MAX_SKB_FRAGS];?//?保存分頁數據(skb->data_len=所有的數組數據長度之和)??
  10. };??

關于frags和frag_list沒有必然的聯系!


> : 對于frags[]一般用在,當數據真的很多,而且在線性數據區域裝不下的時候,需要使用這個,skb_frag_t中是一頁一頁的數據,先看看結構體:

[cpp]?view plain?copy
?print?
  1. struct?skb_frag_struct?{??
  2. ?????????struct?page?*page;????//?代表一頁數據??
  3. ?????????__u16?page_offset;????//?代表相對開始位置的頁偏移量??
  4. ?????????__u16?size;???????????//?page中數據長度??
  5. };??

需要注意的是:只有在DMA支持物理分散頁的Scatter/Gather(SG,分散/聚集)操作時候才可以使用frags[]來保存剩下的數據,否則,只能擴展線性數據區域進行保存!!!

這些頁其實是其實就是虛擬頁映射到物理頁的結構,看下圖(圖三):


? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? (圖三)


> : 對于frag_list來說,一般我們在分片的時候里面裝入每個片的信息,注意,每個片最終也都是被封裝成一個小的skb,這個必須

? ? ?的!

? ? ?注意:具體怎么分片的看上一篇博文:數據分片?( ?看其中的ip_fragment函數??)

? ? ?那么看一下其基本結構如圖四:

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? (圖四)


三、

最重要的是需要理解對于這個skb是怎么操作的,在操作的過程中,每一塊的內存分配是怎么變化的,這才更重要!

看下面的函數們:


> : alloc_skb()函數

[cpp]?view plain?copy
?print?
  1. static?inline?struct?sk_buff?*alloc_skb(unsigned?int?size,??
  2. ?????????????????????????????????????????gfp_t?priority)??
  3. {??
  4. ?????????return?__alloc_skb(size,?priority,?0);??
  5. }??


其實看__alloc_skb函數:

[cpp]?view plain?copy
?print?
  1. struct?sk_buff?*__alloc_skb(unsigned?int?size,?gfp_t?gfp_mask,??
  2. ?????????????????????????????int?fclone)??
  3. {??
  4. ?????????kmem_cache_t?*cache;??
  5. ?????????struct?skb_shared_info?*shinfo;??
  6. ?????????struct?sk_buff?*skb;??
  7. ?????????u8?*data;??
  8. ???
  9. ?????????cache?=?fclone???skbuff_fclone_cache?:?skbuff_head_cache;????//?根據克隆狀態來判斷在哪一個緩沖區進行分配cache??
  10. ???
  11. ?????????/*?Get?the?HEAD?*/??
  12. ?????????skb?=?kmem_cache_alloc(cache,?gfp_mask?&?~__GFP_DMA);????????//?得到skb,注意這里沒有包含數據,僅僅是skb_buff這個結構體??
  13. ?????????if?(!skb)??
  14. ?????????????????goto?out;??
  15. ???
  16. ?????????/*?Get?the?DATA.?Size?must?match?skb_add_mtu().?*/??
  17. ?????????size?=?SKB_DATA_ALIGN(size);?????????????????????????????????????//?獲得線性數據分片長度(注意對齊)??
  18. ?????????data?=?kmalloc(size?+?sizeof(struct?skb_shared_info),?gfp_mask);?//?注意分配的是什么,是size?+?skb_shared_info!!!!!??
  19. ?????????if?(!data)??
  20. ?????????????????goto?nodata;??
  21. ???
  22. ?????????memset(skb,?0,?offsetof(struct?sk_buff,?truesize));??????????//?初始化??
  23. ?????????skb->truesize?=?size?+?sizeof(struct?sk_buff);???????????????//?實際大小等于sk_buff?+?size,剛剛開始還沒有非線性數據??
  24. ?????????atomic_set(&skb->users,?1);????????????????????????????????????
  25. ?????????skb->head?=?data;????????????????????????????????????????????//?注意指針,這個結合上面的圖一清二楚??
  26. ?????????skb->data?=?data;??
  27. ?????????skb->tail?=?data;??
  28. ?????????skb->end??=?data?+?size;??
  29. ?????????/*?make?sure?we?initialize?shinfo?sequentially?*/??
  30. ?????????shinfo?=?skb_shinfo(skb);??
  31. ?????????atomic_set(&shinfo->dataref,?1);??
  32. ?????????shinfo->nr_frags??=?0;??
  33. ?????????shinfo->tso_size?=?0;??
  34. ?????????shinfo->tso_segs?=?0;??
  35. ?????????shinfo->ufo_size?=?0;??
  36. ?????????shinfo->ip6_frag_id?=?0;??
  37. ?????????shinfo->frag_list?=?NULL;??
  38. ???
  39. ?????????if?(fclone)?{??
  40. ?????????????????struct?sk_buff?*child?=?skb?+?1;??
  41. ?????????????????atomic_t?*fclone_ref?=?(atomic_t?*)?(child?+?1);??
  42. ???
  43. ?????????????????skb->fclone?=?SKB_FCLONE_ORIG;??
  44. ?????????????????atomic_set(fclone_ref,?1);??
  45. ???
  46. ?????????????????child->fclone?=?SKB_FCLONE_UNAVAILABLE;??
  47. ?????????}??
  48. out:??
  49. ?????????return?skb;??
  50. nodata:??
  51. ?????????kmem_cache_free(cache,?skb);??
  52. ?????????skb?=?NULL;??
  53. ?????????goto?out;??
  54. }??


那么alloc之后的圖就是(圖五):

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ?(圖五)

其實和圖二是一樣的!我們可以看到,現在僅僅是分配了線束數據區域,但是現在還沒有數據!一定要注意!所以前面三個指針指在一起!因為沒有數據,那么len和data_len的值就是0 !


> : skb_reserve函數

[cpp]?view plain?copy
?print?
  1. static?inline?void?skb_reserve(struct?sk_buff?*skb,?int?len)??
  2. {??
  3. ?????????skb->data?+=?len;??
  4. ?????????skb->tail?+=?len;??
  5. ?}??


代碼其實很easy、就是移動兩個指針而已~


這個函數很重要,是為“協議頭”預留空間!而且是盡最大的空間預留,因為很多頭都會有可選項,那么我們不知道可選項是多大,所以只能是按照最大的分配,那么也說明了一點,預留的空間headroom也就是不一定都能使用完的!可能還有剩余的,由上面的圖也可以看出來!這也是為什么需要這么多指針的問題!那么這個函數直接導致head指針和tail、data指針分離,入下面圖六所示:

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? (圖六)


注意headroom就是用來存儲各個協議頭的足夠大的空間,tailroom就可以認為是存儲其他線性數據的空間。( 這里不要曲解協議頭不是線性數據,其實協議頭也是!!!所以當增加頭的時候,data指針向上移動,當增加其他數據的時候,tail指針向下移動 )。現在data和tail指向一起,那么還是說明數據沒有!!!


> : skb_put函數 ----> 用于操作線性數據區域(tailroom區域)的用戶數據

[cpp]?view plain?copy
?print?
  1. static?inline?unsigned?char?*skb_put(struct?sk_buff?*skb,?unsigned?int?len)??
  2. {??
  3. ?????????unsigned?char?*tmp?=?skb->tail;??
  4. ?????????SKB_LINEAR_ASSERT(skb);????????????
  5. ?????????skb->tail?+=?len;?????????????????//?移動指針??
  6. ?????????skb->len??+=?len;?????????????????//?數據空間增大len??
  7. ?????????if?(unlikely(skb->tail>skb->end))?//?如果tail指針超過end指針了,那么處理錯誤~??
  8. ?????????????????skb_over_panic(skb,?len,?current_text_addr());??
  9. ?????????return?tmp;??
  10. }??


這函數其實就是從tailroom預留空間,相當于是移動tail指針,這樣如果從上圖(圖六)開始看,也就是tail開始向下移動,和data分離了。。。一般來說,這樣做都是為了用戶數據再次處理,或者說為TCP/IP的負載預留空間!

看圖七,當使用skb_put時候,由圖六---->圖七

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ?(圖七)

我們可以看到指針的移動data還是在headroom的下面,中間的是用戶數據預留的部分,由skb_put得到,tail表示數據結尾!再看一下sk_buff中的len,變成了數據長度ld!!


> : skb_push函數:----------> 用于操作headroom區域的協議頭

[cpp]?view plain?copy
?print?
  1. static?inline?unsigned?char?*skb_push(struct?sk_buff?*skb,?unsigned?int?len)??
  2. {??
  3. ?????????skb->data?-=?len;??????//?向上移動指針??
  4. ?????????skb->len??+=?len;??????//?數據長度增加??
  5. ?????????if?(unlikely(skb->data<skb->head))??//?data指針超過head那么就是處理錯誤~??
  6. ?????????????????skb_under_panic(skb,?len,?current_text_addr());??
  7. ?????????return?skb->data;??
  8. }??

和skb_put對應,上面試操作用戶數據的,這里是操作協議頭的!其實就是data指針向上移動而已~注意len增大了哦~前面說了協議頭也是屬于數據!

如下面圖所示,由圖七---->圖八

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ?(圖八)


我們可以知道,data向上移動了,同時注意len變成ld+lp了,其中lp是這個增加的協議頭的長度!


> : skb_pull函數:-----------> 其實這個函數才是與skb_push函數對應的函數!因為這是去頭函數,而skb_push是增頭函數!所以這個函數一般用在解包的時候!

[cpp]?view plain?copy
?print?
  1. static?inline?unsigned?char?*skb_pull(struct?sk_buff?*skb,?unsigned?int?len)??
  2. {??
  3. ?????????return?unlikely(len?>?skb->len)???NULL?:?__skb_pull(skb,?len);??
  4. }??
  5. ???
  6. ???
  7. static?inline?unsigned?char?*__pskb_pull(struct?sk_buff?*skb,?unsigned?int?len)??
  8. {??
  9. ?????????if?(len?>?skb_headlen(skb)?&&??
  10. ?????????????!__pskb_pull_tail(skb,?len-skb_headlen(skb)))??
  11. ?????????????????return?NULL;??
  12. ?????????skb->len?-=?len;??????????????????????????????//?長度減小??
  13. ?????????return?skb->data?+=?len;??????????????????????//?移動指針??
  14. }??

其實就是data指針向下移動,當前一個協議頭被去掉,headroom剩余的空間增大了!看下圖:

由圖八---->圖九:

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??(圖九)


虛線是data之前的指針位置,現在移動到下面實線!!需注意:len的長度減小,減小的大小是剝去的頭的大小!!


四、

最后我們從兩條線整體分析一下:

1:從應用層用戶數據開始,直到物理層發送出去

? ? ? > 初始化的什么就不多說了,和前面的差不多,現在也加入用戶數據已經在了,如圖七所示一樣!那么到了TCP層,需要增加

? ? ? ? ?TCP層的頭:

? ? ? ? ?如圖10所示:

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ?(圖10)


? ? ? ??? ? 需要注意的是這里是傳輸層,那么傳輸層的結構u中的th代表的是tcp的頭,那么tcp指向tcp頭OK!同時注意 len長度+=l1 哦~~~

? ? ? ? > 再看到了IP層:如圖11

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ???(圖11)


??? ? ? ? ? ? ? 至于需要解釋什么就沒什么了,都是一樣的~

? ? ? ? ? ? ?> 到鏈路層了:如圖12

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? (圖12)


??OK!


2:第二個過程其實是第一個逆過程,都差不多,所以不多說了~


五、

最后看一下操作skb的兩個函數pskb_copy和skb_copy

前者僅僅是將sk_buff的結構體和線性數據copy過來,對于非線性數據,是引用原始的skb的數據的!而后者是不僅將sk_buff和線性數據拷貝,同時將非線性數據也copy了一份,看下面就明白了!這就在效率上就差了很多!所以如果不想修改數據,那么還是使用pskb_copy更好!


對于pskb_copy:



對于skb_copy:



OK ?我覺得差不多了~~~~~結束~~~~~~~~~~~~~?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/385010.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/385010.shtml
英文地址,請注明出處:http://en.pswp.cn/news/385010.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

可變參數輸出(三)

Linux C關于輸出函數的定義&#xff1a; int printf(const char *format,…); int vprintf(const char * format,va_list ap); int vfprintf(FILE *stream,cosnt char *format,va_list ap); int vsprintf(char *str,const char *format,va_list ap); int vsnprintf(char *str,s…

最常用的設計模式---適配器模式(C++實現)

適配器模式屬于結構型的設計模式&#xff0c;它是結構型設計模式之首&#xff08;用的最多的結構型設計模式&#xff09;。 適配器設計模式也并不復雜&#xff0c;適配器它是主要作用是將一個類的接口轉換成客戶希望的另外一個接口這樣使得原本由于接口不兼容而不能一起工作的那…

Linux 簡單打印日志(二)

#include<stdio.h> #include<stdlib.h> #include<string.h> #include<time.h> //#include<windows.h> #include <unistd.h> // linux下頭文件#define FILE_MAX_SIZE (1024*1024)void get_local_time(char* buffer){time_t rawtime;struct …

程序隨筆——C++實現的一個線程池

1.線程池簡介 我們知道在線程池是一種多線程處理形式&#xff0c;處理過程中我們將相應的任務提交給線程池&#xff0c;線程池會分配對應的工作線程執行任務或存放在任務隊列中&#xff0c;等待執行。 面向對象編程中&#xff0c;創建和銷毀對象是需要消耗一定時間的&#xff0…

線程池原理及創建并C++實現

本文給出了一個通用的線程池框架&#xff0c;該框架將與線程執行相關的任務進行了高層次的抽象&#xff0c;使之與具體的執行任務無關。另外該線程池具有動態伸縮性&#xff0c;它能根據執行任務的輕重自動調整線程池中線程的數量。文章的最后&#xff0c;我們給出一個簡單示例…

Linux 打印簡單日志(一)

簡單日志輸出&#xff1a; #include<stdio.h> #include<string.h> #include<stdlib.h>void write(char* filename,char* szStr){FILE* fp;fp fopen(filename,"at");if(fp ! NULL){fwrite(szStr,256,1,fp); //fclose(fp);fp NULL;} }int main(int…

c++簡單線程池實現

線程池&#xff0c;簡單來說就是有一堆已經創建好的線程&#xff08;最大數目一定&#xff09;&#xff0c;初始時他們都處于空閑狀態&#xff0c;當有新的任務進來&#xff0c;從線程池中取出一個空閑的線程處理任務&#xff0c;然后當任務處理完成之后&#xff0c;該線程被重…

Linux 打印可變參數日志

實現了傳輸進去的字符串所在的文檔&#xff0c;函數和行數顯示功能。 實現了將傳入的可變參數打印到日志功能。 #include<stdio.h> #include<stdarg.h> #include<string.h>const char * g_path "/home/exbot/wangqinghe/log.txt"; #define LOG(fm…

C++強化之路之線程池開發整體框架(二)

一.線程池開發框架 我所開發的線程池由以下幾部分組成&#xff1a; 1.工作中的線程。也就是線程池中的線程&#xff0c;主要是執行分發來的task。 2.管理線程池的監督線程。這個線程的創建獨立于線程池的創建&#xff0c;按照既定的管理方法進行管理線程池中的所有線程&#xf…

vfprintf()函數

函數聲明&#xff1a;int vfprintf(FILE *stream, const char *format, va_list arg) 函數參數&#xff1a; stream—這是指向了FILE對象的指針&#xff0c;該FILE對象標識了流。 format—c語言字符串&#xff0c;包含了要被寫入到流stream中的文本。它可以包含嵌入的format標簽…

Makefile(二)

將生產的.o文件放進指定的文件中&#xff08;先創建該文件夾&#xff09; src $(wildcard ./*.cpp) obj $(patsubst %.cpp,./output/%.o,$(src))target test$(target) : $(obj)g $(obj) -o $(target) %.o: %.cppg -c $< -o output/$.PHONY:clean clean:rm -f $(target) $…

TCP粘包問題分析和解決(全)

TCP通信粘包問題分析和解決&#xff08;全&#xff09;在socket網絡程序中&#xff0c;TCP和UDP分別是面向連接和非面向連接的。因此TCP的socket編程&#xff0c;收發兩端&#xff08;客戶端和服務器端&#xff09;都要有成對的socket&#xff0c;因此&#xff0c;發送端為了將…

UML類圖符號 各種關系說明以及舉例

UML中描述對象和類之間相互關系的方式包括&#xff1a;依賴&#xff0c;關聯&#xff0c;聚合&#xff0c;組合&#xff0c;泛化&#xff0c;實現等。表示關系的強弱&#xff1a;組合>聚合>關聯>依賴 相互間關系 聚合是表明對象之間的整體與部分關系的關聯&#xff0c…

尋找數組中第二大數

設置兩個數值來表示最大數和第二大數&#xff0c;在循環比較賦值即可 //找給定數組中第二大的數int get_smax(int *arr,int length) {int max;int smax;if(arr[0] > arr[1]){max arr[0];smax arr[1];}else{max arr[1];smax arr[0];}for(int i 2; i < length; i){if(…

timerfd API使用總結

timerfd 介紹 timerfd 是在Linux內核2.6.25版本中添加的接口&#xff0c;其是Linux為用戶提供的一個定時器接口。這個接口基于文件描述符&#xff0c;所以可以被用于select/poll/epoll的場景。當使用timerfd API創建多個定時器任務并置于poll中進行事件監聽&#xff0c;當沒有可…

#if/#else/#endif

在linux環境下寫c代碼時會嘗試各種方法或調整路徑&#xff0c;需要用到#if #include<stdio.h>int main(){int i; #if 0i 1; #elsei 2; #endifprintf("i %d",i);return 0; } 有時候會調整代碼&#xff0c;但是又不是最終版本的更換某些值&#xff0c;就需要注…

內存分配調用

通過函數給實參分配內存&#xff0c;可以通過二級指針實現 #include<stdio.h> #incldue<stdlib.h>void getheap(int *p) //錯誤的模型 {p malloc(100); }void getheap(int **p) //正確的模型 {*p malloc(100); } int main() {int *p NULL;getheap(&p);free(p…

ESP傳輸模式拆解包流程

一、 ESP簡介ESP&#xff0c;封裝安全載荷協議(Encapsulating SecurityPayloads)&#xff0c;是一種Ipsec協議&#xff0c;用于對IP協議在傳輸過程中進行數據完整性度量、來源認證、加密以及防回放攻擊。可以單獨使用&#xff0c;也可以和AH一起使用。在ESP頭部之前的IPV4…

結構體成員內存對齊

#include<stdio.h> struct A {int A; };int main() {struct A a;printf("%d\n",sizeof(a));return 0; } 運行結果&#xff1a;4 #include<stdio.h> struct A {int a;int b&#xff1b; };int main() {struct A a;printf("%d\n",sizeof(a))…

C庫函數-fgets()

函數聲明&#xff1a;char *fgets(char *str,int n,FILE *stream) 函數介紹&#xff1a;從指定的stream流中讀取一行&#xff0c;并把它存儲在str所指向的字符串中。當讀取到&#xff08;n-1&#xff09;個字符時&#xff0c;獲取讀取到換行符時&#xff0c;或者到達文件末尾時…