




已閱讀5頁(yè),還剩18頁(yè)未讀, 繼續(xù)免費(fèi)閱讀
版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
Linux TCP/IP 協(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ù)包的包頭信息。它在中定義,并包含很多成員變量供網(wǎng)絡(luò)代碼中的各子系統(tǒng)使用。 這個(gè)結(jié)構(gòu)在linux內(nèi)核的發(fā)展過(guò)程中改動(dòng)過(guò)很多次,或者是增加新的選項(xiàng),或者是重新組織已存在的成員變量以使得成員變量的布局更加清晰。它的成員變量可以大致分為以下幾類: Layout 布局 General 通用 Feature-specific功能相關(guān) Management functions管理函數(shù) 這個(gè)結(jié)構(gòu)被不同的網(wǎng)絡(luò)層(MAC或者其他二層鏈路協(xié)議,三層的IP,四層的TCP或UDP等)使用,并且其中的成員變量在結(jié)構(gòu)從一層向另一層傳遞時(shí)改變。L4向L3傳遞前會(huì)添加一個(gè)L4的頭部,同樣,L3向L2傳遞前,會(huì)添加一個(gè)L3的頭部。添加頭部比在不同層之間拷貝數(shù)據(jù)的效率更高。由于在緩沖區(qū)的頭部添加數(shù)據(jù)意味著要修改指向緩沖區(qū)的指針,這是個(gè)復(fù)雜的操作,所以內(nèi)核提供了一個(gè)函數(shù)skb_reserve(在后面的章節(jié)中描述)來(lái)完成這個(gè)功能。協(xié)議棧中的每一層在往下一層傳遞緩沖區(qū)前,第一件事就是調(diào)用skb_reserve在緩沖區(qū)的頭部給協(xié)議頭預(yù)留一定的空間。 skb_reserve同樣被設(shè)備驅(qū)動(dòng)使用來(lái)對(duì)齊接收到包的包頭。如果緩沖區(qū)向上層協(xié)議傳遞,舊的協(xié)議層的頭部信息就沒(méi)什么用了。例如,L2的頭部只有在網(wǎng)絡(luò)驅(qū)動(dòng)處理L2的協(xié)議時(shí)有用,L3是不會(huì)關(guān)心它的信息的。但是,內(nèi)核并沒(méi)有把L2的頭部從緩沖區(qū)中刪除,而是把有效荷載的指針指向L3的頭部,這樣做,可以節(jié)省CPU時(shí)間。 1. 網(wǎng)絡(luò)參數(shù)和內(nèi)核數(shù)據(jù)結(jié)構(gòu) 就像你在瀏覽TCP/IP規(guī)范或者配置內(nèi)核時(shí)所看到的一樣,網(wǎng)絡(luò)代碼提供了很多有用的功能,但是這些功能并不是必須的,比如說(shuō),防火墻,多播,還有其他一些功能。大部分的功能都需要在內(nèi)核數(shù)據(jù)結(jié)構(gòu)中添加自己的成員變量。因此,sk_buff里面包含了很多像#ifdef這樣的預(yù)編譯指令。例如,在sk_buff結(jié)構(gòu)的最后,你可以找到: struct sk_buff . . .#ifdef CONFIG_NET_SCHED _ _u32 tc_index;#ifdef CONFIG_NET_CLS_ACT _ _u32 tc_verd; _ _u32 tc_classid;#endif#endif它表明,tc_index只有在編譯時(shí)定義了CONFIG_NET_SCHED符號(hào)才有效。這個(gè)符號(hào)可以通過(guò)選擇特定的編譯選項(xiàng)來(lái)定義(例如:Device Drivers Networking supportNetworking options QoS and/or fair queueing)。這些編譯選項(xiàng)可以由管理員通過(guò)make config來(lái)選擇,或者通過(guò)一些自動(dòng)安裝工具來(lái)選擇。 前面的例子有兩個(gè)嵌套的選項(xiàng):CONFIG_NET_CLS_ACT(包分類器)只有在選擇支持“QoS and/or fair queueing”時(shí)才能生效。 順便提一下,QoS選項(xiàng)不能被編譯成內(nèi)核模塊。原因就是,內(nèi)核編譯之后,由某個(gè)選項(xiàng)所控制的數(shù)據(jù)結(jié)構(gòu)是不能動(dòng)態(tài)變化的。一般來(lái)說(shuō),如果某個(gè)選項(xiàng)會(huì)修改內(nèi)核數(shù)據(jù)結(jié)構(gòu)(比如說(shuō),在sk_buff里面增加一個(gè)項(xiàng)tc_index),那么,包含這個(gè)選項(xiàng)的組件就不能被編譯成內(nèi)核模塊。 你可能經(jīng)常需要查找是哪個(gè)make config編譯選項(xiàng)或者變種定義了某個(gè)#ifdef標(biāo)記,以便理解內(nèi)核中包含的某段代碼。在2.6內(nèi)核中,最快的,查找它們之間關(guān)聯(lián)關(guān)系的方法, 就是查找分布在內(nèi)核源代碼樹(shù)中的kconfig文件中是否定義了相應(yīng)的符號(hào)(每個(gè)目錄都有一個(gè)這樣的文件)。在 2.4內(nèi)核中,你需要查看Documentation/Configure.help文件。 2. Layout Fields有些sk_buff成員變量的作用是方便查找或者是連接數(shù)據(jù)結(jié)構(gòu)本身。內(nèi)核可以把sk_buff組織成一個(gè)雙向鏈表。當(dāng)然,這個(gè)鏈表的結(jié)構(gòu)要比常見(jiàn)的雙向鏈表的結(jié)構(gòu)復(fù)雜一點(diǎn)。 就像任何一個(gè)雙向鏈表一樣,sk_buff中有兩個(gè)指針next和prev,其中,next指向下一個(gè)節(jié)點(diǎn),而 prev指向上一個(gè)節(jié)點(diǎn)。但是,這個(gè)鏈表還有另一個(gè)需求:每個(gè)sk_buff結(jié)構(gòu)都必須能夠很快找到鏈表頭節(jié)點(diǎn)。為了滿足這個(gè)需求,在第一個(gè)節(jié)點(diǎn)前面會(huì)插入另一個(gè)結(jié)構(gòu)sk_buff_head,這是一個(gè)輔助節(jié)點(diǎn),它的定義如下: struct sk_buff_head /* These two members must be first. */ struct sk_buff * next; struct sk_buff * prev; _ _u32 qlen; spinlock_t lock; ;qlen代表鏈表元素的個(gè)數(shù)。lock用于防止對(duì)鏈表的并發(fā)訪問(wèn)。 sk_buff和sk_buff_head的前兩個(gè)元素是一樣的:next和prev指針。這使得它們可以放到同一個(gè)鏈表中,盡管sk_buff_head要比sk_buff小得多。另外,相同的函數(shù)可以同樣應(yīng)用于sk_buff和sk_buff_head。為了使這個(gè)數(shù)據(jù)結(jié)構(gòu)更靈活,每個(gè)sk_buff結(jié)構(gòu)都包含一個(gè)指向sk_buff_head的指針。這個(gè)指針的名字是list。圖1會(huì)幫助你理解它們之間的關(guān)系。 Figure 1. List of sk_buff elements其他有趣的成員變量如下:struct sock *sk這是一個(gè)指向擁有這個(gè)sk_buff的sock結(jié)構(gòu)的指針。這個(gè)指針在網(wǎng)絡(luò)包由本機(jī)發(fā)出或者由本機(jī)進(jìn)程接收時(shí)有效,因?yàn)椴蹇谙嚓P(guān)的信息被L4(TCP或UDP)或者用戶空間程序使用。如果sk_buff只在轉(zhuǎn)發(fā)中使用(這意味著,源地址和目的地址都不是本機(jī)地址),這個(gè)指針是NULL。 unsigned int len這是緩沖區(qū)中數(shù)據(jù)部分的長(zhǎng)度。它包括主緩沖區(qū)中的數(shù)據(jù)長(zhǎng)度(data指針指向它)和分片中的數(shù)據(jù)長(zhǎng)度。它的值在緩沖區(qū)從一個(gè)層向另一個(gè)層傳遞時(shí)改變,因?yàn)橥蠈觽鬟f,舊的頭部就沒(méi)有用了,而往下層傳遞,需要添加本層的頭部。len同樣包含了協(xié)議頭的長(zhǎng)度。 unsigned int data_len和len不同,data_len只計(jì)算分片中數(shù)據(jù)的長(zhǎng)度。unsigned int mac_len這是mac頭的長(zhǎng)度。 atomic_t users這是一個(gè)引用計(jì)數(shù),用于計(jì)算有多少實(shí)體引用了這個(gè)sk_buff緩沖區(qū)。它的主要用途是防止釋放sk_buff后,還有其他實(shí)體引用這個(gè)sk_buff。因此,每個(gè)引用這個(gè)緩沖區(qū)的實(shí)體都必須在適當(dāng)?shù)臅r(shí)候增加或減小這個(gè)變量。這個(gè)計(jì)數(shù)器只保護(hù)sk_buff結(jié)構(gòu)本身,而緩沖區(qū)的數(shù)據(jù)部分由類似的計(jì)數(shù)器(dataref)來(lái)保護(hù).有時(shí)可以用atomic_inc和atomic_dec函數(shù)來(lái)直接增加或減小users,但是,通常還是使用函數(shù)skb_get和kfree_skb來(lái)操作這個(gè)變量。 unsigned int truesize這是緩沖區(qū)的總長(zhǎng)度,包括sk_buff結(jié)構(gòu)和數(shù)據(jù)部分。如果申請(qǐng)一個(gè)len字節(jié)的緩沖區(qū),alloc_skb函數(shù)會(huì)把它初始化成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變化時(shí),這個(gè)變量也會(huì)變化。 unsigned char *headunsigned char *endunsigned char *dataunsigned char *tail它們表示緩沖區(qū)和數(shù)據(jù)部分的邊界。在每一層申請(qǐng)緩沖區(qū)時(shí),它會(huì)分配比協(xié)議頭或協(xié)議數(shù)據(jù)大的空間。head和end指向緩沖區(qū)的頭部和尾部,而data和tail指向?qū)嶋H數(shù)據(jù)的頭部和尾部,參見(jiàn)圖2。每一層會(huì)在head和data之間填充協(xié)議頭,或者在tail和end之間添加新的協(xié)議數(shù)據(jù)。圖2中右邊數(shù)據(jù)部分會(huì)在尾部包含一個(gè)附加的頭部。 Figure 2. head/end versus data/tail pointersvoid (*destructor)(.)這個(gè)函數(shù)指針可以初始化成一個(gè)在緩沖區(qū)釋放時(shí)完成某些動(dòng)作的函數(shù)。如果緩沖區(qū)不屬于一個(gè)socket,這個(gè)函數(shù)指針通常是不會(huì)被賦值的。如果緩沖區(qū)屬于一個(gè)socket, 這個(gè)函數(shù)指針會(huì)被賦值為sock_rfree或sock_wfree(分別由skb_set_owner_r或skb_set_owner_w函數(shù)初始化)。這兩個(gè)sock_xxx函數(shù)用于更新socket的隊(duì)列中的內(nèi)存容量。 3. General Fields本節(jié)描述sk_buff的主要成員變量,這些成員變量與特定的內(nèi)核功能無(wú)關(guān): struct timeval stamp這個(gè)變量只對(duì)接收到的包有意義。它代表包接收時(shí)的時(shí)間戳,或者有時(shí)代表包準(zhǔn)備發(fā)出時(shí)的時(shí)間戳。它在netif_rx里面由函數(shù)net_timestamp設(shè)置,而netif_rx是設(shè)備驅(qū)動(dòng)收到一個(gè)包后調(diào)用的函數(shù)。struct net_device *dev這個(gè)變量的類型是net_device,net_device它代表一個(gè)網(wǎng)絡(luò)設(shè)備。dev的作用與這個(gè)包是準(zhǔn)備發(fā)出的包還是剛接收的包有關(guān)。當(dāng)收到一個(gè)包時(shí),設(shè)備驅(qū)動(dòng)會(huì)把sk_buff的dev指針指向收到這個(gè)包的設(shè)備的數(shù)據(jù)結(jié)構(gòu),就像下面的vortex_rx里的一段代碼所做的一樣,這個(gè)函數(shù)屬于3c59x系列以太網(wǎng)卡驅(qū)動(dòng),用于接收一個(gè)幀。(drivers/net/3c59x.c): static int vortex_rx(struct net_device *dev) . . . skb-dev = dev; . . . skb-protocol = eth_type_trans(skb, dev); netif_rx(skb); /* Pass the packet to the higher layer */ . . . 當(dāng)一個(gè)包被發(fā)送時(shí),這個(gè)變量代表將要發(fā)送這個(gè)包的設(shè)備。在發(fā)送網(wǎng)絡(luò)包時(shí)設(shè)置這個(gè)值的代碼要比接收網(wǎng)絡(luò)包時(shí)設(shè)置這個(gè)值的代碼復(fù)雜。有些網(wǎng)絡(luò)功能可以把多個(gè)網(wǎng)絡(luò)設(shè)備組成一個(gè)虛擬的網(wǎng)絡(luò)設(shè)備(也就是說(shuō),這些設(shè)備沒(méi)有和物理設(shè)備直接關(guān)聯(lián)),并由一個(gè)虛擬網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)管理。當(dāng)虛擬設(shè)備被使用時(shí),dev指針指向虛擬設(shè)備的net_device結(jié)構(gòu)。而虛擬設(shè)備驅(qū)動(dòng)會(huì)在一組設(shè)備中選擇一個(gè)設(shè)備并把dev指針修改為這個(gè)設(shè)備的net_device結(jié)構(gòu)。因此,在某些情況下, 指向傳輸設(shè)備的指針會(huì)在包處理過(guò)程中被改變。 struct net_device *input_dev這是收到包的網(wǎng)絡(luò)設(shè)備的指針。如果包是本地生成的,這個(gè)值為NULL。對(duì)以太網(wǎng)設(shè)備來(lái)說(shuō),這個(gè)值由eth_type_trans初始化,它主要被流量控制代碼使用。 struct net_device *real_dev這個(gè)變量只對(duì)虛擬設(shè)備有意義,它代表與虛擬設(shè)備關(guān)聯(lián)的真實(shí)設(shè)備。例如,Bonding和VLAN設(shè)備都使用它來(lái)指向收到包的真實(shí)設(shè)備。 union . hunion . nhunion . mac這些是指向TCP/IP各層協(xié)議頭的指針:h指向L4,nh指向L3,mac指向L2。每個(gè)指針的類型都是一個(gè)聯(lián)合,包含多個(gè)數(shù)據(jù)結(jié)構(gòu),每一個(gè)數(shù)據(jù)結(jié)構(gòu)都表示內(nèi)核在這一層可以解析的協(xié)議。例如,h是一個(gè)包含內(nèi)核所能解析的L4協(xié)議的數(shù)據(jù)結(jié)構(gòu)的聯(lián)合。每一個(gè)聯(lián)合都有一個(gè)raw變量用于初始化,后續(xù)的訪問(wèn)都是通過(guò)協(xié)議相關(guān)的變量進(jìn)行的。 當(dāng)接收一個(gè)包時(shí),處理n層協(xié)議頭的函數(shù)從n-1層收到一個(gè)緩沖區(qū),它的skb-data指向n層協(xié)議的頭。處理n層協(xié)議的函數(shù)把本層的指針(例如,L3對(duì)應(yīng)的是skb-nh指針)初始化為skb-data,因?yàn)檫@個(gè)指針的值會(huì)在處理下一層協(xié)議時(shí)改變(skb-data將被初始化成緩沖區(qū)里的其他地址)。在處理n層協(xié)議的函數(shù)結(jié)束時(shí),在把包傳遞給n+1層的處理函數(shù)前,它會(huì)把skb-data指針指向n層協(xié)議頭的末尾,這正好是n+1層協(xié)議的協(xié)議頭(參見(jiàn)圖3)。 發(fā)送包的過(guò)程與此相反,但是由于要為每一層添加新的協(xié)議頭,這個(gè)過(guò)程要比接收包的過(guò)程復(fù)雜。 Figure 3. Headers pointer initializations while moving from layer two to layer threestruct dst_entry dst這個(gè)變量在路由子系統(tǒng)中使用。 char cb40這是一個(gè)“control buffer”,或者說(shuō)是一個(gè)私有信息的存儲(chǔ)空間,由每一層自己維護(hù)并使用。它在分配sk_buff結(jié)構(gòu)時(shí)分配(它目前的大小是40字節(jié),已經(jīng)足夠?yàn)槊恳粚哟鎯?chǔ)必要的私有信息了)。在每一層中,訪問(wèn)這個(gè)變量的代碼通常用宏實(shí)現(xiàn)以增強(qiáng)代碼的可讀性。例如,TCP用這個(gè)變量存儲(chǔ)tcp_skb_cb結(jié)構(gòu),這個(gè)結(jié)構(gòu)在include/net/tcp.h中定義: struct tcp_skb_cb . . . _ _u32 seq; /* Starting sequence number */ _ _u32 end_seq; /* SEQ + FIN + SYN + datalen*/ _ _u32 when; /* used to compute rtts */ _ _u8 flags; /* TCP header flags. */ . . .;下面這個(gè)宏被TCP代碼用來(lái)訪問(wèn)cb變量。在這個(gè)宏里面,有一個(gè)簡(jiǎn)單的類型轉(zhuǎn)換: #define TCP_SKB_CB(_ _skb) (struct tcp_skb_cb *)&(_ _skb)-cb0)下面的例子是TCP子系統(tǒng)在收到一個(gè)分段時(shí)填充相關(guān)數(shù)據(jù)結(jié)構(gòu)的代碼: int tcp_v4_rcv(struct sk_buff *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-len - th-doff * 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ù)是如何被取出的,可以查看net/ipv4/tcp_output.c中的tcp_transmit_skb函數(shù)。這個(gè)函數(shù)被TCP用于向IP層發(fā)送一個(gè)分段。 unsigned int csumunsigned char ip_summed表示校驗(yàn)和以及相關(guān)狀態(tài)標(biāo)記。 unsigned char cloned一個(gè)布爾標(biāo)記,當(dāng)被設(shè)置時(shí),表示這個(gè)結(jié)構(gòu)是另一個(gè)sk_buff的克隆。在“克隆和拷貝緩沖區(qū)”一節(jié)中有描述。 unsigned char pkt_type這個(gè)變量表示幀的類型,分類是由L2的目的地址來(lái)決定的??赡艿娜≈刀荚趇nclude/linux/if_packet.h中定義。對(duì)以太網(wǎng)設(shè)備來(lái)說(shuō),這個(gè)變量由eth_type_trans函數(shù)初始化。 類型的可能取值如下: PACKET_HOST包的目的地址與收到它的網(wǎng)絡(luò)設(shè)備的L2地址相等。換句話說(shuō),這個(gè)包是發(fā)給本機(jī)的。 .PACKET_MULTICAST包的目的地址是一個(gè)多播地址,而這個(gè)多播地址是收到這個(gè)包的網(wǎng)絡(luò)設(shè)備所注冊(cè)的多播地址。 PACKET_BROADCAST包的目的地址是一個(gè)廣播地址,而這個(gè)廣播地址也是收到這個(gè)包的網(wǎng)絡(luò)設(shè)備的廣播地址。 PACKET_OTHERHOST包的目的地址與收到它的網(wǎng)絡(luò)設(shè)備的地址完全不同(不管是單播,多播還是廣播),因此,如果本機(jī)的轉(zhuǎn)發(fā)功能沒(méi)有啟用,這個(gè)包會(huì)被丟棄。 PACKET_OUTGOING這個(gè)包將被發(fā)出。用到這個(gè)標(biāo)記的功能包括Decnet協(xié)議,或者是為每個(gè)網(wǎng)絡(luò)tap都復(fù)制一份發(fā)出包的函數(shù)。 PACKET_LOOPBACK這個(gè)包發(fā)向loopback設(shè)備。由于有這個(gè)標(biāo)記,在處理loopback設(shè)備時(shí),內(nèi)核可以跳過(guò)一些真實(shí)設(shè)備才需要的操作。 PACKET_FASTROUTE這個(gè)包由快速路由代碼查找路由??焖俾酚晒δ茉?.6內(nèi)核中已經(jīng)去掉了。 _ _u32 priority這個(gè)變量描述發(fā)送或轉(zhuǎn)發(fā)包的QoS類別。如果包是本地生成的,socket層會(huì)設(shè)置priority變量。如果包是將要被轉(zhuǎn)發(fā)的,rt_tos2priority函數(shù)會(huì)根據(jù)ip頭中的Tos域來(lái)計(jì)算賦給這個(gè)變量的值。這個(gè)變量的值與DSCP(DiffServ CodePoint)沒(méi)有任何關(guān)系。 unsigned short protocol這個(gè)變量是高層協(xié)議從二層設(shè)備的角度所看到的協(xié)議。典型的協(xié)議包括IP,IPV6和ARP。完整的列表在include/linux/if_ether.h中。由于每個(gè)協(xié)議都有自己的協(xié)議處理函數(shù)來(lái)處理接收到的包,因此,這個(gè)域被設(shè)備驅(qū)動(dòng)用于通知上層調(diào)用哪個(gè)協(xié)議處理函數(shù)。每個(gè)網(wǎng)絡(luò)驅(qū)動(dòng)都調(diào)用netif_rx來(lái)通知上層網(wǎng)絡(luò)協(xié)議的協(xié)議處理函數(shù),因此protocol變量必須在這些協(xié)議處理函數(shù)調(diào)用之前初始化。 unsigned short security這是包的安全級(jí)別。這個(gè)變量最初由IPSec子系統(tǒng)使用,但現(xiàn)在已經(jīng)作廢了。 4. Feature-Specific Fieldslinux內(nèi)核是模塊化的,你可以選擇包含或者刪除某些功能。因此,sk_buff結(jié)構(gòu)里面的一些成員變量只有在內(nèi)核選擇支持某些功能時(shí)才有效,比如防火墻(netfilter)或者qos: unsigned long nfmark_ _u32 nfcache_ _u32 nfctinfostruct nf_conntrack *nfctunsigned int nfdebugstruct nf_bridge_info *nf_bridge這些變量被netfilter使用(防火墻代碼),內(nèi)核編譯選項(xiàng)是“Device Drivers-Networking support- Networking options- Network packet filtering”和兩個(gè)子選項(xiàng)“Network packet filtering debugging”和“Bridged IP/ARP packets filtering” union . private這個(gè)聯(lián)合結(jié)構(gòu)被高性能并行接口(HIPPI)使用。相應(yīng)的內(nèi)核編譯選項(xiàng)是“Device-Drivers -Networking support -Network device support -HIPPI driver support” _ _u32 tc_index_ _u32 tc_verd_ _u32 tc_classid這些變量被流量控制代碼使用,內(nèi)核編譯選項(xiàng)是“Device Drivers -Networking-support -Networking options -QoS and/or fair queueing”和它的子選項(xiàng)“Packetclassifier API” struct sec_path *sp這個(gè)變量被IPSec協(xié)議用于跟蹤傳輸?shù)男畔ⅰ?5. Management Functions有很多函數(shù),通常都比較短小而且簡(jiǎn)單,內(nèi)核用這些函數(shù)操作sk_buff的成員變量或者sk_buff鏈表。圖4會(huì)幫助我們理解其中幾個(gè)重要的函數(shù)。我們首先來(lái)看分配和釋放緩沖區(qū)的函數(shù),然后是一些通過(guò)移動(dòng)指針在緩沖區(qū)的頭部或尾部預(yù)留空間的函數(shù)。 如果你看過(guò)include/linux/skbuff.h和net/core/skbuff.c中的函數(shù),你會(huì)發(fā)現(xiàn),基本上每個(gè)函數(shù)都有兩個(gè)版本,名字分別是do_something和_do_something。通常第一種函數(shù)是一個(gè)包裝函數(shù),它會(huì)在第二種函數(shù)的基礎(chǔ)上增加合法性檢查或者鎖。一般來(lái)說(shuō),類似_do_something的函數(shù)不能被直接調(diào)用(除非滿足特定的條件,比如說(shuō)鎖)。那些違反這條規(guī)則而直接引用這些函數(shù)的不良代碼會(huì)最終被更正。 Figure 4. Before and after: (a)skb_put, (b)skb_push, (c)skb_pull, and (d)skb_reserve5.1. Allocating memory: alloc_skb and dev_alloc_skballoc_skb是net/core/skbuff.c里面定義的,用于分配緩沖區(qū)的函數(shù)。我們已經(jīng)知道,數(shù)據(jù)緩沖區(qū)和緩沖區(qū)的描述結(jié)構(gòu)(sk_buff結(jié)構(gòu))是兩種不同的實(shí)體,這就意味著,在分配一個(gè)緩沖區(qū)時(shí),需要分配兩塊內(nèi)存(一個(gè)是緩沖區(qū),一個(gè)是緩沖區(qū)的描述結(jié)構(gòu)sk_buff)。 alloc_skb調(diào)用函數(shù)kmem_cache_alloc從緩存中獲取一個(gè)sk_buff結(jié)構(gòu),并調(diào)用kmalloc分配緩沖區(qū)(如果有緩存的話,它同樣從緩存中獲取內(nèi)存)。簡(jiǎn)化后的代碼如下: skb = kmem_cache_alloc(skbuff_head_cache, gfp_mask & _ _GFP_DMA); . . . size = SKB_DATA_ALIGN(size); data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask);在調(diào)用kmalloc前,size參數(shù)通過(guò)SKB_DATA_ALIGN宏強(qiáng)制對(duì)齊。在函數(shù)返回前,它會(huì)初始化結(jié)構(gòu)中的一些變量,最后的結(jié)構(gòu)如圖5所示。在圖5右邊所示的內(nèi)存塊的底部,你能看到對(duì)齊操作所帶來(lái)的填充區(qū)域。 Figure 5. alloc_skb functiondev_alloc_skb也是一個(gè)緩沖區(qū)分配函數(shù),它主要被設(shè)備驅(qū)動(dòng)使用,通常用在中斷上下文中。這是一個(gè)alloc_skb函數(shù)的包裝函數(shù),它會(huì)在請(qǐng)求分配的大小上增加16字節(jié)的空間以優(yōu)化緩沖區(qū)的讀寫(xiě)效率,它的分配要求使用原子操作(GFP_ATOMIC),這是因?yàn)樗窃谥袛嗵幚砗瘮?shù)中被調(diào)用的。 static inline struct sk_buff *dev_alloc_skb(unsigned int length) return _ _dev_alloc_skb(length, GFP_ATOMIC);static inlinestruct sk_buff *_ _dev_alloc_skb(unsigned int length, int gfp_mask) struct sk_buff *skb = alloc_skb(length + 16, gfp_mask); if (likely(skb) skb_reserve(skb, 16); return skb;如果沒(méi)有體系架構(gòu)相關(guān)的實(shí)現(xiàn),缺省使用_dev_alloc_skb的實(shí)現(xiàn)。 5.2. Freeing memory: kfree_skb and dev_kfree_skb這兩個(gè)函數(shù)釋放緩沖區(qū),并把它返回給緩沖池(緩存)。kfree_skb可以直接調(diào)用,也可以通過(guò)包裝函數(shù)dev_kfree_skb調(diào)用。后面這個(gè)函數(shù)一般被設(shè)備驅(qū)動(dòng)使用,與之功能相反的函數(shù)是dev_alloc_skb。dev_kfree_skb僅是一個(gè)簡(jiǎn)單的宏,它什么都不做,只簡(jiǎn)單地調(diào)用kfree_skb。這些函數(shù)只有在skb-users為1地情況下才釋放內(nèi)存(沒(méi)有人引用這個(gè)結(jié)構(gòu))。否則,它只是簡(jiǎn)單地減小 skb-users。如果緩沖區(qū)有三個(gè)引用者,那么只有第三次調(diào)用dev_kfree_skb或kfree_skb時(shí)才釋放內(nèi)存。 圖6中的流程圖顯示了分配一個(gè)緩沖區(qū)所需要的步驟。當(dāng)sk_buff釋放后,dst_release同樣會(huì)被調(diào)用以減小相關(guān)dst_entry數(shù)據(jù)結(jié)構(gòu)的引用計(jì)數(shù)。 如果destructor被初始化過(guò),相應(yīng)的函數(shù)會(huì)在此時(shí)被調(diào)用.在圖5中,我們看到,一個(gè)簡(jiǎn)單的場(chǎng)景是:一個(gè)sk_buff結(jié)構(gòu)與另一個(gè)內(nèi)存塊相關(guān),這個(gè)內(nèi)存塊里存儲(chǔ)的是真正的數(shù)據(jù)。當(dāng)然,內(nèi)存塊底部的skb_shared_info數(shù)據(jù)結(jié)構(gòu)可以包含指向其他分片的指針(參見(jiàn)圖5)。如果存在分片,kfree_skb同樣會(huì)釋放這些分片所占用的內(nèi)存。最后,kfree_skb 把sk_buff結(jié)構(gòu)返回給skbuff_head_cache緩存。 5.3. Data reservation and alignment: skb_reserve, skb_put, skb_push, and skb_pullskb_reserve可以在緩沖區(qū)的頭部預(yù)留一定的空間,它通常被用來(lái)在緩沖區(qū)中插入?yún)f(xié)議頭或者在某個(gè)邊界上對(duì)齊。這個(gè)函數(shù)改變data和tail指針,而data和tail指針?lè)謩e指向負(fù)載的開(kāi)頭和結(jié)尾,圖4(d)展示了調(diào)用skb_reserve(skb,n)的結(jié)果。這個(gè)函數(shù)通常在分配緩沖區(qū)之后就調(diào)用,此時(shí)的 data和tail指針還是指向同一個(gè)地方。 如果你查看某個(gè)以太網(wǎng)設(shè)備驅(qū)動(dòng)的收包函數(shù)(例如,drivers/net/3c59x.c中的vortex_rx), 你就會(huì)發(fā)現(xiàn)它在分配緩沖區(qū)之后,在向緩沖區(qū)中填充數(shù)據(jù)之前,會(huì)調(diào)用下面的函數(shù): skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */Figure 6. kfree_skb function由于以太網(wǎng)幀的頭部長(zhǎng)度是14個(gè)八位組,這個(gè)函數(shù)把緩沖區(qū)的頭部指針向后移動(dòng)了2個(gè)字節(jié)。這樣,緊跟在以太網(wǎng)頭部之后的IP頭部在緩沖區(qū)中存儲(chǔ)時(shí)就可以在16字節(jié)的邊界上對(duì)齊。如圖7所示。 Figure 7. (a) before skb_reserve, (b) after skb_reserve, and (c) after copying the frame on the buffer圖8展示了一個(gè)在發(fā)送過(guò)程中使用skb_reserve的例子。 Figure 8. Buffer that is filled in while traversing the stack from the TCP layer down to the link layer1. 當(dāng)TCP發(fā)送數(shù)據(jù)時(shí),它根據(jù)一些條件分配一個(gè)緩沖區(qū)(比如,TCP的最大分段長(zhǎng)度(mss),是否支持散讀散寫(xiě)I/O等2. TCP在緩沖區(qū)的頭部預(yù)留足夠的空間(用skb_reserve)用于填充各層的頭部(如TCP,IP,鏈路層等)。MAX_TCP_HEADER參數(shù)是各層頭部長(zhǎng)度的總和,它考慮了最壞的情況:由于tcp層不知道將要用哪個(gè)接口發(fā)送包,它為每一層預(yù)留了最大的頭部長(zhǎng)度。它甚至考慮了出現(xiàn)多個(gè)IP頭的可能性(如果內(nèi)核編譯支持IP over IP, 我們就會(huì)遇到多個(gè)IP頭的情況)。 3. 把TCP的負(fù)載拷貝到緩沖區(qū)。需要注意的是:圖8只是一個(gè)例子。TCP的負(fù)載可能會(huì)被組織成其他形式。例如它可以存儲(chǔ)到分片中。4. TCP層添加自己的頭部。 5. TCP層把緩沖區(qū)傳遞給IP層,IP層同樣添加自己的頭部。 6. IP層把緩沖區(qū)傳遞給鄰居層,鄰居層添加鏈路層頭部。 當(dāng)緩沖區(qū)在協(xié)議棧中向下層傳遞時(shí),每一層都把skb-data指針向下移動(dòng),然后拷貝自己的頭部,同時(shí)更新skb-len。這些操作都使用圖4中所展示的函數(shù)完成。 skb_reserve函數(shù)并沒(méi)有把數(shù)據(jù)移出或移入緩沖區(qū),它只是簡(jiǎn)單地更新了緩沖區(qū)的兩個(gè)指針,這兩個(gè)指針在圖4(d)中有描述。 static inline void skb_reserve(struct sk_buff *skb, unsigned int len) skb-data+=len; skb-tail+=len;skb_push在緩沖區(qū)的開(kāi)頭加入一塊數(shù)據(jù),而skb_put在緩沖區(qū)的末尾加入一塊數(shù)據(jù)。與skb_reserve類似,這些函數(shù)都不會(huì)真的往緩沖區(qū)中添加數(shù)據(jù),相反,它們只是移動(dòng)緩沖區(qū)的頭指針和尾指針。數(shù)據(jù)由其他函數(shù)拷貝到緩沖區(qū)中。skb_pull通過(guò)把head指針往前移來(lái)在緩沖區(qū)的頭部刪除一塊數(shù)據(jù)。圖4展示了這些函數(shù)是如何工作的。 2.1.5.4. The skb_shared_info structure and the skb_shinfo function如圖5所示,在緩沖區(qū)數(shù)據(jù)的末尾,有一個(gè)數(shù)據(jù)結(jié)構(gòu)skb_shared_info,它保存了數(shù)據(jù)塊的附加信息。這個(gè)數(shù)據(jù)結(jié)構(gòu)緊跟在end指針?biāo)傅牡刂分?end指針指示數(shù)據(jù)的末尾)。下面是這個(gè)結(jié)構(gòu)的定義: struct skb_shared_info atomic_t dataref; unsigned int nr_frags; unsigned short tso_size; unsigned short tso_seqs; struct sk_buff *frag_list; skb_frag_t fragsMAX_SKB_FRAGS;dataref表示數(shù)據(jù)塊的“用戶”數(shù),這個(gè)值在下一節(jié)(克隆和拷貝緩沖區(qū))中有描述。nf_frags,frag_list和frags用于存儲(chǔ)IP分片。skb_is_nonlinear函數(shù)用于測(cè)試一個(gè)緩沖區(qū)是否是分片的,而skb_linearize可以把分片組合成一個(gè)單一的緩沖區(qū)。組合分片涉及到數(shù)據(jù)拷貝,它將嚴(yán)重影響系統(tǒng)性能。 有些網(wǎng)卡硬件可以完成一些傳統(tǒng)上由CPU完成的任務(wù)。最常見(jiàn)的例子就是計(jì)算L3和L4校驗(yàn)和。有些網(wǎng)卡甚至可以維護(hù)L4協(xié)議的狀態(tài)機(jī)。在下面的例子中,我們主要討論TCP段卸載TCP segmentation offload,這些網(wǎng)卡實(shí)現(xiàn)了TCP層的一部分功能。tso_size和tso_seqs就在這種情況下使用。 需要注意的是:sk_buff中沒(méi)有指向skb_shared_info結(jié)構(gòu)的指針。如果要訪問(wèn)這個(gè)結(jié)構(gòu), 就需要使用skb_info宏,這個(gè)宏簡(jiǎn)單地返回end指針: #define skb_shinfo(SKB) (struct skb_shared_info *)(SKB)-end)下面的語(yǔ)句展示了如何使用這個(gè)宏來(lái)增加結(jié)構(gòu)中的某個(gè)成員變量的值: skb_shinfo(skb)-dataref+;2.1.5.5. Cloning and copying buffers如果一個(gè)緩沖區(qū)需要被不同的用戶獨(dú)立地操作,而這些用戶可能會(huì)修改sk_buff中某些變量的值(比如h和nh值),內(nèi)核沒(méi)有必要為每個(gè)用戶復(fù)制一份完整的sk_buff以及相應(yīng)的緩沖區(qū)。相反,為提高性能,內(nèi)核克隆一個(gè)緩沖區(qū)。克隆過(guò)程只復(fù)制sk_buff結(jié)構(gòu), 同時(shí)修改緩沖區(qū)的引用計(jì)數(shù)以避免共享的數(shù)據(jù)被提前釋放??寺【彌_區(qū)使用skb_clone函數(shù)。 一個(gè)使用包克隆的場(chǎng)景是:一個(gè)接收包的過(guò)程需要把這個(gè)包傳遞給多個(gè)接收者,例如包處理函數(shù)或者一個(gè)或多個(gè)網(wǎng)絡(luò)模塊。 被克隆的sk_buff不會(huì)放在任何鏈表中,同時(shí)也不會(huì)有到socket的引用。原始的和克隆的sk_buff中的skb-cloned值都被置為1。克隆包的skb-users值被置為1,這樣,在釋放時(shí),可以先釋放sk_buff結(jié)構(gòu)。同時(shí),緩沖區(qū)的引用計(jì)數(shù)(dataref)增加1(因?yàn)橛卸鄠€(gè)sk_buff結(jié)構(gòu)指向它)。圖9展示了克隆緩沖區(qū)的例子。 Figure 9. skb_clone functionskb_cloned函數(shù)可以用來(lái)測(cè)試
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 數(shù)字化車間與智能生產(chǎn)線的構(gòu)建
- 新型農(nóng)業(yè)經(jīng)營(yíng)模式的科技支撐體系
- 慢性疼痛管理中的運(yùn)動(dòng)療法應(yīng)用研究
- 優(yōu)化家居電商關(guān)懷
- 老舊供水管網(wǎng)更新改造工程項(xiàng)目總體規(guī)劃
- 編程語(yǔ)言探秘之旅
- 鍋爐維修協(xié)議書(shū)
- 2025年救護(hù)車項(xiàng)目規(guī)劃申請(qǐng)報(bào)告模板
- 《2025工程建設(shè)項(xiàng)目合同補(bǔ)充協(xié)議》
- 家風(fēng)家訓(xùn)主題班會(huì)課件
- 《新能源材料概論》 課件 第4章 力電轉(zhuǎn)換新能源材料
- 精力管理與時(shí)間管理
- 熱力站基礎(chǔ)知識(shí)培訓(xùn)
- 2025年1月浙江省高考地理試卷(含答案)
- 古典詩(shī)詞的藝術(shù)美與吟誦知到智慧樹(shù)章節(jié)測(cè)試課后答案2024年秋浙江廣廈建設(shè)職業(yè)技術(shù)大學(xué)
- 創(chuàng)傷性休克并發(fā)癥護(hù)理
- 準(zhǔn)零剛度非線性低頻隔振器理論研究及應(yīng)用
- 《預(yù)制高強(qiáng)混凝土風(fēng)電塔筒生產(chǎn)技術(shù)規(guī)程》文本附編制說(shuō)明
- 品牌傳播策略考核試卷
- 2023-2024學(xué)年廣東省深圳市寶安區(qū)七年級(jí)下學(xué)期期末歷史試題及答案
- 《蜻蜓介紹》課件
評(píng)論
0/150
提交評(píng)論