轉自:https://blog.csdn.net/xy010902100449/article/details/47362787
版權聲明:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/xy010902100449/article/details/47362787
函數接口
設備初始化函數
網絡設備驅動在 Linux 內核中是以內核模塊的形式存在的,對應於模塊的初始化,需要提供一個初始化函數來初始化網絡設備的硬件寄存器、配置 DMA 以及初始化相關內核變量等。設備初始化函數在內核模塊被加載時調用,它的函數形式如下:
static int __init xx_init (void) {
……
}
module_init(xx_init); // 這句話表明模塊加載時自動調用 xx_init 函數
設備初始化函數主要完成以下功能:
1. 硬件初始化
因為網絡設備主要分為 PHY、MAC 和 DMA 三個硬件模塊,開發者需要分別對這三個模塊進行初始化。
初始化 PHY 模塊,包括設置雙工 / 半雙工運行模式、設備運行速率和自協商模式等。
初始化 MAC 模塊,包括設置設備接口模式等。
初始化 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 數據結構圖
1. 數據發送流程
當用戶調用 socket 開始發送數據時,數據被儲存到了 sk_buff 類型的緩存中,網絡設備的發送函數(設備初始化函數中注冊的 hard_start_xmit)也隨之被調用,流程圖如下所示。
圖 5. 數據發送流程圖
用戶首先創建一個 socket,然后調用 write 之類的寫函數通過 socket 訪問網絡設備,同時將數據保存在 sk_buff 類型的緩沖區中。
socket 接口調用網絡設備發送函數(hard_start_xmit),hard_start_xmit 已經在初始化過程中被掛接成類似於 xx_tx 的具體的發送函數,xx_tx 主要實現如下步驟。
從發送 BD 表中取出一個空閑的 BD。
根據 sk_buff 中保存的數據修改 BD 的屬性,一個是數據長度,另一個是數據包緩存指針。值得注意的是,數據包緩存指針對應的必須是物理地址,這是因為 DMA 在獲取 BD 中對應的數據時只能識別儲存該數據緩存的物理地址。
bd_p->length = skb_p->len;
bd_p->bufptr = virt_to_phys(skb_p->data);
修改該 BD 的狀態為就緒態,DMA 模塊將自動發送處於就緒態 BD 中所對應的數據。
移動發送 BD 表的指針指向下一個 BD。
DMA 模塊開始將處於就緒態 BD 緩存內的數據發送至網絡中,當發送完成后自動恢復該 BD 為空閑態。
2. 數據接收流程
當網絡設備接收到數據時,DMA 模塊會自動將數據保存起來並通知處理器來取,處理器通過中斷或者輪詢方式發現有數據接收進來后,再將數據保存到 sk_buff 緩沖區中,並通過 socket 接口讀出來。流程圖如下所示。
圖 6. 數據接收流程圖
網絡設備接收到數據后,DMA 模塊搜索接收 BD 表,取出空閑的 BD,並將數據自動保存到該 BD 的緩存中,修改 BD 為就緒態,並同時觸發中斷(該步驟可選)。
處理器可以通過中斷或者輪詢的方式檢查接收 BD 表的狀態,無論采用哪種方式,它們都需要實現以下步驟。
從接收 BD 表中取出一個空閑的 BD。
如果當前 BD 為就緒態,檢查當前 BD 的數據狀態,更新數據接收統計。
從 BD 中取出數據保存在 sk_buff 的緩沖區中。
更新 BD 的狀態為空閑態。
移動接收 BD 表的指針指向下一個 BD。
用戶調用 read 之類的讀函數,從 sk_buff 緩沖區中讀出數據,同時釋放該緩沖區。
中斷和輪詢
Linux 內核在接收數據時有兩種方式可供選擇,一種是中斷方式,另外一種是輪詢方式。
中斷方式
如果選擇中斷方式,首先在使用該驅動之前,需要將該中斷對應的中斷類型號和中斷處理程序注冊進去。網絡設備驅動在初始化時會將具體的 xx_open 函數掛接在驅動的 open 接口上,xx_open 函數掛接中斷的步驟如下。
request_irq(rx_irq, xx_isr_rx, …… );
request_irq(tx_irq, xx_isr_tx, …… );
網絡設備的中斷一般會分為兩種,一種是發送中斷,另一種是接收中斷。內核需要分別對這兩種中斷類型號進行注冊。
發送中斷處理程序(xx_isr_tx)的工作主要是監控數據發送狀態、更新數據發送統計等。
接收中斷處理程序(xx_isr_rx)的工作主要是接收數據並傳遞給協議層、監控數據接收狀態、更新數據接收統計等。
對於中斷方式來說,由於每收到一個包都會產生一個中斷,而處理器會迅速跳到中斷服務程序中去處理收包,因此中斷接收方式的實時性高,但如果遇到數據包流量很大的情況時,過多的中斷會增加系統的負荷。
輪詢方式
如果采用輪詢方式,就不需要使能網絡設備的中斷狀態,也不需要注冊中斷處理程序。操作系統會專門開啟一個任務去定時檢查 BD 表,如果發現當前指針指向的 BD 非空閑,則將該 BD 對應的數據取出來,並恢復 BD 的空閑狀態。
由於是采用任務定時檢查的原理,從而輪詢接收方式的實時性較差,但它沒有中斷那種系統上下文切換的開銷,因此輪詢方式在處理大流量數據包時會顯得更加高效
---------------------
作者:狂奔的烏龜
來源:CSDN
原文:https://blog.csdn.net/xy010902100449/article/details/47362787
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!