1. 程式人生 > >Linux 網絡卡驅動學習(二)(網路驅動介面小結)

Linux 網絡卡驅動學習(二)(網路驅動介面小結)

【摘要】前文我們分析了一個虛擬硬體的網路驅動例子,從中我們看到了網路裝置的一些介面,其實網路裝置驅動和塊裝置驅動的功能比較類似,都是傳送和接收資料包(資料請求)。當然它們實際是有很多不同的。

1、引言

首先塊裝置在/dev目錄下有裝置節點,而網路裝置沒有這樣的裝置入口。read,write等常規的檔案介面在網路裝置下也沒有意義。
最大的區別在於:塊裝置只響應核心的資料請求;而網路裝置驅動要非同步地接收來自外部的資料包。簡單地說,塊裝置驅動是被要求傳輸資料而網路裝置是主動請求傳輸資料。網路裝置驅動還需要支援設定地址,修改傳輸引數等等這樣的操作,所以網路裝置驅動的api需要提供這些介面。

本文是對上文虛擬硬體的網路驅動例子進行一個簡單的梳理。

(1)網路設備註冊

標頭檔案:<linux/netdevice.h>
struct net_device 網路裝置結構體
struct net_device *alloc_netdev (int size_priv, const char *name, void (*setup)(struct net_device *));
int register_netdev(struct net_device *device); 註冊網路裝置
void unregitster_netdev(struct net_device *device); 登出網路裝置

(2)開啟和關閉
驅動在載入入核心後,核心會呼叫probe函式來探測它。在網路介面可以傳送資料包時,核心必須首先開啟它並給它設定地址。核心開啟和關閉網路介面是由ifconfig命令觸發的。

int (*open)(struct net_device*); 開啟網路裝置
int (*stop)(struct net_device*); 關閉網路裝置
void netif_start_queue(struct net_device*); 啟動網路傳輸佇列
void netif_stop_queue(struct net_device*); 關閉網路傳輸佇列

(3)網路資料的傳送
網路介面最重要的作用是傳送和接收網路資料。

標頭檔案:<linux/skbuff.h> 定義了網路驅動中傳輸的基本單元,struct sk_buff
struct netdeviceops 網路裝置驅動需要實現的介面函式
netdev_tx_t (*ndo_start_xmit) (struct
sk_buff *skb, struct net_device *dev); 傳輸網路資料包的函式 void (*ndo_tx_timeout) (struct net_device *dev); 傳輸超時函式

(4)網路資料的接收
接收網路資料相對於傳送資料要複雜一些,因為你需要在原子上下文中把分配一個 sk_buff 並把它移交給上層處理。

資料包接收有兩種實現方式:中斷驅動和輪詢。大多數驅動都是中斷驅動的,有一些高吞吐量的驅動會使用輪詢的方式。

struct sk_buff *dev_alloc_skb(unsigned int length); 原子上下文中分配skb
printk_ratelimit() 限制printk的輸出頻率,在中斷相應函式中減少輸出

實現高吞吐量的網路驅動,要減少網路阻塞最好的方法是使用napi,後面會介紹

(5)中斷處理
硬體可以中斷cpu傳送兩種事件:新的資料包到來和傳送的資料包已經發送完成。

判斷並處理資料包事件
如果在其他地方暫時停止了傳送佇列,應該在中斷函式中重新啟動它

(6)NAPI
高吞吐量的網路介面如果每個資料包都用中斷來處理的話會給系統帶來很大的負擔,這個時候應該使用基於輪詢的 NAPI。這樣可以減輕系統的負擔,減少阻塞的時間。

只有極少數的裝置實現了NAPI,因為實現起來比中斷要複雜,而且有其他的一些條件。

在中斷處理函式中,首先禁止進一步的中斷處理,然後排程輪詢函式,進入輪詢函式後連續處理多個數據傳送請求。

int (*poll)(struct net_device *dev, int *budget); 網路驅動輪詢函式
int netif_rx_schedule(struct net_device *dev); 準備呼叫輪詢函式
int (*poll)(struct net_device *dev, int *budget); 輪詢函式