博客 / 詳情

返回

網絡包的內核漂流記 Part 1 - 圖解網絡包接收流程

image.png

注,原文來自 https://blog.mygraphql.com/zh... 。如你看到的轉載圖片不清,請回到原文。

目錄

  • 目錄
  • 《網絡包的內核漂流記》系列介紹

    • 風格、樣式、本文的交互閲讀方式 📖
  • ISO 網絡模型
  • 網絡包接收流程概述
  • 網絡包接收步驟

    • 初始化與配置
    • IRQ 處理
    • SoftIRQ 中加載網絡包到內存
    • SoftIRQ 中推送網絡包到上層
  • 結尾
  • 相關

《網絡包的內核漂流記》系列介紹

大家知道,網絡數據來源於網線、光纖、無線電波上的比特(bit),然後到網卡,到內核,最後到應用進程 socket。事情好像很簡單。但如果是 SRE/DevOps 或是 Performance Engineer ,需要做細緻的監控和優化時,這些顯然是不夠的。引用本文主要參考作者的原話:

Optimizing and monitoring the network stack is impossible unless you carefully read and understand how it works. You cannot monitor code you don’t understand at a deep level.
除非您仔細閲讀並瞭解其工作原理,否則無法優化和監控網絡堆棧。 您無法深入監控您不理解的代碼。
—— Joe Damato

《網絡包的內核漂流記》嘗試分析和跟蹤一個網絡包在內核各子系統間的流轉和觸發的協作。

開始前先做個預告,《網絡包的內核漂流記》 系統(將)包括:

  • Part 1: 圖解網絡包接收流程
  • Part 2: BPF 跟蹤網絡包的內核漂流

    • 我將演示如何用 bpftrace 蹤網絡包的內核漂流。

為免嚇跑人,還是老套路,多圖少代碼。不過有的圖有點點複雜。🚜

本系列主要參考:Monitoring and Tuning the Linux Networking Stack: Receiving Data 。是的,這是篇被轉載、參考到氾濫的文章。幾乎你能在網上找到的所有講 Linux 網卡和網絡棧收發的文章,都會參考它。但有句話是:

一千個人眼中就有一千個 “哈姆雷特”。 —— 無名高人

風格、樣式、本文的交互閲讀方式 📖

雖然不是寫書,不過還是説明一下吧:

  1. 我不打算像八股文的 Linux 內核文章一樣,貼一堆源碼,讓文章看起來內容很豐滿但無趣。我用交互 SVG 圖片的的方法去引用源碼 😎 。
  2. https://blog.mygraphql.com/zh... 的原文是 SVG 圖片。如果你是在其它地方看到本文,請轉回原文。
  3. 正確瀏覽 SVG 圖片的姿勢是瀏覽器中圖片處右鍵,選擇 “新 Tab 中打開圖片” 。
  4. SVG 圖片可以點擊鏈接,直接跳轉到相應內核源碼網頁,精確到源碼行。 是的,你不需要 git clone那大陀源碼 🤠 ,只需要一個瀏覽器就可以。如果你在電腦前開雙屏,結合源碼和圖看,相信我,內核源碼不是什麼天書,你能看懂大部分的。
  5. 瀏覽內核源碼我用 https://elixir.bootlin.com/li... 。這個是很好的內核源碼瀏覽網站,內置源碼引用跳轉和搜索功能。

ISO 網絡模型

我並不太學院派,但回顧一下基礎知識還是必須的:

image.png

圖:OSI 基本參考模型和 TCP/IP 堆棧之間的邏輯映射,來自這裏

下文把第 n 層縮寫為 Ln,如 TRANSPORT 層是第 4 層,縮寫為 L4

網絡包接收流程概述

圖:網絡包接收流程概述

網絡包接收步驟

從代碼細節看,網絡包接收步驟比較多。下面劃分為 4 步來敍述:

  1. 初始化與配置
  2. IRQ 處理
  3. SoftIRQ 中加載網絡包到內存
  4. SoftIRQ 中推送網絡包到上層

初始化與配置

首先説説,內核啓動時,網絡子系統的初始化流程。
圖:初始化與配置

  1. 創建 ksoftirqd 內核線程(每個 CPU 一個)
  2. ksoftirqd 線程開始在 run_ksoftirqd 函數中執行它們的處理循環。
  3. 接下來,為每個 CPU 創建一個專用的 softnet_data 對象。。這些對象又引用了其它處理網絡數據的重要對象。其中之一是下文會提到 poll_list(輪詢列表)。 程序通過調用 napi_schedule()函數或來自設備驅動程序的其他 NAPI API 來添加 NAPI 到 poll_list中 。
  4. 然後,net_dev_init() 通過調用 open_softirq() 向 softirq 系統註冊 NET_RX_SOFTIRQ 軟中斷。註冊的處理函數稱為“net_rx_action()”。這是softirq內核線程將執行以處理數據包的函數。

不要心急,第 4 步以後的步驟,將在下文繼續。因需要鋪墊一下其它內容。

IRQ 處理

下面,從網卡在網絡上接收到數據開始,説説前期的包處理過程。
網絡包到達網卡觸發 IRQ,IRQ 處理程序完成網絡包處理前半部分,並觸發 SoftIR

  1. 網卡從網絡接收數據。
  2. NIC 使用 DMA 將網絡數據寫入 RAM。
  3. NIC 拉起 IRQ 位。
  4. 設備驅動之前註冊的IRQ處理程序被執行。
  5. 清除網卡上的 IRQ,以便它可以為新的數據包到達生成 IRQ。
  6. 通過調用 napi_schedule() 函數,異步觸發 NAPI softIRQ 輪詢循環。

有了以上鋪墊知識後,我們回頭看看上節的「初始化與配置」中未講解的步驟:

5. 驅動中對napi_schedule()的調用將驅動的NAPI poll對象添加到當前CPU的poll_list中。
6. 標記 softirq 拉起位,以便這個 CPU 上的 ksoftirqd 進程知道有數據包要處理。
7. run_ksoftirqd() 函數被調用(由ksoftirq 內核線程循環運行)執行。
8. __do_softirq() 被調用檢查softirq 拉起位,看到一個 softIRQ 拉起, 並調用拉起位相關的softIRQ註冊的處理程序:net_rx_action(),後續的網絡接收數據所有重要的工作將在這個函數中完成。

SoftIRQ 中加載網絡包到內存

圖:SoftIRQ 中加載網絡包到內存

這個圖比較簡單,不多説。

SoftIRQ 中推送網絡包到上層

圖:SoftIRQ 中推送網絡包到上層

Receive Packet Steering(RPS)被禁用的情況流程為:

1. netif_receive_skb() 將數據傳遞給 __netif_receive_core()
6. __netif_receive_core() 將數據傳送到 TAP(監聽模塊) (如 PCAP)。
7. __netif_receive_core() 將數據傳遞給已註冊的協議層處理程序。在大多數情況下,是 IPv4 協議棧註冊的 ip_rcv 函數。

使用Receive Packet Steering(RPS)的情況流程,這裏不説了,我暫時未分析到。

如果你心急,想了解 Receive Packet Steering(RPS) 是什麼鬼,那麼:

  • https://01.org/linuxgraphics/...
  • https://www.kernel.org/doc/Do...
  • https://www.alibabacloud.com/...

結尾

沒太多好説的,好好學習,天天向上。笨人做笨事,做最好的自己。

ending

相關

如果你關注雲原生,也使用到 Calico,那麼以下這篇兄弟文章你可能喜歡:

  • 一個IP包的旅行 —— K8s 網絡之 Calico 淺度解構
user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.