Linux 内核网络协议栈 ------sk_buff 结构体 以及 完全解释 (2.6.16)
在2.6.24之后這個(gè)結(jié)構(gòu)體有了較大的變化,此處先說(shuō)一說(shuō)2.6.16版本的sk_buff,以及解釋一些問(wèn)題。 一、 先直觀的看一下這個(gè)結(jié)構(gòu)體~~~~~~~~~~~~~~~~~~~~~~在下面解釋每個(gè)字段的意義~~~~~~~~~~~ [cpp]?view plain?copy?print?> : next和prev,這兩個(gè)域是用來(lái)連接相關(guān)的skb的(例如如果有分片,將這些分片連接在一起可以) > : sk,指向報(bào)文所屬的套接字指針 > : tstamp,記錄接收或者傳輸報(bào)文的時(shí)間戳 > : dev和input_dev,記錄接收或者發(fā)送的設(shè)備 >: union u,對(duì)于一個(gè)層次,例如tcp層,可能有很多不同的協(xié)議,他們的協(xié)議頭不一樣,那么這個(gè)聯(lián)合體就是記錄這些協(xié)議頭的。 ? ? ?此處u就是代表傳輸層 > : union nh,代表網(wǎng)絡(luò)層頭 > : union mac,代表鏈路層頭 > : dst,指向des_entry結(jié)構(gòu),記錄了到達(dá)目的地的路由信息,以及其他的一些網(wǎng)絡(luò)特征信息。 > : sp:安全路徑,用于xfrm > : cb[],保存與協(xié)議相關(guān)的控制信息,每個(gè)協(xié)議可能獨(dú)立使用這些信息。 > :?重要的字段 len 和 data_len: ? ? ? len代: 表整個(gè)數(shù)據(jù)區(qū)域的長(zhǎng)度!這里要提前解釋幾個(gè)定義,skb的組成是有sk_buff控制 + 線性數(shù)據(jù) + 非線性數(shù)據(jù)? ? ? ? (skb_shared_info)?組成! ? ? ?后面會(huì)具體解釋是什么意思!在sk_buff這個(gè)里面沒(méi)有實(shí)際的數(shù)據(jù),這里僅僅是控制信息,數(shù)據(jù)是通過(guò)后面的data指針指向其他內(nèi) ? ? ?存塊的!那個(gè)內(nèi)存塊中是線性數(shù)據(jù)和 ? ? ?非線性數(shù)據(jù)!那么len就是length(線性數(shù)據(jù)) + length(非線性數(shù)據(jù))!!! ? ? ?data_len: 指的是length(非線性數(shù)據(jù))!!!那么可以知道:length(線性數(shù)據(jù)) = ?skb->len - skb->data_len > : mac_len,指的是mac頭長(zhǎng)度 > : csum,某時(shí)刻協(xié)議的校驗(yàn)和 > : priority,報(bào)文排隊(duì)優(yōu)先級(jí),取決于ip中的tos域 > : local_df,允許在本地分配 > : cloned,保存當(dāng)前的skb_buff是克隆的還是原始數(shù)據(jù) > : ip_summed,是否計(jì)算ip校驗(yàn)和 > : nohdr,僅僅引用數(shù)據(jù)區(qū)域 > :?pkt_type,報(bào)文類型,例如廣播,多播,回環(huán),本機(jī),傳出... > : fclone,skb_buff克隆狀態(tài) > :?ipvs_property,skb_buff是否屬于ipvs > : protocal,協(xié)議信息 > :?nfmark,用于鉤子之間通信 > :?nfct_reasm,netfilter的跟蹤連接重新組裝指針 > :?nf_bridge,保存橋接信息 > : tc_index: Traffic control index,tc_verd: traffic control verdict > : truesize,該緩沖區(qū)分配的所有總的內(nèi)存,包括:skb_buff + 所有數(shù)據(jù)大小 > : users,保存引用skb_buff的數(shù)量 > : 重要數(shù)據(jù)字段:head,data,tail,end!!! ? ? head:指向分配給的線性數(shù)據(jù)內(nèi)存首地址( 建立起一個(gè)觀念:并不是分配這么多內(nèi)存,就都能被使用作為數(shù)據(jù)存儲(chǔ),可能沒(méi)這么多 ? ? 數(shù)據(jù)也有可能!但是也不要認(rèn)為分配這么多 就足夠了,也不一定(非線性數(shù)據(jù)就是例子) ) ? ? data:指向保存數(shù)據(jù)內(nèi)容的首地址!我們由head可以知道,head和data不一定就是指在同一個(gè)位置!!! ? ? tail:指向數(shù)據(jù)的結(jié)尾! ? ? end:指向分配的內(nèi)存塊的結(jié)尾! ( 由上面我們知道數(shù)據(jù)結(jié)尾 != 分配的內(nèi)存塊的結(jié)尾 ) ? ? 下面還會(huì)具體分析!!!!!!!!!!! 二、 我覺(jué)得需要先了解一些對(duì)于一個(gè)數(shù)據(jù)skb到底有什么,或者說(shuō)由哪些元素組成!這就需要知道所謂的 “線性數(shù)據(jù)” 和 “非線性數(shù)據(jù)”。 基本的組成如下: > : sk_buff : 這是一個(gè)sk_buff的控制結(jié)構(gòu) > : 線性數(shù)據(jù)區(qū)域 > : 非線性數(shù)據(jù)區(qū)域( 由skb_shared_info結(jié)構(gòu)體管理 ) 那么下面通過(guò)一個(gè)圖來(lái)看看這個(gè)skb結(jié)構(gòu)到底是怎么樣的!看(圖一) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ????(圖一) 借助圖一,我們先來(lái)分析兩個(gè)重要字段:len和data_len! 之前說(shuō)過(guò)len代表的是整個(gè)數(shù)據(jù)的長(zhǎng)度,data_len代表的是非線性數(shù)據(jù)長(zhǎng)度。我們由圖一可以看到線性數(shù)據(jù)長(zhǎng)度為l1,再看看非線性數(shù)據(jù),其實(shí)就是看frags[]和frag_list ok...那么我們可以知道非線性數(shù)據(jù)長(zhǎng)度為( l2 + ... + ln ) + ( l(n+1) + ... + lm ) 即:len = l1 +?( l2 + ... + ln ) + ( l(n+1) + ... + lm ) ? ? ? ? data_len =?( l2 + ... + ln ) + ( l(n+1) + ... + lm ) ok... 現(xiàn)在從分配內(nèi)存開(kāi)始解釋這個(gè)圖的由來(lái): 我們使用skb_alloc給skb分配空間,那么剛剛分配結(jié)束返回時(shí)候,是什么樣的情況呢?看下圖(圖二): ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? (圖二) 剛剛開(kāi)始初始化的時(shí)候,預(yù)分配一個(gè)一塊線性數(shù)據(jù)區(qū)域,這個(gè)區(qū)域一般放入的是各個(gè)協(xié)議層次的不同的頭,還有一些實(shí)際數(shù)據(jù),下面的非線性區(qū)域是為了彌補(bǔ)當(dāng)數(shù)據(jù)真的很多的時(shí)候,作為數(shù)據(jù)區(qū)域的擴(kuò)展!關(guān)于skb_shared_info具體意思下面會(huì)繼續(xù)說(shuō)!注意在初始化的時(shí)候,head,data和tail都指向內(nèi)存的開(kāi)始位置,head在這個(gè)位置始終不變,它表示的是分配的內(nèi)存的開(kāi)始位置。end的位置也是不變的,表示的是分配的內(nèi)存的結(jié)束位置。data和tail會(huì)隨著數(shù)據(jù)的加入和減少變化,總之表示的是放入數(shù)據(jù)的內(nèi)存區(qū)域(由圖一)可知。 現(xiàn)在需要解釋一下skb_shared_info這個(gè)結(jié)構(gòu)體,這個(gè)結(jié)構(gòu)體真的是很很有特色!主要是其中的兩個(gè)字段frags和frag_list,下面繼續(xù)解釋: [cpp]?view plain?copy?print?關(guān)于frags和frag_list沒(méi)有必然的聯(lián)系! > : 對(duì)于frags[]一般用在,當(dāng)數(shù)據(jù)真的很多,而且在線性數(shù)據(jù)區(qū)域裝不下的時(shí)候,需要使用這個(gè),skb_frag_t中是一頁(yè)一頁(yè)的數(shù)據(jù),先看看結(jié)構(gòu)體: [cpp]?view plain?copy?print?需要注意的是:只有在DMA支持物理分散頁(yè)的Scatter/Gather(SG,分散/聚集)操作時(shí)候才可以使用frags[]來(lái)保存剩下的數(shù)據(jù),否則,只能擴(kuò)展線性數(shù)據(jù)區(qū)域進(jìn)行保存!!! 這些頁(yè)其實(shí)是其實(shí)就是虛擬頁(yè)映射到物理頁(yè)的結(jié)構(gòu),看下圖(圖三): ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? (圖三) > : 對(duì)于frag_list來(lái)說(shuō),一般我們?cè)诜制臅r(shí)候里面裝入每個(gè)片的信息,注意,每個(gè)片最終也都是被封裝成一個(gè)小的skb,這個(gè)必須 ? ? ?的! ? ? ?注意:具體怎么分片的看上一篇博文:數(shù)據(jù)分片?( ?看其中的ip_fragment函數(shù)??) ? ? ?那么看一下其基本結(jié)構(gòu)如圖四: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? (圖四) 三、 最重要的是需要理解對(duì)于這個(gè)skb是怎么操作的,在操作的過(guò)程中,每一塊的內(nèi)存分配是怎么變化的,這才更重要! 看下面的函數(shù)們: > : alloc_skb()函數(shù) [cpp]?view plain?copy?print?那么alloc之后的圖就是(圖五): ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ?(圖五) 其實(shí)和圖二是一樣的!我們可以看到,現(xiàn)在僅僅是分配了線束數(shù)據(jù)區(qū)域,但是現(xiàn)在還沒(méi)有數(shù)據(jù)!一定要注意!所以前面三個(gè)指針指在一起!因?yàn)闆](méi)有數(shù)據(jù),那么len和data_len的值就是0 ! > : skb_reserve函數(shù) [cpp]?view plain?copy?print?這個(gè)函數(shù)很重要,是為“協(xié)議頭”預(yù)留空間!而且是盡最大的空間預(yù)留,因?yàn)楹芏囝^都會(huì)有可選項(xiàng),那么我們不知道可選項(xiàng)是多大,所以只能是按照最大的分配,那么也說(shuō)明了一點(diǎn),預(yù)留的空間headroom也就是不一定都能使用完的!可能還有剩余的,由上面的圖也可以看出來(lái)!這也是為什么需要這么多指針的問(wèn)題!那么這個(gè)函數(shù)直接導(dǎo)致head指針和tail、data指針?lè)蛛x,入下面圖六所示: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? (圖六) 注意headroom就是用來(lái)存儲(chǔ)各個(gè)協(xié)議頭的足夠大的空間,tailroom就可以認(rèn)為是存儲(chǔ)其他線性數(shù)據(jù)的空間。( 這里不要曲解協(xié)議頭不是線性數(shù)據(jù),其實(shí)協(xié)議頭也是!!!所以當(dāng)增加頭的時(shí)候,data指針向上移動(dòng),當(dāng)增加其他數(shù)據(jù)的時(shí)候,tail指針向下移動(dòng) )。現(xiàn)在data和tail指向一起,那么還是說(shuō)明數(shù)據(jù)沒(méi)有!!! > : skb_put函數(shù) ----> 用于操作線性數(shù)據(jù)區(qū)域(tailroom區(qū)域)的用戶數(shù)據(jù) [cpp]?view plain?copy?print?這函數(shù)其實(shí)就是從tailroom預(yù)留空間,相當(dāng)于是移動(dòng)tail指針,這樣如果從上圖(圖六)開(kāi)始看,也就是tail開(kāi)始向下移動(dòng),和data分離了。。。一般來(lái)說(shuō),這樣做都是為了用戶數(shù)據(jù)再次處理,或者說(shuō)為TCP/IP的負(fù)載預(yù)留空間! 看圖七,當(dāng)使用skb_put時(shí)候,由圖六---->圖七 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ?(圖七) > : skb_push函數(shù):----------> 用于操作headroom區(qū)域的協(xié)議頭 [cpp]?view plain?copy?print?和skb_put對(duì)應(yīng),上面試操作用戶數(shù)據(jù)的,這里是操作協(xié)議頭的!其實(shí)就是data指針向上移動(dòng)而已~注意len增大了哦~前面說(shuō)了協(xié)議頭也是屬于數(shù)據(jù)! 如下面圖所示,由圖七---->圖八 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ?(圖八) 我們可以知道,data向上移動(dòng)了,同時(shí)注意len變成ld+lp了,其中l(wèi)p是這個(gè)增加的協(xié)議頭的長(zhǎng)度! > : skb_pull函數(shù):-----------> 其實(shí)這個(gè)函數(shù)才是與skb_push函數(shù)對(duì)應(yīng)的函數(shù)!因?yàn)檫@是去頭函數(shù),而skb_push是增頭函數(shù)!所以這個(gè)函數(shù)一般用在解包的時(shí)候! [cpp]?view plain?copy?print?其實(shí)就是data指針向下移動(dòng),當(dāng)前一個(gè)協(xié)議頭被去掉,headroom剩余的空間增大了!看下圖: 由圖八---->圖九: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??(圖九) 虛線是data之前的指針位置,現(xiàn)在移動(dòng)到下面實(shí)線!!需注意:len的長(zhǎng)度減小,減小的大小是剝?nèi)サ念^的大小!! 四、 最后我們從兩條線整體分析一下: 1:從應(yīng)用層用戶數(shù)據(jù)開(kāi)始,直到物理層發(fā)送出去 ? ? ? > 初始化的什么就不多說(shuō)了,和前面的差不多,現(xiàn)在也加入用戶數(shù)據(jù)已經(jīng)在了,如圖七所示一樣!那么到了TCP層,需要增加 ? ? ? ? ?TCP層的頭: ? ? ? ? ?如圖10所示: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ?(圖10) ? ? ? ??? ? 需要注意的是這里是傳輸層,那么傳輸層的結(jié)構(gòu)u中的th代表的是tcp的頭,那么tcp指向tcp頭OK!同時(shí)注意 len長(zhǎng)度+=l1 哦~~~ ? ? ? ? > 再看到了IP層:如圖11 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ???(圖11) ??? ? ? ? ? ? ? 至于需要解釋什么就沒(méi)什么了,都是一樣的~ ? ? ? ? ? ? ?> 到鏈路層了:如圖12 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? (圖12) ??OK! 2:第二個(gè)過(guò)程其實(shí)是第一個(gè)逆過(guò)程,都差不多,所以不多說(shuō)了~ 五、 最后看一下操作skb的兩個(gè)函數(shù)pskb_copy和skb_copy 前者僅僅是將sk_buff的結(jié)構(gòu)體和線性數(shù)據(jù)copy過(guò)來(lái),對(duì)于非線性數(shù)據(jù),是引用原始的skb的數(shù)據(jù)的!而后者是不僅將sk_buff和線性數(shù)據(jù)拷貝,同時(shí)將非線性數(shù)據(jù)也copy了一份,看下面就明白了!這就在效率上就差了很多!所以如果不想修改數(shù)據(jù),那么還是使用pskb_copy更好! 對(duì)于pskb_copy: 對(duì)于skb_copy: |
總結(jié)
以上是生活随笔為你收集整理的Linux 内核网络协议栈 ------sk_buff 结构体 以及 完全解释 (2.6.16)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 韩剧安娜是翻拍吗
- 下一篇: 【linux 开发】定时器使用setit