LinuxTCPIP協(xié)議棧的關(guān)鍵數(shù)據(jù)結(jié)構(gòu)SocketBuffer_第1頁
LinuxTCPIP協(xié)議棧的關(guān)鍵數(shù)據(jù)結(jié)構(gòu)SocketBuffer_第2頁
LinuxTCPIP協(xié)議棧的關(guān)鍵數(shù)據(jù)結(jié)構(gòu)SocketBuffer_第3頁
LinuxTCPIP協(xié)議棧的關(guān)鍵數(shù)據(jù)結(jié)構(gòu)SocketBuffer_第4頁
LinuxTCPIP協(xié)議棧的關(guān)鍵數(shù)據(jù)結(jié)構(gòu)SocketBuffer_第5頁
已閱讀5頁,還剩19頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

1、linux tcp/ i p協(xié)議棧的關(guān)鍵數(shù)據(jù)結(jié)構(gòu)socket buffersk_buff結(jié)構(gòu)可能是linux網(wǎng)絡(luò)代碼屮最重要的數(shù)據(jù)結(jié)構(gòu),它表示接收或發(fā)送數(shù)據(jù)包 的包頭信息。它在v include/linux/skbuff.h中定義,并包含很多成員變量供網(wǎng)絡(luò)代碼中 的各了系統(tǒng)使用。這個結(jié)構(gòu)在linux內(nèi)核的發(fā)展過程中改動過很多次,或者是增加新的選項(xiàng),或者是重 新組織己存在的成員變量以使得成員變量的布局更加清晰。它的成員變量可以人致分為以下 幾類: layout 布局 general 通用 feature-specific 功能相關(guān) management functions 管理函數(shù)這個結(jié)構(gòu)被不同

2、的網(wǎng)絡(luò)層(mac或者其他二層鏈路協(xié)議,三層的ip,四層的tcp或 udp等)使用,并門其中的成員變量在結(jié)構(gòu)從一層向另一層傳遞時改變。l4向l3傳遞前 會添加一個l4的頭部,同樣,l3向l2傳遞前,會添加一個l3的頭部。添加頭部比在不 同層z間拷貝數(shù)據(jù)的效率更鬲。由于在緩沖區(qū)的頭部添加數(shù)據(jù)意味著耍修改指向緩沖區(qū)的指 針,這是個復(fù)雜的操作,所以內(nèi)核提供了一個函數(shù)skb_reserve (在后面的章節(jié)中描述) 來完成這個功能。協(xié)議棧中的每一層在往下一層傳遞緩沖區(qū)前,第一件事就是調(diào)用skb.re serve在緩沖區(qū)的頭部給協(xié)議頭預(yù)留一定的空間。skbeserve同樣被設(shè)備驅(qū)動使川來對齊接收到包的包頭。

3、如果緩沖區(qū)向上層協(xié)議傳 遞,ii的i辦議層的頭部信息就沒什么用了。例如,l2的頭部只有在網(wǎng)絡(luò)驅(qū)動處理l2的協(xié) 議時有用,l3是不會關(guān)心它的信息的。但是,內(nèi)核并沒有把l2的頭部從緩沖區(qū)中刪除, 而是把有效荷載的指針指向l3的頭部,這樣做,可以節(jié)省cpu時間。1. 網(wǎng)絡(luò)參數(shù)和內(nèi)核數(shù)據(jù)結(jié)構(gòu)就像你在瀏覽tcp/1p規(guī)范或者配置內(nèi)核時所看到的一樣,網(wǎng)絡(luò)代碼提供了很多有用 的功能,但是這些功能并不是必須的,比如說,防火墻,多播,還冇其他一些功能。人部分 的功能都需要在內(nèi)核數(shù)據(jù)結(jié)構(gòu)中添加自己的成員變量。因此,sk_buff里血包含了很多像# ifdef這樣的預(yù)編譯指令。例如,在sk_buff結(jié)構(gòu)的最后,你可

4、以找到:struct skbuff #ifdef config_net_schedu32 tc index;#ifdcf config net cls actu32tc verd;u32tc_classid;sendif#endif它表明,tjindex只有在編譯時定義了 config_net_sched符號才有效。這個 符號可以通過選擇特定的編譯選項(xiàng)來定義(例如:"device drivers networking suppo rtnetworking options qos and/or fair queueing")。這些編譯選項(xiàng)可以由管理員通 過make confi

5、g來選擇,或者通過一些白動安裝工具來選擇。前面的例了有兩個嵌套的選項(xiàng):config_net_cls_act (包分類器)只冇在選擇 支持“qos and/or fair queueing"時才能生效。順便提一下,qos選項(xiàng)不能被編譯成內(nèi)核模塊。原因就是,內(nèi)核編譯之后,由某個選 項(xiàng)所控制的數(shù)據(jù)結(jié)構(gòu)是不能動態(tài)變化的。一般來說,如果某個選項(xiàng)會修改內(nèi)核數(shù)據(jù)結(jié)構(gòu)(比 如說,在sk_buff 里面增加一個項(xiàng)tcjndex),那么,包含這個選項(xiàng)的組件就不能被編譯成內(nèi)核模塊。你可能經(jīng)常需要查找是哪個make config編譯選項(xiàng)或者變種定義了某個#ifdef標(biāo)記, 以便理解內(nèi)核中包含的某段代碼。在2

6、.6內(nèi)核屮,最快的,杏找它們之間關(guān)聯(lián)關(guān)系的方法, 就是查找分布在內(nèi)核源代碼樹中的kconfig文件中是否定義了相應(yīng)的符號(每個目錄都有 一個這樣的文件)。在2.4 內(nèi)核中,你需要查看 documentation/configure.help 文件。2. layout fields冇些sk.buff成員變量的作用是方便查找或者是連接數(shù)據(jù)結(jié)構(gòu)本身。內(nèi)核可以把sk_ buff組織成一個雙向鏈表。當(dāng)然,這個鏈表的結(jié)構(gòu)要比常見的雙向鏈表的結(jié)構(gòu)復(fù)雜一點(diǎn)。就像任何一個雙向鏈表-一樣,sk_buff中有兩個指針next和prev,其中,next指向 下一個節(jié)點(diǎn),而prev指向上一個節(jié)點(diǎn)。但是,這個鏈表還有另一

7、個需求:每個sk_buff結(jié)構(gòu)都必須能夠很 快找到鏈表頭節(jié)點(diǎn)。為了滿足這個需求,在第一個節(jié)點(diǎn)前面會插入另一個結(jié)構(gòu)sk_buff_he ad,這是一個輔助節(jié)點(diǎn),它的定義如下:struct skbuffhead /* these two members must be first. */ struct skbuff * nex t;_ _u32spinlock_tqlcn;lock;qlen代表鏈表元素的個數(shù)。lock用于防止對鏈表的并發(fā)訪問。sk_buff和sk_buff_head的前兩個元素是一樣的:next和prev指針。這使得它們 可以放到同一個鏈表中,盡管sk_buff_head要比sk

8、_buff小得多。另外,相同的函數(shù)可 以同樣應(yīng)用于sk buff和sk buff heado為了使這個數(shù)據(jù)結(jié)構(gòu)更靈活,每個sk_buff結(jié)構(gòu)都包含一個指向sk_buff_head的指 針。這個指針的名字是list。圖1會幫助你理解它們z間的關(guān)系。figure 1. list of sk_buff elementsanextprev qlen=4lock i1structbuff .headnextnextnextvnextprevtst"itstruct slbuffprev 瓦 "sfstruct slbuffprev jist skstruct slbuffprev 瓦

9、struct slbuff具他有趣的成員變量如f:struct sock * sk這是一個指向擁有這個sk.buff的sock結(jié)構(gòu)的指針。這個指針在網(wǎng)絡(luò)包由本機(jī)發(fā)出或者由 木機(jī)進(jìn)程接收時冇效,因?yàn)椴蹇谙嚓P(guān)的信息被l4(tcp或udp)或者用戶空間程序使川。如 果sk_buff只在轉(zhuǎn)發(fā)中使用(這意味著,源地址和冃的地址都不是本機(jī)地址),這個指針是n ullounsigned int len這是緩沖區(qū)中數(shù)據(jù)部分的長度。它包括主緩沖區(qū)中的數(shù)據(jù)長度(data指針指向它)和分片中 的數(shù)據(jù)長度。它的值在緩沖區(qū)從一個層向另一個層傳遞吋改變,因?yàn)橥蠈觽鬟f,舊的頭部 就沒有用了,而往下層傳遞,需要添加本層的頭

10、部。len同樣包含了協(xié)議頭的長度。unsigned int data_len和len不同,data_len只計(jì)算分片中數(shù)據(jù)的長度。unsigned int mac_len這是mac頭的長度。atomic_t users這是一個引用計(jì)數(shù),用于計(jì)算有多少實(shí)體引用了這個sk_buff緩沖區(qū)。它的主要用途是防 止釋放sk_buff后,還有其他實(shí)體引用這個sk_buffo因此,每個引用這個緩沖區(qū)的實(shí)體 都必須在適當(dāng)?shù)臅r候增加或減小這個變量。這個計(jì)數(shù)器只保護(hù)sk_buff結(jié)構(gòu)本少,而緩沖 區(qū)的數(shù)據(jù)部分由類似的計(jì)數(shù)器(dataref)來保護(hù).有時可以用atomic_inc和atomic_dec兩數(shù)來直接增加

11、或減小users,但是,通常還是 使用函數(shù)skb_get和kfree_skb來操作這個變量。unsigned int truesize這是緩沖區(qū)的總長度,包括sk_buff結(jié)構(gòu)和數(shù)據(jù)部分。如果申請一個len字節(jié)的緩沖區(qū),a lloc_skb 函數(shù)會把它初始化成 len+ sizeof(sk_buff)。struct sk buff *alloc_skb(unsigned int size, int gfp mask)skb>truesize 二 size + sizeof(struct sk buff);當(dāng)skb-> len變化時,這個變暈也會變化。unsigned char *

12、head unsigned char * end unsigned char * dataunsigned char * tail它們表示緩沖區(qū)和數(shù)據(jù)部分的邊界。在每一層申請緩沖區(qū)時,它會分配比協(xié)議頭或協(xié)議數(shù)據(jù) 大的空間。head和end指向緩沖區(qū)的頭部和尾部,而data和tail指向?qū)嶋H數(shù)據(jù)的頭部和 尾部,參見圖2o每一層會在head和data z間填充協(xié)議頭,或者在tail和end z間添加 新的協(xié)議數(shù)據(jù)。圖2屮右邊數(shù)據(jù)部分會在尾部包含一個附加的頭部。figure 2. head/end versus data/tail pointershead data tail endstruct s

13、k_buffvoid (*destructor)(.)這個函數(shù)指針可以初始化成一個在緩沖區(qū)釋放吋完成某些動作的函數(shù)。如呆緩沖區(qū)不屬于一 個socket,這個函數(shù)指針通常是不會被賦值的。如果緩沖區(qū)屬于一個socket,這個函數(shù) 指針會被賦值為 sock_rfree 或 sock_wfree(分別山 skb_set_owner或 skb_set_ow ner_w函數(shù)初始化)。這兩個sock_xxx函數(shù)用于更新socket的隊(duì)列中的內(nèi)存容量。3. general fields本節(jié)描述sk.buff的主要成員變量,這些成員變量與特定的內(nèi)核功能無關(guān):struct timeval stamp這個變量只對接

14、收到的包有意義。它代表包接收時的時間跖 或者有時代表包準(zhǔn)備發(fā)出時的 時間戳。它在netif_rx里面由函數(shù)net_timestamp設(shè)置,而netif_rx是設(shè)備驅(qū)動收到一 個包后調(diào)用的函數(shù)。struct net_device * dev這個變量的類型是net_device, net_device它代表一個網(wǎng)絡(luò)設(shè)備。dev的作用與這個包 是準(zhǔn)備發(fā)出的包還是剛接收的包冇關(guān)。當(dāng)收到一個包時,設(shè)備驅(qū)動會把sk_buff的dev指 針指向收到這個包的設(shè)備的數(shù)據(jù)結(jié)構(gòu),就像卜面的vortex.rx里的一段代碼所做的一樣, 這個函數(shù)屬于3c59x系列以太網(wǎng)卡驅(qū)動,川于接收一個幀° (drivers

15、/net/3c59x.c):static int vortcx_rx(struct nct_dcvicc *dcv)skb->dev = dev;skb>protocol = cth_typc_trans(skb, dev);netif_rx(skb); /* pass the packet to the higher layer*/當(dāng)一個包被發(fā)送時,這個變量代表將耍發(fā)送這個包的設(shè)備。在發(fā)送網(wǎng)絡(luò)包時 設(shè)置這個值的代碼要比接收網(wǎng)絡(luò)包時設(shè)置這個值的代碼復(fù)雜。冇些網(wǎng)絡(luò)功能可以把 多個網(wǎng)絡(luò)設(shè)備組成一個虛擬的網(wǎng)絡(luò)設(shè)備(也就是說,這些設(shè)備沒有和物理設(shè)備直接 關(guān)聯(lián)),并由一個虛擬網(wǎng)絡(luò)設(shè)備驅(qū)動管理

16、。當(dāng)虛擬設(shè)備被使川時,dev指針指向虛 擬設(shè)備的net_device結(jié)構(gòu)。而虛擬設(shè)備驅(qū)動會在一紐設(shè)備屮選擇一個設(shè)備并把d ev指針修改為這個設(shè)備的net_device結(jié)構(gòu)。因此 在某些情況下,指向傳輸設(shè) 備的指針會在包處理過程中被改變。struct net_device * input_dev這是收到包的網(wǎng)絡(luò)設(shè)備的指針。如果包是本地生成的,這個值為null。對以太網(wǎng)設(shè)備來說, 這個值由eth_type_trans初始化,它主要被流最控制代碼使用。struct net_device * real_dev這個變屋只對虛擬設(shè)備有意義,它代表少虛擬設(shè)備關(guān)聯(lián)的真實(shí)設(shè)備。例如,bonding和vl an設(shè)

17、備都使用它來指向收到包的真實(shí)設(shè)備。union . h union . nh union . mac這些是指向tcp/1p各層協(xié)議頭的指針:h指向l4, nh指向l3, mac指向l20每個指針的類型都是一個聯(lián)合,包含多個數(shù)據(jù)結(jié)構(gòu),每一個數(shù)據(jù)結(jié)構(gòu)都表示 內(nèi)核在這一層可以解析的協(xié)議。例如,h是一個包含內(nèi)核所能解析的l4協(xié)議的數(shù)據(jù)結(jié)構(gòu)的聯(lián)合。每一個聯(lián)合都有一個raw變量用于初始化,后續(xù)的訪問都是通過 協(xié)議相關(guān)的變量進(jìn)行的。當(dāng)接收一個包時,處理n層協(xié)議頭的函數(shù)從n1層收到一個緩沖區(qū),它的s kb->data指向n層協(xié)議的頭。處理n層協(xié)議的函數(shù)把木層的指針(例如,l3對 皿的是skb->nh

18、指針)初始化為skb->data,因?yàn)檫@個指針的值會在處理下一層 協(xié)議時改變(skb->data將被初始化成緩沖區(qū)里的其他地址)。在處理n層協(xié)議的 函數(shù)結(jié)束時,在把包傳遞給n+ 1層的處理函數(shù)前,它會把skb->data指針指向 n層協(xié)議頭的末尾,這正好是n+1層協(xié)議的協(xié)議頭(參見圖3)。發(fā)送包的過程與此相反,但是由于要為每一層添加新的協(xié)議頭,這個過程要 比接收包的過程復(fù)雜。figure 3. header's pointer initializations while moving from lay er two to layer threeskb-> mac

19、 skb -> nhethernetiptcpethernetiptcpiheaderheaderheaderheaderheaderheader (mac-l2)(u)(l4)1(magl2)(l3)(31skb -> macskb -> dataskb > data(b) after(a) beforestruct dst_entry dst這個變量在路由子系統(tǒng)中使用。char cb40這是一個ucontrol buffer”,或者說是一個私有信息的存儲空間,由每一層白己維護(hù)并使用。 它在分配sk_buff結(jié)構(gòu)時分配(它冃前的大小是40字節(jié),已經(jīng)足夠?yàn)槊恳粚哟鎯Ρ匾?/p>

20、的私 有信息了)。在每一層屮,訪問這個變量的代碼通常川広實(shí)現(xiàn)以增強(qiáng)代碼的可讀性。例如, tcp丿i這個變量存儲tcp_skb_cb結(jié)構(gòu),這個結(jié)構(gòu)在include/net/tcp.h中定義:struct tcp_skb_cb _ _u32seq;/_ _u32endseq;/_ _u32when;*/_ _u8flags;*/* starting sequence number */* seq + ftn + syn + datalen*/* used to compute rtt,s/* tcp header flags;卜面這個宏被tcp代碼用來訪問cb變量。在這個宏里面,有一個簡單的類型轉(zhuǎn)

21、換:#dcfinc tcp_skb_cb(skb)(struct tcp_skb_cb *)&(skb)->cb0)下面的例子是tcp子系統(tǒng)在收到一個分段吋填充相關(guān)數(shù)據(jù)結(jié)構(gòu)的代碼: int tcp_v4_rcv(struct skbuff *skb)th 二 skb->h. th;tcp skb cb(skb)->seq 二 ntohl (th->seq);tcp_skb_cb(skb)->end_seq = (tcp_skb_cb(skb)->seq + th ->syn + th->fin +skb->lcn - th>d

22、off * 4);tcp_skb_cb(skb)->ack_seq 二 ntohl(th->ack_seq);tcp skb cb(skb)->when 二 0;tcp skb cb (skb)->flags = skb->nh. iph->tos;tcp_skb_cb(skb)->sacked = 0;如果想要了解cb屮的參數(shù)是如何被取岀的,"j以查看net/ipv4/tcp_output.c屮的tcp_transmit_skb函數(shù)。這個函數(shù)被tcp用于向ip層發(fā)送一個分段。unsigned int csumunsigned char ip

23、_summed表示校驗(yàn)和以及相關(guān)狀態(tài)標(biāo)記。unsigned char cloned一個布爾標(biāo)記,當(dāng)被設(shè)置時,表示這個結(jié)構(gòu)是另一個sk.buff的克隆。在“克隆和拷貝緩沖 區(qū)”一節(jié)屮有描述。unsigned char pkt_type這個變量表示幀的類型,分類是由l2的目的地址來決定的。可能的取值都在include/linu x/if_packet.h +定義。對以太網(wǎng)設(shè)備來說,這個變量由eth_type_trans函數(shù)初始化。 類型的可能取值如2packet_host包的目的地址與收到它的網(wǎng)絡(luò)設(shè)備的l2地址相等。換句話說,這個包是發(fā)給本機(jī) 的。.packet_multicast包的h的地址是一

24、個多播地址,而這個多播地址是收到這個包的網(wǎng)絡(luò)設(shè)備所注冊的 多播地址。packet_broadcast包的目的地址是一個廣播地址,而這個廣播地址也是收到這個包的網(wǎng)絡(luò)設(shè)備的廣播地址。packet_otherhost包的h的地址與收到它的網(wǎng)絡(luò)設(shè)備的地址完全不同(不管是單播,多播還是廣播), 因此,如果本機(jī)的轉(zhuǎn)發(fā)功能沒有啟用,這個包會被丟棄。packet_outgoing這個包將被發(fā)出。用到這個標(biāo)記的功能包括decnet協(xié)議,或者是為每個網(wǎng)絡(luò)ta p都復(fù)制一份發(fā)出包的函數(shù)。packet. loopback這個包發(fā)向loopback設(shè)備。由于有這個標(biāo)記,在處理loopback設(shè)備時,內(nèi)核町 以跳過一些真

25、實(shí)設(shè)備才需要的操作。packet_ fastroute這個包由快速路由代碼查找路由??焖俾酚晒δ茉?.6內(nèi)核小己經(jīng)去掉了。u32 priority這個變雖描述發(fā)送或轉(zhuǎn)發(fā)包的qos類別。如果包是木地生成的,socket層會設(shè)置priorit y變量。如果包是將要被轉(zhuǎn)發(fā)的,rt_tos2priority函數(shù)會根據(jù)ip頭屮的tos域來計(jì)算賦給 這個變量的值。這個變量的值與dscp(diffserv codepoint)沒冇任何關(guān)系。unsigned short protocol這個變雖是高層協(xié)議從二層設(shè)備的角度所看到的協(xié)議。典型的協(xié)議包括ip, ipv6和arp。 完整的列表在include/lin

26、ux/if_ether.h屮。由于每個協(xié)議部有自己的協(xié)議處理函數(shù)來處 理接收到的包,因此,這個域被設(shè)備驅(qū)動用于通知上層調(diào)用哪個協(xié)議處理函數(shù)。每個網(wǎng)絡(luò)驅(qū) 動都調(diào)用netif_rx來通知上層網(wǎng)絡(luò)協(xié)議的協(xié)議處理函數(shù),因此protocol變量必須在這些協(xié) 議處理函數(shù)調(diào)用z前初始化。unsigned short security這是包的安全級別。這個變量最初iii ipsec子系統(tǒng)使用,但現(xiàn)在己經(jīng)作廢了。4. featurespecific fieldslinux內(nèi)核是模塊化的,你可以選擇包含或者刪除某些功能。因此sk_buff結(jié)構(gòu)里面 的一些成員變量只有在內(nèi)核選擇支持某些功能時才有效,比如防火墻(ne

27、tfilter)或者qos:unsigned long nfmarku32 nfcacheu32 nfctinfostruct nf_conntrack * nfctunsigned int nfdebugstruct nf_bridge_info 5f_bridge這些變最被netfilter使用(防火墻代碼),內(nèi)核編譯選項(xiàng)是“device drivers-> networkin g support-> networking options-> network packet filtering”和兩個子選項(xiàng)“net work packet filtering debuggi

28、ng”和“bridged ip/arp packets filtering"union . private這個聯(lián)合結(jié)構(gòu)被高性能并行接口(hi ppi)使用。相應(yīng)的內(nèi)核編譯選項(xiàng)是“devices drivers> networking support > network device support > hi ppi driver support"u32 tc_indexu32 tc_verdu32 tc_classid這些變量被流量控制代碼使用,內(nèi)核編譯選項(xiàng)是hdevice drivers -> networkings sup port ->

29、networking options - >qos and/or fair queueing"和它的子選項(xiàng)a pack etc lassifier api5,struct sec_path * sp這個變量被ipsec協(xié)議用于跟蹤傳輸?shù)男畔ⅰ?. managem ent functions有很多函數(shù),通常都比較短小而口簡單,內(nèi)核用這些隊(duì)i數(shù)操作sk_buff的成員變量或 者 sk_buff鏈表。圖4會幫助我們理解其中兒個重要的函數(shù)。我們首先來看分配和釋放緩沖區(qū)的函數(shù), 然后是一些通過移動指針在緩沖區(qū)的頭部或尾部預(yù)昭空間的函數(shù)。如果你看過include/linux/skbuff.

30、h和net/core/skbuff.c中的函數(shù),你會發(fā)現(xiàn),基 木上每個函數(shù)都有兩個版木,名字分別是do_something和_do_something。通常第一 種函數(shù)是一個包裝函數(shù),它會在第二種兩數(shù)的棊礎(chǔ)上增加合法性檢查或者鎖。一般來說,類 _do_something的函數(shù)不能被直接調(diào)用(除非滿足特定的條件,比如說鎖)。那些違反 這條規(guī)則而直接引用這些函數(shù)的不良代碼會最終被更e。figure 4. before and after: (a)skb_put, (b)skb_push, (c)skb_pull, and (d)skb_reserve(b1)skb->data$kb ->

31、; lennskb -> tail(b2)skb -> tailskb-> dataskb -> lenskb -> tail(cl)(d1)skb -> len((d2)skb -> dataskb ->lenskb -> tailskb -> dataskb >lenskb -> tailskb -> data打仆skb > lenskb -> tail l5.1. allocating memory: alloc_skb and dev_alloc_skballoc_skb是net/core/skb

32、uff.c里面定義的,用于分配緩沖區(qū)的函數(shù)。我們已經(jīng)知 道,數(shù)據(jù)緩沖區(qū)和緩沖區(qū)的描述結(jié)構(gòu)(sk_buff結(jié)構(gòu))是兩種不同的實(shí)體,這就意味著,在分 配一個緩沖區(qū)時,需要分配兩塊內(nèi)存(一個是緩沖區(qū),一個是緩沖區(qū)的描述結(jié)構(gòu)sk_buff)oalloc_skb調(diào)用函數(shù)kmem_cache_alloc從緩存中獲取一個sk_buff結(jié)構(gòu),并調(diào)用kmalloc分配緩沖區(qū)(如果冇緩存的話,它同樣從緩存中獲取內(nèi)存)。簡化后的代碼如下:skb = kmcm_cachc_alloc(skbuff_hcad_cachc, gfp_mask & gfp_dma);size = skb_data_align(si

33、ze);data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask);在調(diào)用kmalloc前,size參數(shù)通過skb_data_align宏強(qiáng)制対齊。在函數(shù)返回前, 它會初始化結(jié)構(gòu)中的一些變最,最后的結(jié)構(gòu)如圖5所示。在圖5右邊所示的內(nèi)存塊的底部, 你能看到對齊操作所帶來的填充區(qū)域。struct sk buffmta_augnsize)dev_alloc_skb也是一個緩沖區(qū)分配函數(shù),它主要被設(shè)備駁動使用,通常用在中斷上 下文中。這是一個alloc_skb函數(shù)的包裝函數(shù),它會在請求分配的大小上增加16字節(jié)的空 間以優(yōu)化緩沖區(qū)的讀寫

34、效率,它的分配要求使用原子操作(gfp_atomic),這是因?yàn)樗?在中斷處理函數(shù)中被調(diào)用的。static inline struct skbuff *dev_alloc_skb(unsigned int length)static inlinestruct skbuff *devallocskb(unsigned int length, int gfpmask)struct sk_buff *skb = alloc_skb(length + 16, gfp_mask); if (1 ikely(skb)skb reserve(skb, 16);return skb;如果沒有體系架構(gòu)和關(guān)的實(shí)

35、現(xiàn),缺省使用_dev_alloc_skb的實(shí)現(xiàn)。5.2. freeing memory: kfree_skb and dev_kfree_skb這兩個函數(shù)釋放緩沖區(qū),并把它返回給緩沖池(緩存)。kfree_skb可以直接調(diào)用,也 可以通過包裝函數(shù)dev_kfree_skb調(diào)用。麻面這個函數(shù)一般被設(shè)備驅(qū)動使用,與之功能相 反的函數(shù)是dev_alloc_skbo dev_kfree_skb僅是一個簡單的宏,它什么都不做,只簡單 地調(diào)用kfree_skb。這些函數(shù)只有fii skb-> users為1地情況下才釋放內(nèi)存(沒有人引用這 個結(jié)構(gòu))。否則,它只是簡單地減小skb-> users

36、o如果緩沖區(qū)有三個引用者,那么只有第三次調(diào)用dev_kfree_skb或kfree _skb時才釋放內(nèi)存。圖6中的流程圖顯示了分配一個緩沖區(qū)所需要的步驟。當(dāng)sk_buff釋放后,dst_rel ease同樣會被調(diào)用以減小和關(guān)dst_entry數(shù)據(jù)結(jié)構(gòu)的引用計(jì)數(shù)。如果destructor被初始化過,相應(yīng)的函數(shù)會在此時被調(diào)用.在圖5中,我們看到,一個簡單的場景是:一個sk.buff結(jié)構(gòu)與另一個內(nèi)存塊相關(guān), 這個內(nèi)存塊里存儲的是真正的數(shù)據(jù)。當(dāng)然,內(nèi)存塊底部的skb_shared_info數(shù)據(jù)結(jié)構(gòu)可以 包含指向其他分片的指針(參見圖5)。如果存在分片,kfree_skb同樣會釋放這些分片所占 用的內(nèi)存

37、。最后,kfree_skb把sk_buff結(jié)構(gòu)返回給skbuff_head_cache緩存。5.3. data reservation and alignment: skb_reserve, skb_put, skb _push, and skb_pullskbeserve可以在緩沖區(qū)的頭部預(yù)留一定的空間,它通常被用來在緩沖區(qū)中插入?yún)f(xié) 議頭或者在某個邊界上対齊。這個函數(shù)改變data和tail指針,而data和tail指針分別指向負(fù)載的開頭和結(jié)尾,圖4(d)展示了調(diào)用skb_reserve(skb,n)的結(jié)果。這個函數(shù)通常在 分配緩沖區(qū)z后就調(diào)用,此時的 data和tail指針還是指向同一個地方

38、。如果你查看某個以太網(wǎng)設(shè)備驅(qū)動的收包函數(shù)(例如,drivers/net/3c59x.c中的vort exx),你就會發(fā)現(xiàn)它在分配緩沖區(qū)之示,在向緩沖區(qū)屮填充數(shù)據(jù)之前,會調(diào)用下面的函 數(shù):skb_reserve(skb, 2);/* align ip on 16 byte boundaries */figure 6. kfree_skb functionyesdecrement skh refcntisswrefent=0?yesis wo ina list?noprint warning msq (likely to be a bug)nodestfiktorinitialized?execu

39、te destructornoskb.release datanoyesnois 5kb a done?is refcnt dataref=o?free the main buffer and any fragmentsdecrement refcnt ondata (dataref)return skb to thecache由于以太網(wǎng)幀的頭部長度是14個八位紐,這個函數(shù)把緩沖區(qū)的頭部指針向后移動了 2 個字節(jié)。這樣,緊跟在以太網(wǎng)頭部z后的ip頭部在緩沖區(qū)中存儲時就可以在16字節(jié)的邊 界上對齊。如圖7所示。the trame on the buffer(a)(b)(c)struct sk_b

40、uffstruct sk_buffstruct sk_bufflen=l headdatatailendpaddingethernet headerrip headerip payload圖8展示了一個在發(fā)送過程屮使用skbeserve的例子。figure 8. buffer that is filled in w hile traversing the stackfromthe tcp layerdow n to the link layer(b):(0struct sk_buffstruct sk_bufflen=l1struct sk_buff(d)struct sk bufftcp h

41、eadertcp payload5av3hldxl5w (e)struct buffl3head data tail end(f)struct sk buffethernet headerip headertcp headertcp payload1 當(dāng)tcp發(fā)送數(shù)據(jù)時,它根據(jù)一些條件分配一個緩沖區(qū)(比如,tcp的最人 分段長度(mss),是否支持散讀散寫i/o等2. tcp在緩沖區(qū)的頭部預(yù)留足夠的空間(用skb_reserve)用于填充各層的 頭部(如tcp, ip,鏈路層等)。max_tcp_header參數(shù)是各層頭部長度的總利 它考慮了最壞的情況:由于tcp層不知道將要用哪個接口發(fā)送包,

42、它為每一層預(yù)留 了最大的頭部長度。它甚至考慮了出現(xiàn)多個ip頭的可能性(如果內(nèi)核編譯支持ip o ver ip,我們就會遇到多個ip頭的情況)。3. 把tcp的負(fù)載拷貝到緩沖區(qū)。需要注意的是:圖8只是一個例子。tcp 的負(fù)載可能會被組織成其他形式。例如它可以存儲到分片中。4. tcp層添加自己的頭部。5. tcp層把緩沖區(qū)傳遞給i p層,ip層同樣添加白己的頭部。6. i p層把緩沖區(qū)傳遞給鄰居層,鄰居層添加鏈路層頭部。當(dāng)緩沖區(qū)在協(xié)議棧中向下層傳遞時,每一層都把skb->data指針向下移動,然后拷貝 自己的頭部,同時更新skb->len0這些操作都使用圖4中所展示的函數(shù)完成。skb

43、eserve函數(shù)并沒有把數(shù)據(jù)移出或移入緩沖區(qū),它只是簡單地更新了緩沖區(qū)的兩 個指針,這兩個指針在圖4(d)中有描述。static inline void skb reserve(struct sk buff *skb, unsigned int len)skb->data+=len; skb->tail+=lcn;skb_push在緩沖區(qū)的開頭加入一塊數(shù)據(jù),而skb_put在緩沖區(qū)的末尾加入一塊數(shù)據(jù)。 與 skb_reserve類似,這些函數(shù)都不會真的往緩沖區(qū)屮添加數(shù)據(jù),相反,它們只是移動緩沖區(qū)的頭指針和尾 指針。數(shù)據(jù)由其他函數(shù)拷貝到緩沖區(qū)中。skb_pull通過把head指針往前

44、移來在緩沖區(qū)的 頭部刪除一塊數(shù)據(jù)。圖4展示了這些函數(shù)是如何工作的。2.1.5.4. the skb shared info structure and the skb shinfo func tion如圖5所示,在緩沖區(qū)數(shù)據(jù)的末尾,有一個數(shù)據(jù)結(jié)構(gòu)skb_sharednfo,它保存了數(shù) 據(jù)塊的附加信息。這個數(shù)據(jù)結(jié)構(gòu)緊跟在end指針?biāo)傅牡刂穤后(end指針指示數(shù)據(jù)的末 尾)。下面是這個結(jié)構(gòu)的定義:struet skb shared info atomic_tdataref;unsigned intnrfrags;unsigned shorttso_size;unsigned shorttso_s

45、cqs;struct sk_buff*frag_list;skb_frag_tfragsmax_skb_frags;dataref表示數(shù)據(jù)塊的“用八”數(shù),這個值在下一節(jié)(克隆和拷貝緩沖區(qū))中有描述。nf_ frags, frag_list和frags用于存儲ip分片。skb_is_nonlinear函數(shù)用于測試一個緩沖 區(qū)是否是分片的,而skbjinearize可以把分片組合成一個單一的緩沖區(qū)。組合分片涉及 到數(shù)據(jù)拷貝,它將嚴(yán)重影響系統(tǒng)性能。有些網(wǎng)卡碾件可以完成一些傳統(tǒng)上山cpu完成的任務(wù)。最常見的例子就是計(jì)算l3和 l4校驗(yàn)和。冇些網(wǎng)卡甚至可以維護(hù)l4協(xié)議的狀態(tài)機(jī)。在下面的例了中,我們主要討

46、論tc p段卸載tcp segmentation offload,這些網(wǎng)卡實(shí)現(xiàn)了 tcp層的一部分功能。tso_size 和tso_seqs就在這種情況下使用。需要注意的是:sk_buff中沒有指向skb_sharednfo結(jié)構(gòu)的指針。如呆要訪問這個 結(jié)構(gòu),就需要使用skb_info宏,這個宏簡單地返回end指針:#define skb_shinfo(skb)(struct skb_shared_info *) (skb)->end)卜-而的語句展示了如何使用這個宏來增加結(jié)構(gòu)中的某個成員變量的值: skb_shinfo(skb)->dataref+;2.1.5.5 cloning

47、and copying buffers如果一個緩沖區(qū)需要被不同的用戶獨(dú)立地操作,而這些用戶町能會修改sk.buff中某 些變量的值(比如h和nh值),內(nèi)核沒有必耍為每個用戶復(fù)制-份完整的sk.buff以及相應(yīng) 的緩沖區(qū)。相反,為提高性能,內(nèi)核克隆一個緩沖區(qū)??私颠^程只復(fù)制sk_buff結(jié)構(gòu),同 時修改緩沖區(qū)的引用計(jì)數(shù)以避免共亨的數(shù)據(jù)被提前釋放??寺【彌_區(qū)使川skb clone兩數(shù)。一個使用包克隆的場景是:一個接收包的過程需要把這個包傳遞給多個接收者,例如 包處理函數(shù)或者一個或多個網(wǎng)絡(luò)模塊。被克隆的sk_buff不會放在任何璉表中,同時也不會有到socket的引用。原始的和 克隆的sk_buff

48、中的skb-> cloned值都被置為1??寺“膕kb-> users值被置為1,這 樣,在釋放時,可以先釋放sk_buff結(jié)構(gòu)。同時,緩沖區(qū)的引用計(jì)數(shù)(dataref)增加1(因?yàn)?冇多個sk_buff結(jié)構(gòu)指向它)o圖9展示了克隆緩沖區(qū)的例子。headdatatailendstruct $k_buff struct 5kb_5haredlinfoslshinfo(done)'figure 9. skb_clone functionskb_cloned函數(shù)可以用來測試skb的克隆狀態(tài)。圖9展示了一個分片緩沖區(qū)的例子,這個緩沖區(qū)的一些數(shù)據(jù)保存在分片結(jié)構(gòu)數(shù)組frag s中。skb_share_check用于檢查引用

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論