0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

深度解析Linux網(wǎng)絡(luò)路徑及sk_buff struct 數(shù)據(jù)結(jié)構(gòu)

454398 ? 來(lái)源:博客園 ? 作者: SammyLiu ? 2020-10-22 15:04 ? 次閱讀

理解 Linux 網(wǎng)絡(luò)棧(1):Linux 網(wǎng)絡(luò)協(xié)議棧簡(jiǎn)單總結(jié)

本系列文章總結(jié) Linux 網(wǎng)絡(luò)棧,包括:

(1)Linux 網(wǎng)絡(luò)協(xié)議??偨Y(jié)

(2)非虛擬化Linux環(huán)境中的網(wǎng)絡(luò)分段卸載技術(shù) GSO/TSO/UFO/LRO/GRO

(3)QEMU/KVM + VxLAN 環(huán)境下的 Segmentation Offloading 技術(shù)(發(fā)送端)

(4)QEMU/KVM + VxLAN 環(huán)境下的 Segmentation Offloading 技術(shù)(接收端)

1. Linux 網(wǎng)絡(luò)路徑

1.1 發(fā)送端

1.1.1 應(yīng)用層

(1) Socket

應(yīng)用層的各種網(wǎng)絡(luò)應(yīng)用程序基本上都是通過(guò) Linux Socket 編程接口來(lái)和內(nèi)核空間的網(wǎng)絡(luò)協(xié)議棧通信的。Linux Socket 是從 BSD Socket 發(fā)展而來(lái)的,它是 Linux 操作系統(tǒng)的重要組成部分之一,它是網(wǎng)絡(luò)應(yīng)用程序的基礎(chǔ)。從層次上來(lái)說(shuō),它位于應(yīng)用層,是操作系統(tǒng)為應(yīng)用程序員提供的 API,通過(guò)它,應(yīng)用程序可以訪問(wèn)傳輸層協(xié)議。

socket 位于傳輸層協(xié)議之上,屏蔽了不同網(wǎng)絡(luò)協(xié)議之間的差異

socket 是網(wǎng)絡(luò)編程的入口,它提供了大量的系統(tǒng)調(diào)用,構(gòu)成了網(wǎng)絡(luò)程序的主體

在Linux系統(tǒng)中,socket 屬于文件系統(tǒng)的一部分,網(wǎng)絡(luò)通信可以被看作是對(duì)文件的讀取,使得我們對(duì)網(wǎng)絡(luò)的控制和對(duì)文件的控制一樣方便。

(2) 應(yīng)用層處理流程

網(wǎng)絡(luò)應(yīng)用調(diào)用Socket APIsocket (int family, int type, int protocol)創(chuàng)建一個(gè) socket,該調(diào)用最終會(huì)調(diào)用 Linux system callsocket() ,并最終調(diào)用 Linux Kernel 的 sock_create() 方法。該方法返回被創(chuàng)建好了的那個(gè) socket 的 file descriptor。對(duì)于每一個(gè) userspace 網(wǎng)絡(luò)應(yīng)用創(chuàng)建的 socket,在內(nèi)核中都有一個(gè)對(duì)應(yīng)的 struct socket和 struct sock。其中,struct sock 有三個(gè)隊(duì)列(queue),分別是rx , tx 和 err,在sock 結(jié)構(gòu)被初始化的時(shí)候,這些緩沖隊(duì)列也被初始化完成;在收據(jù)收發(fā)過(guò)程中,每個(gè) queue 中保存要發(fā)送或者接受的每個(gè) packet 對(duì)應(yīng)的 Linux 網(wǎng)絡(luò)棧 sk_buffer 數(shù)據(jù)結(jié)構(gòu)的實(shí)例 skb。

對(duì)于 TCP socket 來(lái)說(shuō),應(yīng)用調(diào)用 connect()API ,使得客戶端和服務(wù)器端通過(guò)該 socket 建立一個(gè)虛擬連接。在此過(guò)程中,TCP 協(xié)議棧通過(guò)三次握手會(huì)建立 TCP 連接。默認(rèn)地,該 API 會(huì)等到 TCP 握手完成連接建立后才返回。在建立連接的過(guò)程中的一個(gè)重要步驟是,確定雙方使用的 Maxium Segemet Size (MSS)。因?yàn)?UDP 是面向無(wú)連接的協(xié)議,因此它是不需要該步驟的。

應(yīng)用調(diào)用 Linux Socket 的 send 或者 write API 來(lái)發(fā)出一個(gè) message 給接收端

sock_sendmsg 被調(diào)用,它使用 socket descriptor 獲取 sock struct,創(chuàng)建 message header 和 socket control message

_sock_sendmsg 被調(diào)用,根據(jù) socket 的協(xié)議類(lèi)型,調(diào)用相應(yīng)協(xié)議的發(fā)送函數(shù)。

對(duì)于 TCP ,調(diào)用 tcp_sendmsg 函數(shù)。

對(duì)于 UDP 來(lái)說(shuō),userspace 應(yīng)用可以調(diào)用 send()/sendto()/sendmsg() 三個(gè) system call 中的任意一個(gè)來(lái)發(fā)送 UDP message,它們最終都會(huì)調(diào)用內(nèi)核中的 udp_sendmsg() 函數(shù)。

1.1.2 傳輸層

傳輸層的最終目的是向它的用戶提供高效的、可靠的和成本有效的數(shù)據(jù)傳輸服務(wù),主要功能包括 (1)構(gòu)造 TCP segment (2)計(jì)算 checksum (3)發(fā)送回復(fù)(ACK)包 (4)滑動(dòng)窗口(sliding windown)等保證可靠性的操作。TCP 協(xié)議棧的大致處理過(guò)程如下圖所示:

TCP 棧簡(jiǎn)要過(guò)程:

tcp_sendmsg 函數(shù)會(huì)首先檢查已經(jīng)建立的 TCP connection 的狀態(tài),然后獲取該連接的 MSS,開(kāi)始 segement 發(fā)送流程。

構(gòu)造 TCP 段的 playload:它在內(nèi)核空間中創(chuàng)建該 packet 的 sk_buffer 數(shù)據(jù)結(jié)構(gòu)的實(shí)例 skb,從 userspace buffer 中拷貝 packet 的數(shù)據(jù)到 skb 的 buffer。

構(gòu)造 TCP header。

計(jì)算 TCP 校驗(yàn)和(checksum)和 順序號(hào) (sequence number)。

TCP 校驗(yàn)和是一個(gè)端到端的校驗(yàn)和,由發(fā)送端計(jì)算,然后由接收端驗(yàn)證。其目的是為了發(fā)現(xiàn)TCP首部和數(shù)據(jù)在發(fā)送端到接收端之間發(fā)生的任何改動(dòng)。如果接收方檢測(cè)到校驗(yàn)和有差錯(cuò),則TCP段會(huì)被直接丟棄。TCP校驗(yàn)和覆蓋 TCP 首部和 TCP 數(shù)據(jù)。

TCP的校驗(yàn)和是必需的

發(fā)到 IP 層處理:調(diào)用 IP handler 句柄 ip_queue_xmit,將 skb 傳入 IP 處理流程。

UDP 棧簡(jiǎn)要過(guò)程:

UDP 將 message 封裝成 UDP 數(shù)據(jù)報(bào)

調(diào)用 ip_append_data() 方法將 packet 送到 IP 層進(jìn)行處理。

1.1.3 IP 網(wǎng)絡(luò)層 - 添加header 和 checksum,路由處理,IP fragmentation

網(wǎng)絡(luò)層的任務(wù)就是選擇合適的網(wǎng)間路由和交換結(jié)點(diǎn), 確保數(shù)據(jù)及時(shí)傳送。網(wǎng)絡(luò)層將數(shù)據(jù)鏈路層提供的幀組成數(shù)據(jù)包,包中封裝有網(wǎng)絡(luò)層包頭,其中含有邏輯地址信息- -源站點(diǎn)和目的站點(diǎn)地址的網(wǎng)絡(luò)地址。其主要任務(wù)包括 (1)路由處理,即選擇下一跳 (2)添加 IP header(3)計(jì)算 IP header checksum,用于檢測(cè) IP 報(bào)文頭部在傳播過(guò)程中是否出錯(cuò) (4)可能的話,進(jìn)行 IP 分片(5)處理完畢,獲取下一跳的 MAC 地址,設(shè)置鏈路層報(bào)文頭,然后轉(zhuǎn)入鏈路層處理。

IP 頭:

IP ?;咎幚磉^(guò)程如下圖所示:

首先,ip_queue_xmit(skb)會(huì)檢查skb->dst路由信息。如果沒(méi)有,比如套接字的第一個(gè)包,就使用ip_route_output()選擇一個(gè)路由。

接著,填充IP包的各個(gè)字段,比如版本、包頭長(zhǎng)度、TOS等。

中間的一些分片等,可參閱相關(guān)文檔?;舅枷胧牵?dāng)報(bào)文的長(zhǎng)度大于mtu,gso的長(zhǎng)度不為0就會(huì)調(diào)用 ip_fragment 進(jìn)行分片,否則就會(huì)調(diào)用ip_finish_output2把數(shù)據(jù)發(fā)送出去。ip_fragment 函數(shù)中,會(huì)檢查 IP_DF 標(biāo)志位,如果待分片IP數(shù)據(jù)包禁止分片,則調(diào)用icmp_send()向發(fā)送方發(fā)送一個(gè)原因?yàn)樾枰制O(shè)置了不分片標(biāo)志的目的不可達(dá)ICMP報(bào)文,并丟棄報(bào)文,即設(shè)置IP狀態(tài)為分片失敗,釋放skb,返回消息過(guò)長(zhǎng)錯(cuò)誤碼。

接下來(lái)就用 ip_finish_ouput2 設(shè)置鏈路層報(bào)文頭了。如果,鏈路層報(bào)頭緩存有(即hh不為空),那就拷貝到skb里。如果沒(méi),那么就調(diào)用neigh_resolve_output,使用 ARP 獲取。

1.1.4 數(shù)據(jù)鏈路層

功能上,在物理層提供比特流服務(wù)的基礎(chǔ)上,建立相鄰結(jié)點(diǎn)之間的數(shù)據(jù)鏈路,通過(guò)差錯(cuò)控制提供數(shù)據(jù)幀(Frame)在信道上無(wú)差錯(cuò)的傳輸,并進(jìn)行各電路上的動(dòng)作系列。數(shù)據(jù)鏈路層在不可靠的物理介質(zhì)上提供可靠的傳輸。該層的作用包括:物理地址尋址、數(shù)據(jù)的成幀、流量控制、數(shù)據(jù)的檢錯(cuò)、重發(fā)等。在這一層,數(shù)據(jù)的單位稱為幀(frame)。數(shù)據(jù)鏈路層協(xié)議的代表包括:SDLC、HDLC、PPP、STP、幀中繼等。

實(shí)現(xiàn)上,Linux 提供了一個(gè) Network device 的抽象層,其實(shí)現(xiàn)在 linux/net/core/dev.c。具體的物理網(wǎng)絡(luò)設(shè)備在設(shè)備驅(qū)動(dòng)中(driver.c)需要實(shí)現(xiàn)其中的虛函數(shù)。Network Device 抽象層調(diào)用具體網(wǎng)絡(luò)設(shè)備的函數(shù)。

、

1.1.5 物理層 - 物理層封裝和發(fā)送

物理層在收到發(fā)送請(qǐng)求之后,通過(guò) DMA 將該主存中的數(shù)據(jù)拷貝至內(nèi)部RAM(buffer)之中。在數(shù)據(jù)拷貝中,同時(shí)加入符合以太網(wǎng)協(xié)議的相關(guān)header,IFG、前導(dǎo)符和CRC。對(duì)于以太網(wǎng)網(wǎng)絡(luò),物理層發(fā)送采用CSMA/CD,即在發(fā)送過(guò)程中偵聽(tīng)鏈路沖突。

一旦網(wǎng)卡完成報(bào)文發(fā)送,將產(chǎn)生中斷通知CPU,然后驅(qū)動(dòng)層中的中斷處理程序就可以刪除保存的 skb 了。

1.1.6 簡(jiǎn)單總結(jié)

(來(lái)源)

1.2 接收端

1.2.1 物理層和數(shù)據(jù)鏈路層

簡(jiǎn)要過(guò)程:

一個(gè) package 到達(dá)機(jī)器的物理網(wǎng)絡(luò)適配器,當(dāng)它接收到數(shù)據(jù)幀時(shí),就會(huì)觸發(fā)一個(gè)中斷,并將通過(guò) DMA 傳送到位于 linux kernel 內(nèi)存中的 rx_ring。

網(wǎng)卡發(fā)出中斷,通知 CPU 有個(gè) package 需要它處理。中斷處理程序主要進(jìn)行以下一些操作,包括分配 skb_buff 數(shù)據(jù)結(jié)構(gòu),并將接收到的數(shù)據(jù)幀從網(wǎng)絡(luò)適配器I/O端口拷貝到skb_buff 緩沖區(qū)中;從數(shù)據(jù)幀中提取出一些信息,并設(shè)置 skb_buff 相應(yīng)的參數(shù),這些參數(shù)將被上層的網(wǎng)絡(luò)協(xié)議使用,例如skb->protocol;

終端處理程序經(jīng)過(guò)簡(jiǎn)單處理后,發(fā)出一個(gè)軟中斷(NET_RX_SOFTIRQ),通知內(nèi)核接收到新的數(shù)據(jù)幀。

內(nèi)核 2.5 中引入一組新的 API 來(lái)處理接收的數(shù)據(jù)幀,即 NAPI。所以,驅(qū)動(dòng)有兩種方式通知內(nèi)核:(1) 通過(guò)以前的函數(shù)netif_rx;(2)通過(guò)NAPI機(jī)制。該中斷處理程序調(diào)用 Network device的 netif_rx_schedule 函數(shù),進(jìn)入軟中斷處理流程,再調(diào)用 net_rx_action 函數(shù)。

該函數(shù)關(guān)閉中斷,獲取每個(gè) Network device 的 rx_ring 中的所有 package,最終 pacakage 從 rx_ring 中被刪除,進(jìn)入 netif _receive_skb 處理流程。

netif_receive_skb 是鏈路層接收數(shù)據(jù)報(bào)的最后一站。它根據(jù)注冊(cè)在全局?jǐn)?shù)組 ptype_all 和 ptype_base 里的網(wǎng)絡(luò)層數(shù)據(jù)報(bào)類(lèi)型,把數(shù)據(jù)報(bào)遞交給不同的網(wǎng)絡(luò)層協(xié)議的接收函數(shù)(INET域中主要是ip_rcv和arp_rcv)。該函數(shù)主要就是調(diào)用第三層協(xié)議的接收函數(shù)處理該skb包,進(jìn)入第三層網(wǎng)絡(luò)層處理。

1.2.2 網(wǎng)絡(luò)層

IP 層的入口函數(shù)在 ip_rcv 函數(shù)。該函數(shù)首先會(huì)做包括 package checksum 在內(nèi)的各種檢查,如果需要的話會(huì)做 IP defragment(將多個(gè)分片合并),然后 packet 調(diào)用已經(jīng)注冊(cè)的 Pre-routing netfilter hook ,完成后最終到達(dá) ip_rcv_finish 函數(shù)。

ip_rcv_finish 函數(shù)會(huì)調(diào)用 ip_router_input 函數(shù),進(jìn)入路由處理環(huán)節(jié)。它首先會(huì)調(diào)用 ip_route_input 來(lái)更新路由,然后查找 route,決定該 package 將會(huì)被發(fā)到本機(jī)還是會(huì)被轉(zhuǎn)發(fā)還是丟棄:

如果是發(fā)到本機(jī)的話,調(diào)用 ip_local_deliver 函數(shù),可能會(huì)做 de-fragment(合并多個(gè) IP packet),然后調(diào)用 ip_local_deliver 函數(shù)。該函數(shù)根據(jù) package 的下一個(gè)處理層的 protocal number,調(diào)用下一層接口,包括 tcp_v4_rcv (TCP), udp_rcv (UDP),icmp_rcv (ICMP),igmp_rcv(IGMP)。對(duì)于 TCP 來(lái)說(shuō),函數(shù) tcp_v4_rcv 函數(shù)會(huì)被調(diào)用,從而處理流程進(jìn)入 TCP 棧。

如果需要轉(zhuǎn)發(fā) (forward),則進(jìn)入轉(zhuǎn)發(fā)流程。該流程需要處理 TTL,再調(diào)用 dst_input 函數(shù)。該函數(shù)會(huì) (1)處理 Netfilter Hook (2)執(zhí)行 IP fragmentation (3)調(diào)用dev_queue_xmit,進(jìn)入鏈路層處理流程。

1.2.3 傳輸層 (TCP/UDP)

傳輸層 TCP 處理入口在 tcp_v4_rcv 函數(shù)(位于 linux/net/ipv4/tcp ipv4.c 文件中),它會(huì)做 TCP header 檢查等處理。

調(diào)用 _tcp_v4_lookup,查找該 package 的 open socket。如果找不到,該 package 會(huì)被丟棄。接下來(lái)檢查 socket 和 connection 的狀態(tài)。

如果socket 和 connection 一切正常,調(diào)用 tcp_prequeue 使 package 從內(nèi)核進(jìn)入 user space,放進(jìn) socket 的 receive queue。然后 socket 會(huì)被喚醒,調(diào)用 system call,并最終調(diào)用 tcp_recvmsg 函數(shù)去從 socket recieve queue 中獲取 segment。

1.2.4 接收端 - 應(yīng)用層

每當(dāng)用戶應(yīng)用調(diào)用 read 或者 recvfrom 時(shí),該調(diào)用會(huì)被映射為/net/socket.c 中的 sys_recv 系統(tǒng)調(diào)用,并被轉(zhuǎn)化為 sys_recvfrom 調(diào)用,然后調(diào)用 sock_recgmsg 函數(shù)。

對(duì)于 INET 類(lèi)型的 socket,/net/ipv4/af inet.c 中的inet_recvmsg 方法會(huì)被調(diào)用,它會(huì)調(diào)用相關(guān)協(xié)議的數(shù)據(jù)接收方法。

對(duì) TCP 來(lái)說(shuō),調(diào)用 tcp_recvmsg。該函數(shù)從 socket buffer 中拷貝數(shù)據(jù)到 user buffer。

對(duì) UDP 來(lái)說(shuō),從 user space 中可以調(diào)用三個(gè) system call recv()/recvfrom()/recvmsg() 中的任意一個(gè)來(lái)接收 UDP package,這些系統(tǒng)調(diào)用最終都會(huì)調(diào)用內(nèi)核中的 udp_recvmsg 方法。

1.2.5 報(bào)文接收過(guò)程簡(jiǎn)單總結(jié)

2. Linux sk_buff struct 數(shù)據(jù)結(jié)構(gòu)和隊(duì)列(Queue)

2.1 sk_buff

(本章節(jié)摘選自http://amsekharkernel.blogspot.com/2014/08/what-is-skb-in-linux-kernel-what-are.html)

2.1.1 sk_buff 是什么

當(dāng)網(wǎng)絡(luò)包被內(nèi)核處理時(shí),底層協(xié)議的數(shù)據(jù)被傳送更高層,當(dāng)數(shù)據(jù)傳送時(shí)過(guò)程反過(guò)來(lái)。由不同協(xié)議產(chǎn)生的數(shù)據(jù)(包括頭和負(fù)載)不斷往下層傳遞直到它們最終被發(fā)送。因?yàn)檫@些操作的速度對(duì)于網(wǎng)絡(luò)層的表現(xiàn)至關(guān)重要,內(nèi)核使用一個(gè)特定的結(jié)構(gòu)叫 sk_buff,其定義文件在skbuffer.h。Socket buffer被用來(lái)在網(wǎng)絡(luò)實(shí)現(xiàn)層交換數(shù)據(jù)而不用拷貝來(lái)或去數(shù)據(jù)包 –這顯著獲得速度收益。

sk_buff 是 Linux 網(wǎng)絡(luò)的一個(gè)核心數(shù)據(jù)結(jié)構(gòu),其定義文件在skbuffer.h。

socket kernel buffer (skb) 是 Linux 內(nèi)核網(wǎng)絡(luò)棧(L2 到 L4)處理網(wǎng)絡(luò)包(packets)所使用的 buffer,它的類(lèi)型是 sk_buffer。簡(jiǎn)單來(lái)說(shuō),一個(gè) skb 表示 Linux 網(wǎng)絡(luò)棧中的一個(gè) packet;TCP 分段和 IP 分組生產(chǎn)的多個(gè) skb 被一個(gè) skb list 形式來(lái)保存。

struct sock 有三個(gè) skb 隊(duì)列(sk_buffer queue),分別是rx , tx 和 err。

它的主要結(jié)構(gòu)成員:

struct sk_buff {
    /* These two members must be first. */ # packet 可以存在于 list 或者 queue 中,這兩個(gè)成員用于鏈表處理
    struct sk_buff        *next;
    struct sk_buff        *prev;
    struct sk_buff_head    *list; #該 packet 所在的 list
 ...
    struct sock        *sk;      #跟該 skb 相關(guān)聯(lián)的 socket
    struct timeval        stamp; # packet 發(fā)送或者接收的時(shí)間,主要用于 packet sniffers
    struct net_device    *dev;  #這三個(gè)成員跟蹤該 packet 相關(guān)的 devices,比如接收它的設(shè)備等
    struct net_device    *input_dev;
    struct net_device    *real_dev;

    union {                  #指向各協(xié)議層 header 結(jié)構(gòu)
        struct tcphdr    *th;
        struct udphdr    *uh;
        struct icmphdr    *icmph;
        struct igmphdr    *igmph;
        struct iphdr    *ipiph;
        struct ipv6hdr    *ipv6h;
        unsigned char    *raw;
    } h;

    union {
        struct iphdr    *iph;
        struct ipv6hdr    *ipv6h;
        struct arphdr    *arph;
        unsigned char    *raw;
    } nh;

    union {
        unsigned char    *raw;
    } mac;

    struct  dst_entry    *dst; #指向該 packet 的路由目的結(jié)構(gòu),告訴我們它會(huì)被如何路由到目的地
    char            cb[40];    # SKB control block,用于各協(xié)議層保存私有信息,比如 TCP 的順序號(hào)和幀的重發(fā)狀態(tài)
    unsigned int        len, #packet 的長(zhǎng)度
                data_len,
                mac_len,       # MAC header 長(zhǎng)度
                csum;          # packet 的 checksum,用于計(jì)算保存在 protocol header 中的校驗(yàn)和。發(fā)送時(shí),當(dāng) checksum offloading 時(shí),不設(shè)置;接收時(shí),可以由device計(jì)算

    unsigned char        local_df, #用于 IPV4 在已經(jīng)做了分片的情況下的再分片,比如 IPSEC 情況下。
                cloned:1, #在 skb 被 cloned 時(shí)設(shè)置,此時(shí),skb 各成員是自己的,但是數(shù)據(jù)是shared的
                nohdr:1,  #用于支持 TSO
                pkt_type, #packet 類(lèi)型
                ip_summed; # 網(wǎng)卡能支持的校驗(yàn)和計(jì)算的類(lèi)型,NONE 表示不支持,HW 表示支持,

    __u32            priority; #用于 QoS
    unsigned short        protocol, # 接收 packet 的協(xié)議
                security;

2.1.2 skb 的主要操作

(1)分配skb = alloc_skb(len, GFP_KERNEL)

(2)添加 payload(skb_put(skb, user_data_len))

(3)使用skb->push 添加 protocol header,或者 skb->pull 刪除 header

2.2 Linux 網(wǎng)絡(luò)棧使用的驅(qū)動(dòng)隊(duì)列 (driver queue)

(本章節(jié)摘選自Queueing in the Linux Network StackbyDan Siemon)

2.2.1 隊(duì)列

在 IP 棧和 NIC 驅(qū)動(dòng)之間,存在一個(gè) driver queue (驅(qū)動(dòng)隊(duì)列)。典型地,它被實(shí)現(xiàn)為 FIFO ring buffer,簡(jiǎn)單地可以認(rèn)為它是固定大小的。這個(gè)隊(duì)列不包含 packet data,相反,它只是保存 socket kernel buffer (skb)的指針,而 skb 的使用如上節(jié)所述是貫穿內(nèi)核網(wǎng)絡(luò)棧處理過(guò)程的始終的。

該隊(duì)列的輸入時(shí) IP 棧處理完畢的 packets。這些packets 要么是本機(jī)的應(yīng)用產(chǎn)生的,要么是進(jìn)入本機(jī)又要被路由出去的。被 IP 棧加入隊(duì)列的 packets 會(huì)被網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)(hardware driver)取出并且通過(guò)一個(gè)數(shù)據(jù)通道(data bus)發(fā)到 NIC 硬件設(shè)備并傳輸出去。

在不使用 TSO/GSO 的情況下,IP 棧發(fā)到該隊(duì)列的 packets 的長(zhǎng)度必須小于 MTU。

2.2.2 skb 大小 - 默認(rèn)最大大小為 NIC MTU

絕大多數(shù)的網(wǎng)卡都有一個(gè)固定的最大傳輸單元(maximum transmission unit, MTU)屬性,它是該網(wǎng)絡(luò)設(shè)備能夠傳輸?shù)淖畲髱╢rame)的大小。對(duì)以太網(wǎng)來(lái)說(shuō),默認(rèn)值為 1500 bytes,但是有些以太網(wǎng)絡(luò)可以支持巨幀(jumbo frame),最大能到 9000 bytes。在 IP 網(wǎng)絡(luò)棧內(nèi),MTU 表示能發(fā)給 NIC 的最大 packet 的大小。比如,如果一個(gè)應(yīng)用向一個(gè) TCP socket 寫(xiě)入了 2000 bytes 數(shù)據(jù),那么 IP 棧需要?jiǎng)?chuàng)建兩個(gè) IP packets 來(lái)保持每個(gè) packet 的大小等于或者小于 1500 bytes??梢?jiàn),對(duì)于大數(shù)據(jù)傳輸,相對(duì)較小的 MTU 會(huì)導(dǎo)致產(chǎn)生大量的小網(wǎng)絡(luò)包(small packets)并被傳入 driver queue。這成為 IP 分片 (IP fragmentation)。

下圖表示 payload 為 1500 bytes 的 IP 包,在 MTU 為 1000 和 600 時(shí)候的分片情況:

備注:

以上資料是從網(wǎng)絡(luò)上獲取的各種資料整理而來(lái)

這一塊本身就比較復(fù)雜,而且不同的 linux 內(nèi)核的版本之間也有差異,文中的內(nèi)容還需要進(jìn)一步加工,錯(cuò)誤在所難免。

編輯:hfy

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11171

    瀏覽量

    208480
  • Socket
    +關(guān)注

    關(guān)注

    0

    文章

    188

    瀏覽量

    34604
  • 網(wǎng)絡(luò)協(xié)議

    關(guān)注

    3

    文章

    258

    瀏覽量

    21485
  • 數(shù)據(jù)結(jié)構(gòu)

    關(guān)注

    3

    文章

    569

    瀏覽量

    40063
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    Linux sk_buff四大指針與相關(guān)操作

     在以上文章中,沒(méi)有分析過(guò)Linux內(nèi)核網(wǎng)絡(luò)關(guān)鍵的數(shù)據(jù)結(jié)構(gòu)-套接字數(shù)據(jù)緩存struct sk_buff
    發(fā)表于 10-13 17:23 ?4293次閱讀
    <b class='flag-5'>Linux</b> <b class='flag-5'>sk_buff</b>四大指針與相關(guān)操作

    深度數(shù)據(jù)結(jié)構(gòu)的疑問(wèn)

    你好,我對(duì)深度數(shù)據(jù)結(jié)構(gòu)(STDepthTableControl)有一些疑問(wèn):typedef結(jié)構(gòu){ uint32_t depthUnits; int32_t depthClampMin
    發(fā)表于 11-08 11:09

    嵌入式linux TCP/IP協(xié)議棧概述

    包包頭。3. 數(shù)據(jù)傳輸數(shù)據(jù)的傳輸使用了一個(gè)非常重要的結(jié)構(gòu)sk_buff,該結(jié)構(gòu)體用來(lái)實(shí)現(xiàn)數(shù)據(jù)
    發(fā)表于 12-07 10:05

    Linux內(nèi)核中的數(shù)據(jù)結(jié)構(gòu)的一點(diǎn)認(rèn)識(shí)

    大家都知道linux內(nèi)核是世界上優(yōu)秀的軟件之一,作為一款優(yōu)秀的軟件,其中的許多的設(shè)計(jì)都精妙之處,十分值得學(xué)習(xí)和借鑒。今天我們就帶大家看一下內(nèi)核中的數(shù)據(jù)結(jié)構(gòu)中一點(diǎn)設(shè)計(jì)。打開(kāi)內(nèi)核源碼中的 include
    發(fā)表于 04-20 16:42

    LINUX 進(jìn)程源代碼分析

    LINUX 進(jìn)程源代碼分析 task_struct 數(shù)據(jù)結(jié)構(gòu)表示進(jìn)程的數(shù)據(jù)結(jié)構(gòu)struct task_
    發(fā)表于 02-09 15:13 ?16次下載

    Linux網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)程序

    當(dāng)要發(fā)送數(shù)據(jù)包的時(shí)候,內(nèi)核必須建立一個(gè)包含傳輸數(shù)據(jù)sk_buff,然后將sk_buff交給下層,各層在sk_buff遞交給下一層,各層在
    發(fā)表于 05-10 11:15 ?1800次閱讀

    Linux發(fā)送HTTP網(wǎng)絡(luò)包圖像 sk_buff數(shù)據(jù)結(jié)構(gòu)解析

    如果你對(duì)Linux是如何實(shí)現(xiàn) 對(duì)用戶原始的網(wǎng)絡(luò)包進(jìn)行協(xié)議頭封裝與解析,為什么會(huì)粘包拆包,期間網(wǎng)絡(luò)包經(jīng)歷了哪些緩沖區(qū)、經(jīng)歷了幾次拷貝(CPU、DMA),TCP又是如何實(shí)現(xiàn)滑動(dòng)/擁塞窗口
    的頭像 發(fā)表于 05-10 12:14 ?2147次閱讀

    網(wǎng)卡的Ring Buffer詳解

    DMA 將 NIC 接收的數(shù)據(jù)包逐個(gè)寫(xiě)入 sk_buff ,一個(gè)數(shù)據(jù)包可能占用多個(gè) sk_buff , sk_buff 讀寫(xiě)順序遵循FIFO
    的頭像 發(fā)表于 03-17 14:25 ?1429次閱讀

    Linux內(nèi)核的鏈表數(shù)據(jù)結(jié)構(gòu)

    Linux內(nèi)核實(shí)現(xiàn)了自己的鏈表數(shù)據(jù)結(jié)構(gòu),它的設(shè)計(jì)與傳統(tǒng)的方式不同,非常巧妙也很通用。
    的頭像 發(fā)表于 03-24 11:34 ?795次閱讀
    <b class='flag-5'>Linux</b>內(nèi)核的鏈表<b class='flag-5'>數(shù)據(jù)結(jié)構(gòu)</b>

    網(wǎng)卡的Ring Buffer詳解

    DMA 將 NIC 接收的數(shù)據(jù)包逐個(gè)寫(xiě)入 sk_buff ,一個(gè)數(shù)據(jù)包可能占用多個(gè) sk_buff , sk_buff 讀寫(xiě)順序遵循FIFO
    的頭像 發(fā)表于 04-04 09:15 ?1071次閱讀

    sk_buff內(nèi)存空間布局情況與相關(guān)操作(一)

    套接字數(shù)據(jù)緩存(socket buffer)在Linux內(nèi)核中表示為:struct sk_buff,是Linux內(nèi)核中
    的頭像 發(fā)表于 07-30 16:43 ?1070次閱讀
    <b class='flag-5'>sk_buff</b>內(nèi)存空間布局情況與相關(guān)操作(一)

    sk_buff內(nèi)存空間布局情況與相關(guān)操作(二)

    操作tailroom中用戶數(shù)據(jù)塊區(qū)域:skb_put用于修改指向數(shù)據(jù)區(qū)末尾的指針tail: void *skb_put( struct sk_buff *skb, unsigned i
    的頭像 發(fā)表于 07-30 16:47 ?690次閱讀
    <b class='flag-5'>sk_buff</b>內(nèi)存空間布局情況與相關(guān)操作(二)

    sk_buff內(nèi)存空間布局情況與相關(guān)操作(三)

    2、非線性區(qū)域 在1、中,可以看到每張sk_buff的圖: 在end指針緊挨著一個(gè)非線性區(qū)域 ; 在struct sk_buff中沒(méi)有指向skb_shared_info結(jié)構(gòu)的指針,利用
    的頭像 發(fā)表于 07-30 16:48 ?1024次閱讀
    <b class='flag-5'>sk_buff</b>內(nèi)存空間布局情況與相關(guān)操作(三)

    linux設(shè)備模型數(shù)據(jù)結(jié)構(gòu)分析

    數(shù)據(jù)結(jié)構(gòu) 2.1 kobject kobject 代表內(nèi)核對(duì)象,結(jié)構(gòu)體本身不單獨(dú)使用,而是嵌套在其他高層結(jié)構(gòu)中,用于組織成拓?fù)潢P(guān)系; sysfs 文件系統(tǒng)中一個(gè)目錄對(duì)應(yīng)一個(gè) kobject ; 看看
    的頭像 發(fā)表于 09-28 14:44 ?460次閱讀
    <b class='flag-5'>linux</b>設(shè)備模型<b class='flag-5'>數(shù)據(jù)結(jié)構(gòu)</b>分析

    Linux GIC驅(qū)動(dòng)數(shù)據(jù)結(jié)構(gòu)分析

    數(shù)據(jù)結(jié)構(gòu)分析 先來(lái)張圖: GIC驅(qū)動(dòng)中,使用 struct gic_chip_data 結(jié)構(gòu)體來(lái)描述GIC控制器的信息,整個(gè)驅(qū)動(dòng)都是圍繞著該結(jié)構(gòu)體的初始化,驅(qū)動(dòng)中將函數(shù)指針都初始化好,
    的頭像 發(fā)表于 09-28 15:18 ?506次閱讀
    <b class='flag-5'>Linux</b> GIC驅(qū)動(dòng)<b class='flag-5'>數(shù)據(jù)結(jié)構(gòu)</b>分析