天天看點

Linux 網卡驅動學習(五)(收發包具體過程)

函數接口

裝置初始化函數

網絡裝置驅動在 Linux 核心中是以核心子產品的形式存在的,對應于子產品的初始化,需要提供一個初始化函數來初始化網絡裝置的硬體寄存器、配置 DMA 以及初始化相關核心變量等。裝置初始化函數在核心子產品被加載時調用,它的函數形式如下:

static int __init xx_init (void) { 
   ……
 } 
 module_init(xx_init);   // 這句話表明子產品加載時自動調用 xx_init 函數      

裝置初始化函數主要完成以下功能:

1. 硬體初始化

因為網絡裝置主要分為 PHY、MAC 和 DMA 三個硬體子產品,開發者需要分别對這三個子產品進行初始化。

  1. 初始化 PHY 子產品,包括設定雙工 / 半雙工運作模式、裝置運作速率和自協商模式等。
  2. 初始化 MAC 子產品,包括設定裝置接口模式等。
  3. 初始化 DMA 子產品,包括建立 BD 表、設定 BD 屬性以及給 BD 配置設定緩存等。

2. 核心變量初始化

初始化并注冊核心裝置。核心裝置是屬性為 net_device 的一個變量,開發者需要申請該變量對應的空間(通過 alloc_netdev 函數)、設定變量參數、挂接接口函數以及注冊裝置(通過 register_netdev 函數)。

常用的挂接接口函數如下:

net_device *dev_p; 
 dev_p->open              = xx_open;   // 裝置打開函數
 dev_p->stop              = xx_stop;   // 裝置停止函數
 dev_p->hard_start_xmit = xx_tx;     // 資料發送函數
 dev_p->do_ioctl          = xx_ioctl; // 其它的控制函數
……      

資料收發函數

資料的接收和發送是網絡裝置驅動最重要的部分,對于使用者來說,他們無需了解目前系統使用了什麼網絡裝置、網絡裝置收發如何進行等,所有的這些細節對于使用者都是屏蔽的。Linux 使用 socket 做為連接配接使用者和網絡裝置的一個橋梁。使用者可以通過 read / write 等函數操作 socket,然後通過 socket 與具體的網絡裝置進行互動,進而進行實際的資料收發工作。

Linux 提供了一個被稱為 sk_buff 的資料接口類型,使用者傳給 socket 的資料首先會儲存在 sk_buff 對應的緩沖區中,sk_buff 的結構定義在 include/linux/skbuff.h 檔案中。它儲存資料包的結構示意圖如下所示。

圖 4. sk_buff 資料結構圖
Linux 網卡驅動學習(五)(收發包具體過程)

1. 資料發送流程

當使用者調用 socket 開始發送資料時,資料被儲存到了 sk_buff 類型的緩存中,網絡裝置的發送函數(裝置初始化函數中注冊的 hard_start_xmit)也随之被調用,流程圖如下所示。

圖 5. 資料發送流程圖
Linux 網卡驅動學習(五)(收發包具體過程)
  1. 使用者首先建立一個 socket,然後調用 write 之類的寫函數通過 socket 通路網絡裝置,同時将資料儲存在 sk_buff 類型的緩沖區中。
  2. socket 接口調用網絡裝置發送函數(hard_start_xmit),hard_start_xmit 已經在初始化過程中被挂接成類似于 xx_tx 的具體的發送函數,xx_tx 主要實作如下步驟。
    1. 從發送 BD 表中取出一個空閑的 BD。
    2. 根據 sk_buff 中儲存的資料修改 BD 的屬性,一個是資料長度,另一個是資料包緩存指針。值得注意的是,資料包緩存指針對應的必須是實體位址,這是因為 DMA 在擷取 BD 中對應的資料時隻能識别儲存該資料緩存的實體位址。
      bd_p->length = skb_p->len; 
       bd_p->bufptr = virt_to_phys(skb_p->data);      
    3. 修改該 BD 的狀态為就緒态,DMA 子產品将自動發送處于就緒态 BD 中所對應的資料。
    4. 移動發送 BD 表的指針指向下一個 BD。
  3. DMA 子產品開始将處于就緒态 BD 緩存内的資料發送至網絡中,當發送完成後自動恢複該 BD 為空閑态。

2. 資料接收流程

當網絡裝置接收到資料時,DMA 子產品會自動将資料儲存起來并通知處理器來取,處理器通過中斷或者輪詢方式發現有資料接收進來後,再将資料儲存到 sk_buff 緩沖區中,并通過 socket 接口讀出來。流程圖如下所示。

圖 6. 資料接收流程圖
Linux 網卡驅動學習(五)(收發包具體過程)
  1. 網絡裝置接收到資料後,DMA 子產品搜尋接收 BD 表,取出空閑的 BD,并将資料自動儲存到該 BD 的緩存中,修改 BD 為就緒态,并同時觸發中斷(該步驟可選)。
  2. 處理器可以通過中斷或者輪詢的方式檢查接收 BD 表的狀态,無論采用哪種方式,它們都需要實作以下步驟。
    1. 從接收 BD 表中取出一個空閑的 BD。
    2. 如果目前 BD 為就緒态,檢查目前 BD 的資料狀态,更新資料接收統計。
    3. 從 BD 中取出資料儲存在 sk_buff 的緩沖區中。
    4. 更新 BD 的狀态為空閑态。
    5. 移動接收 BD 表的指針指向下一個 BD。
  3. 使用者調用 read 之類的讀函數,從 sk_buff 緩沖區中讀出資料,同時釋放該緩沖區。

中斷和輪詢

Linux 核心在接收資料時有兩種方式可供選擇,一種是中斷方式,另外一種是輪詢方式。

中斷方式

如果選擇中斷方式,首先在使用該驅動之前,需要将該中斷對應的中斷類型号和中斷處理程式注冊進去。網絡裝置驅動在初始化時會将具體的 xx_open 函數挂接在驅動的 open 接口上,xx_open 函數挂接中斷的步驟如下。

request_irq(rx_irq, xx_isr_rx, …… ); 
 request_irq(tx_irq, xx_isr_tx, …… );      

網絡裝置的中斷一般會分為兩種,一種是發送中斷,另一種是接收中斷。核心需要分别對這兩種中斷類型号進行注冊。

  1. 發送中斷處理程式(xx_isr_tx)的工作主要是監控資料發送狀态、更新資料發送統計等。
  2. 接收中斷處理程式(xx_isr_rx)的工作主要是接收資料并傳遞給協定層、監控資料接收狀态、更新資料接收統計等。

對于中斷方式來說,由于每收到一個包都會産生一個中斷,而處理器會迅速跳到中斷服務程式中去處理收包,是以中斷接收方式的實時性高,但如果遇到資料包流量很大的情況時,過多的中斷會增加系統的負荷。

輪詢方式

如果采用輪詢方式,就不需要使能網絡裝置的中斷狀态,也不需要注冊中斷處理程式。作業系統會專門開啟一個任務去定時檢查 BD 表,如果發現目前指針指向的 BD 非空閑,則将該 BD 對應的資料取出來,并恢複 BD 的空閑狀态。

由于是采用任務定時檢查的原理,進而輪詢接收方式的實時性較差,但它沒有中斷那種系統上下文切換的開銷,是以輪詢方式在處理大流量資料包時會顯得更加高效。

繼續閱讀