歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> 關於Linux >> Linux 網絡設備驅動開發(二) —— Linux 網絡棧剖析

Linux 網絡設備驅動開發(二) —— Linux 網絡棧剖析

日期:2017/3/1 12:09:55   编辑:關於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 中可以找出網絡設備專用的驅動程序。



Copyright © Linux教程網 All Rights Reserved