日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

之前咸魚在《linux 網絡收包流程》一文中介紹了 Linux 是如何實現網絡接收數據包的

 

簡單回顧一下:

  • 數據到達網卡之后,網卡通過 DMA 將數據放到內存分配好的一塊 ring buffer 中,然后觸發硬中斷

  • CPU 收到硬中斷之后簡單的處理了一下(分配 skb_buffer),然后觸發軟中斷

  • 軟中斷進程 ksoftirqd 執行一系列操作(例如把數據幀從 ring ruffer上取下來)然后將數據送到三層協議棧中

  • 在三層協議棧中數據被進一步處理發送到四層協議棧

  • 在四層協議棧中,數據會從內核拷貝到用戶空間,供應用程序讀取

  • 最后被處在應用層的應用程序去讀取

 

當 Linux 要發送一個數據包的時候,這個包是怎么從應用程序再到 Linux 的內核最后由網卡發送出去的呢?

 

那么今天咸魚將會為大家介紹 Linux 是如何實現網絡發送數據包

 

 
發包流程

 

假設我們的網卡已經啟動好(分配和初始化 RingBuffer) 且 server 和 client 已經建立好 socket

 

這里需要注意的是,網卡在啟動過程中申請分配的 RingBuffer 是有兩個:

  • igb_tx_buffer 數組:這個數組是內核使用的,用于存儲要發送的數據包描述信息,通過 vzalloc申請的

  • e1000_adv_tx_desc 數組:這個數組是網卡硬件使用的,用于存儲要發送的數據包,網卡硬件可以通過 DMA 直接訪問這塊內存,通過 dma_alloc_coherent分配

 

igb_tx_buffer 數組中的每個元素都有一個指針指向 e1000_adv_tx_desc

 

這樣內核就可以把要發送的數據填充到 e1000_adv_tx_desc 數組上

 

然后網卡硬件會直接從 e1000_adv_tx_desc 數組中讀取實際數據,并將數據發送到網絡上

 

圖片

 

 
拷貝到內核

 

  • socket 系統調用將數據拷貝到內核

應用程序首先通過 socket 提供的接口實現系統調用

 

我們在用戶態使用的 send 函數和 sendto 函數其實都是 sendto 系統調用實現的

 

send/sendto函數 只是為了用戶方便,封裝出來的一個更易于調用的方式而已

 

圖片

 

sendto 系統調用內部,首先 sockfd_lookup_light 函數會查找與給定文件描述符(fd)關聯的 socket

 

接著調用 sock_sendmsg 函數(sock_sendmsg ==> __sock_sendmsg ==> __sock_sendmsg_nosec

 

其中 sock->ops->sendmsg 函數實際執行的是 .NET_sendmsg 協議棧函數

圖片

 

這時候內核會去找 socket 上對應的具體協議發送函數

 

以 TCP 為例,具體協議發送函數為 tcp_sendmsg

圖片

 

tcp_sendmsg 會去申請一個內核態內存 skb(sk_buff) ,然后掛到發送隊列上(發送隊列是由 skb 組成的一個鏈表)

圖片

 

接著把用戶待發送的數據拷貝到 skb 中,拷貝之后會觸發【發送】操作

 

這里說的發送是指在當前上下文中,待發送數據從 socket 層發送到傳輸層

 

需要注意的是,這時候不一定開始真正發送,因為還要進行一些條件判斷(比如說發送隊列中的數據已經超過了窗口大小的一半)

 

只有滿足了條件才能夠發送,如果沒有滿足條件這次系統調用就可能直接返回了

 

 
網絡協議棧處理

 

  • 傳輸層處理

接著數據來到了傳輸層

 

傳輸層主要看 tcp_write_xmit 函數,這個函數處理了傳輸層的擁塞控制、滑動窗口相關的工作

 

該函數會根據發送窗口和最大段大小等因素計算出本次發送的數據大小,然后將數據封裝成 TCP 段并發送出去

 

如果滿足窗口要求,設置 TCP 頭然后將數據傳到更低的網絡層進行處理

 

在傳輸層中,內核主要做了兩件事:

  • 復制一份數據(skb)

為什么要復制一份出來呢?因為網卡發送完成之后,skb 會被釋放掉,但 TCP 協議是支持丟失重傳的

 

所以在收到對方的 ACK 之前必須要備份一個 skb 去為重傳做準備

 

實際上一開始發送的是 skb 的拷貝版,收到了對方的 ACK 之后系統才會把真正的 skb 刪除掉

 

  • 封裝 TCP 頭

系統會根據實際情況添加 TCP 頭封裝成 TCP 段

 

這里需要知道的是:每個 skb 內部包含了網絡協議中的所有頭部信息,例如 mac 頭、IP 頭、TCP/UDP 頭等

 

在設置這些頭部時,內核會通過調整指針的位置來填充相應的字段,而不是頻繁申請和拷貝內存

圖片

比如說在設置 TCP 頭的時候,只是把指針指向 skb 的合適位置。后面再設置 IP 頭的時候,再把指針挪一挪就行

 

這種方式利用了 skb 數據結構的鏈表特性可以避免內存分配和數據拷貝所帶來的性能開銷,從而提高數據傳輸的效率

 

  • 網絡層處理

數據離開了傳輸層之后,就來到了網絡層

 

網絡層主要做下面的事情:

  • 路由項查找:

根據目標 IP 地址查找路由表,確定數據包的下一跳(ip_queue_xmit 函數)

 

  • IP 頭設置:

根據路由表查找的結果,設置 IP 頭中的源和目標 IP 地址、TTL(生存時間)、IP 協議等字段

 

  • netfilter 過濾:

netfilter 是 Linux 內核中的一個框架,用于實現數據包的過濾和修改

 

在網絡層,netfilter 可以用于對數據包進行過濾、NAT(網絡地址轉換)等操作

 

  • skb 切分:

如果數據包的大小超過了 MTU(最大傳輸單元),需要將數據包進行切分成多個片段,以適應網絡傳輸,每個片段會被封裝成單獨的 skb

 

  • 數據鏈路層處理

當數據來到了數據鏈路層之后,會有兩個子系統協同工作,確保數據包在發送和接收過程中能夠正確地對數據進行封裝、解析和傳輸

 

  • 鄰居子系統

管理和維護主機或路由器與其它設備之間的鄰居關系

 

鄰居子系統里會發送 arp 請求找鄰居,然后把鄰居信息存在鄰居緩存表里,用于存儲目標主機的 MAC 地址

 

當需要發送數據包到某個目標主機時,數據鏈路層會首先查詢鄰居緩存表,以獲取目標主機的 MAC 地址,從而正確地封裝數據包(封裝 MAC 頭)

 

  • 網絡設備子系統

網絡設備子系統負責處理與物理網絡接口相關的操作,包括數據包的封裝和發送,以及從物理接口接收數據包并進行解析

 

網絡設備子系統不但處理數據包的格式轉換,如在以太網中添加幀頭和幀尾,以及從幀中提取數據

 

還負責處理硬件相關的操作,如發送和接收數據包的時鐘同步、物理層錯誤檢測等

 

  • 到達網卡發送隊列

接著網絡設備子系統會選擇一個合適的網卡發送隊列并把 skb 添加到隊列中(繞過軟中斷處理程序)

 

然后,內核會調用網卡驅動的入口函數 dev_hard_start_xmit 來觸發數據包的發送

 

在一些情況下,鄰居子系統還會將 skb 數據包添加到軟中斷隊列(softnet_data)上,并觸發軟中斷(NET_TX_SOFTIRQ)

 

這個過程是為了將 skb 數據包交給軟中斷處理程序進行進一步處理和發送。軟中斷處理程序會負責實際的數據包發送

 

這就是為什么一般服務器上查看 /proc/softirqs,一般 NET_RX 都要比 NET_TX 大的多的原因之一

 

即對于收包來說,都是要經過 NET_RX 軟中斷;而對于發包來說,只有某些情況下才觸發 NET_TX 軟中斷

 

 
數據發送

圖片

 

驅動程序從發送隊列中讀取 skb 的描述信息,將其掛到 RingBuffer 上(前面提到的igb_tx_buffer 數組)

 

接著將 skb 的描述信息映射到網卡可訪問的內存 DMA 區域中(前面提到的e1000_adv_tx_desc 數組)

 

網卡會直接從 e1000_adv_tx_desc 數組中根據描述信息讀取實際數據并將數據發送到網絡。這樣就完成了數據包的發送過程

 

 
收尾工作

當數據發送完成后,網卡設備會觸發一個中斷(NET_RX_SOFTIRQ),這個中斷通常稱為“發送完成中斷”或者“發送隊列清理中斷”

 

這個中斷的主要作用是執行發送完成的清理工作,包括釋放之前為數據包分配的內存,即釋放 skb 內存和 RingBuffer 內存

 

最后,當收到這個 TCP 報文的 ACK 應答時,傳輸層就會釋放原始的 skb(前面有講到發送的其實是 skb 的拷貝版)

 

可以看到,當數據發送完成以后,通過硬中斷的方式來通知驅動發送完畢,而這個中斷類型是 NET_RX_SOFTIRQ

 

前面我們講到過網卡收到一個網絡包的時候,會觸發 NET_RX_SOFTIRQ中斷去告訴 CPU 有數據要處理

 

也就是說,無論是網卡接收一個網絡包還是發送網絡包結束之后,觸發的都是  NET_RX_SOFTIRQ

 

 
總結

 

最后總結一下在 Linux 系統中發送網絡數據包的流程:

圖片

 

最后總結一下在 Linux 系統中發送網絡數據包的流程:

  • 傳輸層處理:以 TCP 為例,在傳輸層中會復制一份數據(為了丟失重傳),然后為數據封裝 TCP 頭

  • 網絡層處理:選取路由(確認下一跳的 IP)、填充 IP 頭、netfilter 過濾、對超過 MTU 大小的數據包進行分片等操作

  • 鄰居子系統和網絡設備子系統處理:在這里數據會被進一步處理和封裝,然后被添加到網卡的發送隊列中

  • 應用程序通過 socket 提供的接口進行系統調用,將數據從用戶態拷貝到內核態的 socket 緩沖區中

  • 網絡協議棧從 socket 緩沖區中拿取數據,并按照 TCP/IP 協議棧從上到下逐層處理

  • 驅動程序從發送隊列中讀取 skb 的描述信息然后掛在 RingBuffer 上,接著將 skb 的描述信息映射到網卡可訪問的內存 DMA 區域中

  • 網卡將數據發送到網絡

  • 當數據發送完成后觸發硬中斷,釋放  skb 內存和 RingBuffer 內存

分享到:
標簽:網絡
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定