Linux 網路收包流程

来源:https://www.cnblogs.com/edisonfish/archive/2023/07/24/17578159.html
-Advertisement-
Play Games

哈嘍大家好,我是鹹魚 我們在跟別人網上聊天的時候,有沒有想過你發送的信息是怎麼傳到對方的電腦上的 又或者我們在上網衝浪的時候,有沒有想過 HTML 頁面是怎麼顯示在我們的電腦屏幕上的 無論是我們跟別人聊天還是上網衝浪,其實都依靠於電腦網路這項技術 > 電腦網路是指將多台電腦通過通信設備和傳輸介 ...


哈嘍大家好,我是鹹魚

我們在跟別人網上聊天的時候,有沒有想過你發送的信息是怎麼傳到對方的電腦上的

又或者我們在上網衝浪的時候,有沒有想過 HTML 頁面是怎麼顯示在我們的電腦屏幕上的

無論是我們跟別人聊天還是上網衝浪,其實都依靠於電腦網路這項技術

電腦網路是指將多台電腦通過通信設備和傳輸介質連接在一起,使得它們之間能夠相互通信、資源共用和協同工作

而電腦之間是通過數據包來實現信息傳輸和信息交換的,數據包是電腦網路中傳輸數據的基本單位

今天鹹魚將以 Linux 為例來給大家介紹一下 Linux 是如何實現網路接收數據包

網路協議棧&網路架構

在正文開始之前,我們先來瞭解一下 Linux 中的網路協議模型和網路子系統

  • 網路協議模型(網路協議棧)

在 Linux 中,Linux 網路協議棧分成了五層
image
其中:

  • 應用層提供 socket 介面來供用戶進程訪問內核空間的網路協議棧

  • 傳輸層、網路層協議由 Linux 內核網路協議棧實現

  • 鏈路層協議靠網卡驅動來實現

  • 物理層協議由硬體網卡實現
    image

  • 網路子系統(網路架構)

網路子系統是 Linux 內核中的一部分,由多個模塊和驅動程式組成,它負責管理和控制系統的網路功能以實現網路通信

通過 Linux 網路子系統(網路架構)來實現上述網路協議模型

image

其中

  • System call interface:為應用程式獲取內核的網路系統提供了介面,例如 socket
  • Protocol agnostic interface:為和各種傳輸層協議的網路交互提供的一層公共介面
  • Network protocals:對各種傳輸層協議的實現,如 TCP、UDP、IP 等
  • Device agnostic interface:為各種底層網路設備抽象出的公共介面,與各種網路設備驅動連接在一起
  • Device drivers:與各種網路設備交互的驅動

收包過程

當 Linux 接收一個數據包的時候,這個包是怎麼經過 Linux 的內核從而被應用程式拿到的呢?
image

  • 到達網卡(NIC,Network Interface Card)

首先數據包到達網卡之後,網卡會校驗接收到的數據包中的目的 MAC 地址是不是自己的 MAC 地址,如果不是的話通常就會丟棄掉

這種只接受發送給自己的數據包(其餘的扔掉)的工作模式稱為非混雜模式(Non-Promiscuous Mode)

混雜模式(Promiscuous Mode)則是網卡會接收通過網路傳輸的所有數據包,而不僅僅是發送給它自己的數據包

非混雜模式是網卡預設的工作模式,可以儘可能的保護網路安全和減少網路負載

網卡在校驗完 MAC 地址之後還會校驗數據幀(Data Frame)中校驗欄位 FCS 來一次確保接收到的數據包是正確的

  • 網卡硬體緩衝區 ——> 系統記憶體(ring buffer)

當網卡接收到數據包時,它將數據包的內容存儲在硬體緩衝區中,然後通過 DMA 將接收到的數據從硬體緩衝區傳輸到系統記憶體中的指定位置,這個位置通常是一個環形緩衝區( ring buffer)

DMA(直接記憶體訪問,Direct Memory Access)

DMA是一種數據傳輸技術,允許外設(如網卡、硬碟控制器、顯卡等)直接訪問電腦記憶體,而無需經過 CPU

通過 DMA 可以大大提高數據傳輸的效率,減輕 CPU 的負擔

  • 觸發硬中斷

當網卡將數據包 DMA 到用於接收的環形緩衝區(rx_ring)之後,就會觸發一個硬中斷來告訴 CPU 數據包收到了

什麼時候會觸發一個硬中斷,可以通過下麵的參數來進行配置:

  • rx-usecs:當過這麼長時間過後,一個中斷就會被產生
  • rx-frames:當累計接收到這麼多個數據幀後,一個中斷就會被產生

上面的參數配置可以通過下麵的命令來查看

# 以 CentOS 7 為例
ethtool -c <網卡名稱>

當 ring buffer 滿了之後,新來的數據包將給丟棄

ifconfig 查看網卡的時候,可以裡面有個 overruns,表示因為環形隊列滿而被丟棄的包

CPU 收到硬中斷之後就會停止手中的活,保存上下文,然後去調用網卡驅動註冊的硬中斷處理函數

為數據包分配 skb_buff ,並將接收到的數據拷貝到 skb_buff 緩衝區中

當一個數據包經過了網卡引起中斷之後,每一個包都會在記憶體中分配一塊區域,稱為 sk_buff (套接字緩存,socket buffer )

sk_buff 是 Linux 網路的一個核心數據結構

  • 觸發軟中斷

網卡的硬中斷處理函數處理完之後驅動先 disable 硬中斷,然後 enable 軟中斷

ps:待 ring buffer 中的所有數據包被處理完成後,enable 網卡的硬中斷,這樣下次網卡再收到數據的時候就會通知 CPU

內核負責軟中斷進程 ksoftirqd 發現有軟中斷請求到來,進行下麵的一些操作

# 查看軟中斷進程
[root@localhost ~]# ps -ef | grep ksoftirqd

調用 net_rx_action 函數

它會通過 poll 函數去 rx_ring 中拿數據幀,獲取的時候順便把 rx_ring 上的數據給刪除

static void net_rx_action(struct softirq_action *h)
{
    struct softnet_data *sd = &__get_cpu_var(softnet_data);
    unsigned long time_limit = jiffies + 2;
    int budget = netdev_budget;
    void *have;

    local_irq_disable();

    while (!list_empty(&sd->poll_list)) {
        ......
        n = list_first_entry(&sd->poll_list, struct napi_struct, poll_list);

        work = 0;
        if (test_bit(NAPI_STATE_SCHED, &n->state)) {
            work = n->poll(n, weight);
            trace_napi_poll(n);
        }

        budget -= work;
    }
}

除此之外,poll 函數會把 ring buffer 中的數據包轉換成內核網路模塊能夠識別的 skb 格式(即 socket kernel buffer

socket kernel buffer (skb) 是 Linux 內核網路棧處理網路包(packets)所使用的 buffer,它的類型是 sk_buffer

3、最後進入 netif _receive_skb 處理流程,它是數據鏈路層接收數據幀的最後一關

根據註冊在全局數組 ptype_allptype_base 里的網路層數據幀類型去調用第三層協議的接收函數處理

例如對於 ip 包來講,就會進入到 ip_rcv;如果是 arp 包的話,會進入到 arp_rcv

  • 到達網路層(以 IP 協議為例)

IP 層的入口函數在 ip_rcv 函數,調用 ip_rcv 函數進入三層協議棧

首先會對數據包進行各種檢查(檢查 IP Header),然後調用 netfilter 中的鉤子函數: NF_INET_PRE_ROUTING

netfilter: 是 Linux 內核中進行數據包過濾,連接跟蹤(Connect Track),網路地址轉換(NAT)等功能的主要實現框架

該框架在網路協議棧處理數據包的關鍵流程中定義了一系列鉤子點(Hook 點),併在這些鉤子點中註冊一系列函數對數據包進行處理

這些註冊在鉤子點的函數即為設置在網路協議棧內的數據包通行策略,也就意味著,這些函數可以決定內核是接受還是丟棄某個數據包

NF_INET_PRE_ROUTING 會根據預設的規則對數據包進行判斷並根據判斷結果做相關的處理(修改或者丟棄數據包)

處理完成後,數據包交由 ip_rcv_finish 處理,該函數根據路由判決結果,決定數據包是交由本機上層應用處理,還是需要進行轉發

如果是交由本機處理,則會交由 ip_local_deliver 本地上交流程;如果需要轉發,則交由 ip_forward 函數走轉發流程

  • 到達傳輸層(以 TCP 為例)

傳輸層 TCP 處理入口在 tcp_v4_rcv 函數,首先檢查數據包的 TCP 頭部等信息,確保數據包的完整性和正確性

然後去查找該數據包對應的已經打開的 socket ,如果找不到匹配的 socket,表示該數據包不屬於任何一個已建立的連接,因此該數據包會被丟棄

如果找到了匹配的 socket,TCP 會進一步檢查該 socket 和連接的狀態,如果狀態正常,TCP 會將數據包從內核傳輸到用戶空間,放入 socket 的接收緩衝區(socket receive buffer)

  • 應用層獲取數據

當數據包到達操作系統內核的傳輸層時,應用程式可以從套接字的接收緩衝區(socket receive buffer)中讀取數據包

一般有兩種方式讀取數據,一種是 recvfrom 函數阻塞在那裡等著數據來,這種情況下當 socket 收到通知後,recvfrom 就會被喚醒,然後讀取接收隊列的數據

另一種是通過 epoll 或者 select 監聽相應的 socket,當收到通知後,再調用 recvfrom 函數去讀取接收隊列的數據

總結

網路模塊可以說是 Linux 內核中最複雜的模塊了

看起來一個簡簡單單的收包過程就涉及到許多內核組件之間的交互,如網卡驅動、協議棧,內核ksoftirqd 線程等

鹹魚原本打算把收包和發包的流程都寫上的,但是光是寫收包流程就就要了我半條命了,等下次有機會把發包的流程也寫一下

總結一下 Linux 網路收包流程:

  • 數據到達網卡之後,網卡通過 DMA 將數據放到記憶體分配好的一塊 ring buffer 中,然後觸發硬中斷
  • CPU 收到硬中斷之後簡單的處理了一下(分配 skb_buffer),然後觸發軟中斷
  • 軟中斷進程 ksoftirqd 執行一系列操作(例如把數據幀從 ring ruffer上取下來)然後將數據送到三層協議棧中
  • 在三層協議棧中數據被進一步處理髮送到四層協議棧
  • 在四層協議棧中,數據會從內核拷貝到用戶空間,供應用程式讀取
  • 最後被處在應用層的應用程式去讀取

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • # 背景 公司最近的業務大量涉及安可項目,要求避免使用第三方組件,原有開發框架支持本地文件存儲/Minio/各類雲存儲,現在要求文件獨立存儲且文件服務需要自研,經調研評估後決定基於SpringBoot開發文件存儲服務,使用s3協議標準,這樣可以直接使用aws-sdk接入無需再開發客戶端,且安全安全性 ...
  • ![image.png](https://cdn.nlark.com/yuque/0/2023/png/2548312/1690078539162-4a2c1ab0-6ab8-4c04-b83b-b15517f0df8a.png#averageHue=%23040100&clientId=u8654 ...
  • ### 歡迎訪問我的GitHub > 這裡分類和彙總了欣宸的全部原創(含配套源碼):[https://github.com/zq2599/blog_demos](https://github.com/zq2599/blog_demos) ### 本篇概覽 - 前文咱們曾提到過幾種啟動方式,有一種用m ...
  • [toc] # 一、爬取目標 您好,我是[@馬哥python說](https://www.zhihu.com/people/13273183132),一名10年程式猿。 本次爬取的目標是:[抖音熱榜](https://www.douyin.com/hot) ![抖音熱榜頁面](https://img ...
  • # 簡介 主流的識別庫主要有ZXing.NET和ZBar,OpenCV 4.0後加入了QR碼檢測和解碼功能。本文使用的是ZBar,同等條件下ZBar識別率更高,圖片和部分代碼參考[在C#中使用ZBar識別條形碼](https://www.cnblogs.com/w2206/p/7755656.htm ...
  • # Avalonia中用FluentAvalonia+DialogHost.Avalonia實現界面彈窗和對話框 本文是項目中關於 **`彈窗界面`** 設計的技術分享,通過 **`FluentAvalonia`+`DialogHost.Avalonia`** 開源nuget包來實現項目中需要 ** ...
  • # 面試常考:C# 委托(delegate、Action、Func、predicate)和事件 剛開始工作的時候,覺得委托和事件有些神秘,而當你理解他們之後,也覺得好像沒有想象中的那麼難,這篇博文算是自己對委托和事件的一次梳理和總結。 ## 二、委托 C#中的委托,相當於C++中的指針函數,但委托是 ...
  • 大家好,我是 god23bin,歡迎回到咱們的《**一分鐘學一個 Linux 命令**》系列,今天我要講的是一個比較**危險**的命令,rm 命令,沒錯,你可以沒聽過 rm 命令,但是**刪庫跑路**你不可能沒聽過吧?什麼?沒聽過,沒事,現在你就聽過了,我剛剛已經講了,哈哈哈。好了,廢話不多說,現在... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...