Linux 網絡棧剖析

M. Tim Jones ([email protected]), 顧問工程師, Emulex

Linux® 操作系統的最大特性之一就是它的網絡棧。它最初源於 BSD 的網絡棧,具有一套非常乾淨的接口,組織得非常好。其接口範圍從協議無關層(例如通用 socket 層接口或設備層)到各種網絡協議的具體層。本文將從分層角度對 Linux 網絡棧的接口進行探索,並介紹其中的一些主要結構。

協議簡介

雖然對於網絡的正式介紹一般都參考了 OSI(Open Systems Interconnection)模型,但是本文對 Linux 中基本網絡棧的介紹分爲四層的 Internet 模型(如圖 1 所示)。


圖 1. 網絡棧的 Internet 模型
網絡棧的 Internet 模型

這個棧的最底部是鏈路層。鏈路層是指提供對物理層訪問的設備驅動程序,這可以是各種介質,例如串口鏈路或以太網設備。鏈路層上面是網絡層,它負責將報文定向到目標位置。再上一層稱爲傳輸層,負責端到端的通信(例如,在一臺主機內部)。儘管網絡層負責管理主機之間的通信,但是傳輸層需要負責管理主機內部各端之間的通信。最後一層是應用層,它通常是一個語義層,能夠理解要傳輸的數據。例如,超文本傳輸協議(HTTP)就負責傳輸服務器和客戶機之間對 Web 內容的請求與響應。

實際來說,網絡棧的各個層次有一些更爲人所熟知的名字。在鏈路層上,可以找到以太網,這是最常用的一種高速介質。更早的鏈路層協議包括一些串口協議,例如 SLIP(Serial Line Internet Protocol)、CSLIP(Compressed SLIP)和PPP(Point-to-Point Protocol)。最常見的網絡層協議是 IP(Internet Protocol),但是網絡層中還存在一些滿足其他需求的協議,例如 ICMP(Internet Control Message Protocol)和ARP( Address Resolution Protocol)。在傳輸層上是 TCP(Transmission Control Protocol)和 UDP(User Datagram Protocol)。最後,應用層中包含很多大家都非常熟悉的協議,包括標準的 Web 協議 HTTP 和電子郵件協議 SMTP(Simple Mail Transfer Protocol)。





回頁首


核心網絡架構

現在繼續瞭解 Linux 網絡棧的架構以及如何實現這種 Internet 模型。圖 2 提供了 Linux 網絡棧的高級視圖。最上面是用戶空間層,或稱爲應用層,其中定義了網絡棧的用戶。底部是物理設備,提供了對網絡的連接能力(串口或諸如以太網之類的高速網絡)。中間是內核空間,即網絡子系統,也是本文介紹的重點。流經網絡棧內部的是 socket 緩衝區(sk_buffs),它負責在源和匯點之間傳遞報文數據。您很快就將看到 sk_buff 的結構。


圖 2. Linux 高級網絡棧架構
Linux 高級網絡棧架構

首先,讓我們來快速瀏覽一下 Linux 網絡子系統的核心元素,後續章節中會更詳細進行介紹。頂部(請參閱圖 2)是系統調用接口。它簡單地爲用戶空間的應用程序提供了一種訪問內核網絡子系統的方法。位於其下面的是一個協議無關層,它提供了一種通用方法來使用底層傳輸層協議。然後是實際協議,在 Linux 中包括內嵌的協議 TCP、UDP,當然還有 IP。然後是另外一個協議無關層,提供了與各個設備驅動程序通信的通用接口,最下面是設備驅動程序本身。





回頁首


系統調用接口

系統調用接口可以從兩個角度進行描述。用戶發起網絡調用時,通過系統調用接口進入內核的過程應該是多路的。最後調用 ./net/socket.c 中的 sys_socketcall 結束該過程,然後進一步將調用分路發送到指定目標。系統調用接口的另一種描述是使用普通文件操作作爲網絡 I/O。例如,典型的讀寫操作可以在網絡 socket 上執行(socket 使用一個文件描述符表示,與一個普通文件一樣)。因此,儘管有很多操作是網絡專用的(使用 socket 調用創建一個 socket,使用 connect 調用連接一個收信方,等等),但是也有一些標準的文件操作可以應用於網絡對象,就像操作普通文件一樣。最後,系統調用接口提供了在用戶空間應用程序和內核之間轉移控制的方法。





回頁首


協議無關接口

socket 層是一個協議無關接口,它提供了一組通用函數來支持各種不同協議。socket 層不但可以支持典型的 TCP 和 UDP 協議,而且還可以支持 IP、裸以太網和其他傳輸協議,例如 SCTP(Stream Control Transmission Protocol)。

通過網絡棧進行的通信都需要對 socket 進行操作。Linux 中的 socket 結構是 struct sock,這個結構是在 linux/include/net/sock.h 中定義的。這個巨大的結構中包含了特定 socket 所需要的所有狀態信息,其中包括 socket 所使用的特定協議和在 socket 上可以執行的一些操作。

網絡子系統可以通過一個定義了自己功能的特殊結構來了解可用協議。每個協議都維護了一個名爲 proto 的結構(可以在 linux/include/net/sock.h 中找到)。這個結構定義了可以在從 socket 層到傳輸層中執行特定的 socket 操作(例如,如何創建一個 socket,如何使用 socket 建立一個連接,如何關閉一個 socket 等等)。





回頁首


網絡協議

網絡協議這一節對一些可用的特定網絡協議作出了定義(例如 TCP、UDP 等)。它們都是在 linux/net/ipv4/af_inet.c 文件中一個名爲 inet_init 的函數中進行初始化的(因爲 TCP 和 UDP 都是 inet 簇協議的一部分)。 inet_init 函數使用 proto_register 函數來註冊每個內嵌協議。這個函數是在 linux/net/core/sock.c 中定義的,除了可以將這個協議添加到活動協議列表中之外,如果需要,該函數還可以選擇分配一到多個 slab 緩存。

通過 linux/net/ipv4/ 目錄中 udp.c 和 raw.c 文件中的 proto 接口,您可以瞭解各個協議是如何標識自己的。這些協議接口每個都按照類型和協議映射到 inetsw_array,該數組將內嵌協議與操作映射到一起。inetsw_array 結構及其關係如圖 3 所示。最初,會調用 inet_init 中的 inet_register_protosw 將這個數組中的每個協議都初始化爲 inetsw。函數 inet_init 也會對各個 inet 模塊進行初始化,例如 ARP、ICMP 和 IP 模塊,以及 TCP 和 UDP 模塊。


圖 3. Internet 協議數組結構
Internet 協議數組結構


Socket 協議的相互關係
回想以下在創建 socket 時,需要指定類型和協議,例如my_sock = socket( AF_INET, SOCK_STREAM, 0 )AF_INET 表示一個 Internet 地址簇,它使用的是一個流 socket,定義爲 SOCK_STREAM(如此處的 inetsw_array 所示)。

注意在 圖 3 中,proto 結構定義了傳輸特有的方法,而 proto_ops 結構則定義了通用的 socket 方法。可以通過調用 inet_register_protosw 將其他協議加入到 inetsw 協議中。例如,SCTP 就是通過調用 linux/net/sctp/protocol.c 中的 sctp_init 加入其中的。有關 SCTP 的更多信息,請參閱 參考資料 一節的內容。

socket 中的數據移動是使用一個所謂的 socket 緩衝區(sk_buff)的核心結構實現的。sk_buff 中包含了報文數據,以及涉及協議棧中多個層次的狀態數據。所發送或接收的每個報文都是使用一個 sk_buff 表示的。sk_buff 結構是在 linux/include/linux/skbuff.h 中定義的,如圖 4 所示。


圖 4. Socket 緩衝區及其與其他結構的關係
Socket 緩衝區及其與其他結構的關係

如圖所示,多個 sk_buff 可以針對某個給定連接鏈接在一起。每個 sk_buff 都在設備結構(net_device)中標識報文發送的目的地,或者接收報文的來源地。由於每個報文都是使用一個 sk_buff 表示的,因此報文頭都可以通過一組指針(thiphmac[用於 Media Access Control 或者 MAC 頭])方便地進行定位。由於 sk_buff 是 socket 數據管理的中心,因此創建了很多支持函數來對它們進行管理。其中有些函數用於創建和銷燬 sk_buff 結構,或對它進行克隆或排隊管理。

針對給定的 socket,Socket 緩衝區可以鏈接在一起,這樣可以包含衆多信息,包括到協議頭的鏈接、時間戳(報文是何時發送或接收的),以及與這個報文相關的設備。





回頁首


設備無關接口

協議層下面是另外一個無關接口層,它將協議與具有很多各種不同功能的硬件設備連接在一起。這一層提供了一組通用函數供底層網絡設備驅動程序使用,讓它們可以對高層協議棧進行操作。

首先,設備驅動程序可能會通過調用 register_netdeviceunregister_netdevice 在內核中進行註冊或註銷。調用者首先填寫 net_device 結構,然後傳遞這個結構進行註冊。內核調用它的 init 函數(如果定義了這種函數),然後執行一組健全性檢查,並創建一個 sysfs 條目,然後將新設備添加到設備列表中(內核中的活動設備鏈表)。在 linux/include/linux/netdevice.h 中可以找到這個 net_device 結構。這些函數都是在 linux/net/core/dev.c 中實現的。

要從協議層向設備中發送 sk_buff,就需要使用 dev_queue_xmit 函數。這個函數可以對 sk_buff 進行排隊,從而由底層設備驅動程序進行最終傳輸(使用 sk_buff 中引用的 net_devicesk_buff->dev 所定義的網絡設備)。dev 結構中包含了一個名爲 hard_start_xmit 的方法,其中保存有發起 sk_buff 傳輸所使用的驅動程序函數。

報文的接收通常是使用 netif_rx 執行的。當底層設備驅動程序接收一個報文(包含在所分配的 sk_buff 中)時,就會通過調用 netif_rxsk_buff 上傳至網絡層。然後,這個函數通過 netif_rx_schedulesk_buff 在上層協議隊列中進行排隊,供以後進行處理。可以在 linux/net/core/dev.c 中找到 dev_queue_xmitnetif_rx 函數。

最近,內核中引入了一種新的應用程序編程接口(NAPI),該接口允許驅動程序與設備無關層(dev)進行交互。有些驅動程序使用的是 NAPI,但是大多數驅動程序仍然在使用老式的幀接收接口(比例大約是 6 比 1)。NAPI 在高負載的情況下可以產生更好的性能,它避免了爲每個傳入的幀都產生中斷。





回頁首


設備驅動程序

網絡棧底部是負責管理物理網絡設備的設備驅動程序。例如,包串口使用的 SLIP 驅動程序以及以太網設備使用的以太網驅動程序都是這一層的設備。

在進行初始化時,設備驅動程序會分配一個 net_device 結構,然後使用必須的程序對其進行初始化。這些程序中有一個是 dev->hard_start_xmit,它定義了上層應該如何對 sk_buff 排隊進行傳輸。這個程序的參數爲 sk_buff。這個函數的操作取決於底層硬件,但是通常 sk_buff 所描述的報文都會被移動到硬件環或隊列中。就像是設備無關層中所描述的一樣,對於 NAPI 兼容的網絡驅動程序來說,幀的接收使用了 netif_rxnetif_receive_skb 接口。NAPI 驅動程序會對底層硬件的能力進行一些限制。有關更詳細的信息,請參閱 參考資料 一節的內容。

設備驅動程序在 dev 結構中配置好自己的接口之後,調用 register_netdevice 便可以使用該配置。在 linux/drivers/net 中可以找出網絡設備專用的驅動程序。





回頁首


展望

Linux 源代碼是學習有關大多數設備類型的設備驅動程序設計最佳方法,包括網絡設備驅動程序。在這裏可以找到的是各種設計的變化以及對可用內核 API 的使用,但是所學到的每一點都會非常有用,都可以作爲新設備驅動程序的起點。除非您需要一種新協議,否則網絡棧中的其餘代碼都是通用的,都會非常有用。即使現在,TCP(用於流協議)或 UDP(用於基於消息的協議)的實現都可以作爲開始新開發有用模塊使用。



參考資料

學習
  • 您可以參閱本文在 developerWorks 全球站點上的 英文原文

  • 請查閱 www.linuxjunkies.org 上的 “Introduction to the Internet Protocols” ,獲得有關 TCP/IP、UDP 和 ICMP 的簡要介紹。

  • 使用 Linux 系統調用的內核命令”(developerWorks,2007 年 3 月)介紹了 Linux 系統調用接口,它是 Linux 內核中很重要的一層,GNU C Library(glbic)提供了對用戶空間支持,這樣便可以在用戶空間和內核之間進行函數調用。

  • 使用 /proc 文件系統來訪問 Linux 內核的內容”(developerWorks,2006 年 3 月)介紹了 /proc 文件系統,它是一個虛擬文件系統,爲用戶空間的應用程序與內核進行通信提供了一種創新的方法。這篇文章展示 /proc 的應用,並演示可加載的內核模塊。

  • 如果對網絡協議感興趣,Linux 是一個非常不錯的操作系統,這一點與 BSD 類似。“使用 SCTP 優化網絡”(developerWorks,2006 年 2 月)介紹了最有趣的網絡協議之一 SCTP,它的操作方式和 TCP 類似,但是卻增加了一些有用的特性,例如消息、多宿主和多流特性。

  • Linux slab 分配器詳解”(developerWorks,2007 年 5 月)介紹了 Linux 中內存管理的最有趣特性之一:slab 分配器。這種機制源自於 SunOS,但是它在 Linux 內核中找到了一個友好的家園。

  • NAPI 驅動程序與使用老式報文處理框架的驅動程序相比,具有很多優點,從終端管理到報文處理都要好很多。在 OSDL 上的 NAPI 接口和設計 中可以瞭解更多內容。

  • 有關 Linux 用戶空間編程的更多信息,請參考 Tim 撰寫的 GNU/Linux Application Programming 一書。

  • 在 Tim 撰寫的 BSD Sockets Programming from a Multi-Language Perspective 一書中,可以學習有關使用 BSD socket API 進行 socket 編程的知識。

  • Free Software Foundation Web 站點上學習更多有關自由軟件的內容。由於 Linux 是一個自由軟件,因此任何希望從事相關工作的人都可以裝配和發行自己的發行版。

  • developerWorks Linux 專區 中可找到適合 Linux 開發人員的更多資源,包括 Linux 教程

  • 隨時關注 developerWorks 技術活動和網絡廣播

獲得產品和技術
  • 定購 Linux SEK,共包含兩張 DVD,其中包含 Linux 最新的 IBM 試用軟件,包括 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere®。

  • 利用可直接從 developerWorks 下載的 IBM 試用軟件 在 Linux 上構建您的下一個開發項目。


討論


關於作者

M. Tim Jones

M. Tim Jones 是一名嵌入式軟件工程師,他是 GNU/Linux Application ProgrammingAI Application Programming 以及 BSD Sockets Programming from a Multilanguage Perspective 等書的作者。他的工程背景非常廣泛,從同步宇宙飛船的內核開發到嵌入式架構設計,再到網絡協議的開發。Tim 是位於科羅拉多州 Longmont 的 Emulex Corp. 的一名顧問工程師。


發佈了10 篇原創文章 · 獲贊 12 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章