Linux 網絡發包流程

哈嘍大家好,我是咸魚

之前咸魚在《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 系統調用 省略了一些代碼 */
SYSCALL_DEFINE6(sendto, int, fd, void __user *, buff, size_t, len,unsigned int, flags, struct sockaddr __user *, addr,int, addr_len)
{...sock = sockfd_lookup_light(fd, &err, &fput_needed);...err = sock_sendmsg(sock, &msg, len);...	
}

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

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

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

/*
__sock_sendmsg_nosec 函數iocb:指向與 I/O 操作相關的結構體 kiocb
sock: 指向要執行發送操作的套接字結構體
msg: 指向存儲要發送數據的消息頭結構體 msghdr
size: 要發送的數據大小*/
static inline int __sock_sendmsg_nosec(struct kiocb *iocb, struct socket *sock,struct msghdr *msg, size_t size)
{...return sock->ops->sendmsg(iocb, sock, msg, size);
}

這時候內核會去找 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 系統中發送網絡數據包的流程:

  • 應用程序通過 socket 提供的接口進行系統調用,將數據從用戶態拷貝到內核態的 socket 緩沖區中
  • 網絡協議棧從 socket 緩沖區中拿取數據,并按照 TCP/IP 協議棧從上到下逐層處理
    • 傳輸層處理:以 TCP 為例,在傳輸層中會復制一份數據(為了丟失重傳),然后為數據封裝 TCP 頭
    • 網絡層處理:選取路由(確認下一跳的 IP)、填充 IP 頭、netfilter 過濾、對超過 MTU 大小的數據包進行分片等操作
    • 鄰居子系統和網絡設備子系統處理:在這里數據會被進一步處理和封裝,然后被添加到網卡的發送隊列中
  • 驅動程序從發送隊列中讀取 skb 的描述信息然后掛在 RingBuffer 上,接著將 skb 的描述信息映射到網卡可訪問的內存 DMA 區域中
  • 網卡將數據發送到網絡
  • 當數據發送完成后觸發硬中斷,釋放 skb 內存和 RingBuffer 內存

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/43028.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/43028.shtml
英文地址,請注明出處:http://en.pswp.cn/news/43028.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Lnton羚通關于Optimization在【PyTorch】中的基礎知識

OPTIMIZING MODEL PARAMETERS (模型參數優化) 現在我們有了模型和數據,是時候通過優化數據上的參數來訓練了,驗證和測試我們的模型。訓練一個模型是一個迭代的過程,在每次迭代中,模型會對輸出進行猜測&…

python3 0基礎學習----數據結構(基礎+練習)

python 0基礎學習筆記之數據結構 📚 幾種常見數據結構列表 (List)1. 定義2. 實例:3. 列表中常用方法.append(要添加內容) 向列表末尾添加數據.extend(列表) 將可迭代對象逐個添加到列表中.insert(索引,插入內容) 向指定…

8.17校招 內推 面經

綠泡泡: neituijunsir 交流裙,內推/實習/校招匯總表格 1、校招 | 騰訊2024校園招聘全面啟動(內推) 校招 | 騰訊2024校園招聘全面啟動(內推) 2、校招 | 大華股份2024屆全球校園招聘正式啟動(內推) 校招 | 大華股份2024屆全球校園招聘正式啟動(內推) …

國家一帶一路和萬眾創業創新的方針政策指引下,Live Market探索跨境產業的創新發展

現代社會,全球經濟互聯互通,跨境產業也因此而崛起。為了推動跨境產業的創新發展,中國政府提出了“一帶一路”和“萬眾創業、萬眾創新”的方針政策,旨在促進全球經濟的互聯互通和創新發展。在這個大環境下,Live Market積…

Mariadb高可用MHA

本節主要學習了Mariadb高可用MHA的概述,案例如何構建MHA 提示:以下是本篇文章正文內容,下面案例可供參考 一、概述 1、概念 MHA(MasterHigh Availability)是一套優秀的MySQL高可用環境下故障切換和主從復制的軟件。…

合宙Air724UG LuatOS-Air LVGL API--簡介

為何是 LVGL LVGL 是一個開源的圖形庫,它提供了創建嵌入式 GUI 所需的一切,具有易于使用的圖形元素、漂亮的視覺效果和低內存占用的特點。 LVGL特點: 強大的 控件 :按鈕、圖表、列表、滑動條、圖像等 高級圖形引擎:動…

BIO、NIO和AIO

一.引言 何為IO 涉及計算機核心(CPU和內存)與其他設備間數據遷移的過程,就是I/O。數據輸入到計算機內存的過程即輸入,反之輸出到外部存儲(比如數據庫,文件,遠程主機)的過程即輸出。 I/O 描述了計算機系統…

插入排序優化——超越歸并排序的超級算法

插入排序及優化 插入排序算法算法講解數據模擬代碼 優化思路一、二分查找二、copy函數 優化后代碼算法的用途題目:數星星(POJ2352 star)輸入輸出格式輸入格式:輸出格式 輸入輸出樣例輸入樣例輸出樣例 題目講解步驟如下AC 代碼 插入…

HIVE SQL實現分組字符串拼接concat

在Mysql中可以通過group_concat()函數實現分組字符串拼接,在HIVE SQL中可以使用concat_ws()collect_set()/collect_list()函數實現相同的效果。 實例: abc2014B92015A82014A102015B72014B6 1.concat_wscollect_list 非去重拼接 select a ,concat_ws(-…

Linux系統中基于NGINX的代理緩存配置指南

作為一名專業的爬蟲程序員,你一定知道代理緩存在加速網站響應速度方面的重要性。而使用NGINX作為代理緩存服務器,能夠極大地提高性能和效率。本文將為你分享Linux系統中基于NGINX的代理緩存配置指南,提供實用的解決方案,助你解決在…

C語言刷題訓練DAY.8

1.計算單位階躍函數 解題思路&#xff1a; 這個非常簡單&#xff0c;只需要if else語句即可完成 解題代碼&#xff1a; #include <stdio.h>int main() {int t 0;while(scanf("%d",&t)!EOF){if (t > 0)printf("1\n");else if (t < 0)pr…

大模型基礎02:GPT家族與提示學習

大模型基礎&#xff1a;GPT 家族與提示學習 從 GPT-1 到 GPT-3.5 GPT(Generative Pre-trained Transformer)是 Google 于2018年提出的一種基于 Transformer 的預訓練語言模型。它標志著自然語言處理領域從 RNN 時代進入 Transformer 時代。GPT 的發展歷史和技術特點如下: GP…

【校招VIP】java語言類和對象之map、set集合

考點介紹&#xff1a; map、set集合相關內容是校招面試的高頻考點之一。 map和set是一種專門用來進行搜索的容器或者數據結構&#xff0c;其搜索效率與其具體的實例化子類有關系。 『java語言類和對象之map、set集合』相關題目及解析內容可點擊文章末尾鏈接查看&#xff01; …

深入了解Maven(一)

目錄 一.Maven介紹與功能 二.依賴管理 1.依賴的配置 2.依賴的傳遞性 3.排除依賴 4.依賴的作用范圍 5.依賴的生命周期 一.Maven介紹與功能 maven是一個項目管理和構建工具&#xff0c;是基于對象模型POM實現。 Maven的作用&#xff1a; 便捷的依賴管理&#xff1a;使用…

springboot 使用zookeeper實現分布式隊列

一.添加ZooKeeper依賴&#xff1a;在pom.xml文件中添加ZooKeeper客戶端的依賴項。例如&#xff0c;可以使用Apache Curator作為ZooKeeper客戶端庫&#xff1a; <dependency><groupId>org.apache.curator</groupId><artifactId>curator-framework</…

【java安全】Log4j反序列化漏洞

文章目錄 【java安全】Log4j反序列化漏洞關于Apache Log4j漏洞成因CVE-2017-5645漏洞版本復現環境漏洞復現漏洞分析 CVE-2019-17571漏洞版本漏洞復現漏洞分析 參考 【java安全】Log4j反序列化漏洞 關于Apache Log4j Log4j是Apache的開源項目&#xff0c;可以實現對System.out…

英語——構詞法

按照語言一定的規律創造新詞的方法就叫作構詞法。英語中常見的構詞法包括六種:合成法、派生法、轉化法、混合法、截短法和首尾字母結合法。其中后三種將在第四節“縮寫和簡寫”中進行講解。 第一節 合成法 英語構詞法中把兩個單詞連在一起合成一個新詞,前一個詞修飾或限定后…

前端性能優化——包體積壓縮插件,打包速度提升插件,提升瀏覽器響應的速率模式

前端代碼優化 –其他的優化可以具體在網上搜索 壓縮項目打包后的體積大小、提升打包速度&#xff0c;是前端性能優化中非常重要的環節&#xff0c;結合工作中的實踐總結&#xff0c;梳理出一些 常規且有效 的性能優化建議 ue 項目可以通過添加–report命令&#xff1a; "…

innodb索引與算法

B樹主鍵插入 B樹在innodb的插入有三種模式page_last_insert, page_dirction, page_N_direction 而在bustub里面的B樹就是page_N_direction,如果是自增主鍵的話&#xff0c;就是上面這樣的插入法 FIC優化 (DDL) 選擇性統計 覆蓋索引 MMR ICP優化 自適應hash 全文索引 MySQL…

Rust之編寫自動化測試

1、測試函數的構成&#xff1a; 在最簡單的情形下,Rust中的測試就是一個標注有test屬性的函數。屬性 (attribute)是一種用于修飾Rust代碼的元數據。只需要將#[test]添加到關鍵字fn的上一行便可以將函數轉變為測試函數。當測試編寫完成后,我們可以使用cargo test命令來運行測試…