淺談 Linux 核心開發之網路裝置驅動

來源:互聯網
上載者:User

本文來自:http://www.ibm.com/developerworks/cn/linux/l-cn-networkdriver/

網路裝置介紹

網路
裝置是電腦體繫結構中必不可少的一部分,處理器如果想與外界通訊,通常都會選擇網路裝置作為通訊介面。眾所周知,在 OSI(Open
Systems
Interconnection,開放網際互連)中,網路被劃分為七個層次,從下到上分別是物理層、資料連結層、網路層、傳輸層、會話層、展示層和應用
層。我們所講的網路裝置也包括兩個層次,一層叫做 MAC(Media Access Control)層,對應於 OSI 的資料連結層;另一層叫做
PHY(Physical Layer)層,對應於物理層。

常用的網路裝置有很多,比如 PPC85XX 的 TSEC、AMCC 440GX 的 EMAC、INTEL 的 82559 等,它們的工作原理基本相同。

DMA 介紹


絡裝置的核心處理模組是一個被稱作 DMA(Direct Memory Access)的控制器,DMA
模組能夠協助處理器處理資料收發。對於資料發送來說,它能夠將組織好的資料自動發出,無需處理器幹預;對於資料接收來說,它能夠將收到的資料以一定的格式
組織起來,通知處理器,並等待處理器來取。

DMA 模組收發資料的單元被稱為 BD(Buffer Description,緩衝描述符),每個包都會被分成若干個幀,而每個幀則被儲存在一個 BD 中。BD 結構通常包含有以下欄位:

 typedef struct { 
void *bufptr; /* 儲存當前 BD 對應緩衝的起始地址 */
int length; /* 儲存緩衝中儲存的資料包長度 */
int sc; /* 儲存當前 BD 的狀態資訊 */
} BD_STRUCT;

 

所有的 BD 就組成了一張 BD 表, 1 所示,一般來說發送方向和接收方向的 BD 表是各自獨立的。


圖 1. BD 表結構


資料發送流程

網路裝置通過 DMA 進行資料發送的流程如 圖 2
所示。


圖 2. 資料發送流程


圖中各步驟的具體含義描述如下:

(1)協議層通知處理器開始發送資料;

(2)處理器從 BD 表中取出一個 BD,將需要發送的資料拷貝至當前 BD 對應的緩衝內,並設定好 BD 的狀態;

(3)處理器通知網路裝置開始發送資料;

(4)MAC 模組通知 DMA 單元開始發送資料;

(5)DMA 模組操作 BD 表,取出當前有效 BD;

(6)DMA 模組將當前 BD 對應緩衝內的資料發送至 MAC 模組;

(7)MAC 模組將這些資料發送到網路中;

(8)網路裝置通知處理器資料發送完畢;

(9)處理器通知協議層發送下面一幀資料。

其中步驟(4)~(8)是硬體自動完成的,不需要軟體的幹預,如此可以節省處理器的工作量。

資料接收流程

網路裝置通過 DMA 進行資料接收的流程 3 所示。


圖 3. 資料接收流程


圖中各步驟的具體含義描述如下:

(1)處理器初始化 BD 表;

(2)處理器初始化網路裝置;

(3)MAC 模組從網路中接收資料;

(4)MAC 模組通知 DMA 模組來取資料;

(5)DMA 模組從 BD 表中取出合適的 BD;

(6)MAC 模組將資料發送至當前 BD 對應的緩衝內;

(7)網路裝置通知處理器開始接收資料(以中斷方式或輪詢方式);

(8)協議層從當前的 BD 緩衝內取走資料。

其中步驟(3)~(6)是硬體自動完成的,不需要軟體的幹預,如此可以節省處理器的工作量。

回頁首

Linux 網路裝置驅動模型

資料結構

資料結構

Linux 核心中對網路裝置進行描述的核心結構類型叫做 net_device,net_device 結構定義在 include/linux/netdevice.h 檔案中。該結構的欄位可以分為以下幾類。

全域資訊

該類中包含了裝置名稱(name 欄位)、裝置狀態(state 欄位)、裝置初始化函數(init 欄位)等。

硬體資訊

該類中包含了裝置記憶體使用量情況(mem_end 和 mem_start 欄位)、中斷號(irq 欄位)、IO 基地址(base_addr 欄位)等。

介面資訊

該類中包含了 MAC 位址(dev_addr 欄位)、裝置屬性(flag 欄位)、傳輸單元最大值(mtu 欄位)等。

裝置介面函數


類中包含了當前裝置所提供的所有介面函數,比如裝置開啟函數(open 欄位),該函數負責開啟裝置介面,當使用者使用 ifconfig
命令配置網路時,該函數預設被調用;裝置停止函數(stop 欄位),該函數負責關閉裝置介面;資料發送函數(hard_start_xmit
欄位),當使用者調用 socket 開始寫資料時,該函數被調用,並負責往網路裝置中發送資料。

函數介面

裝置初始化函數

網路裝置驅動在 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 資料結構圖


1. 資料發送流程

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


圖 5. 資料發送流程圖


  1.  
    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. 資料接收流程圖


  1.  
    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 的空閑狀態。

由於是採用任務定時檢查的原理,從而輪詢接收方式的即時性較差,但它沒有中斷那種系統環境切換的開銷,因此輪詢方式在處理大流量資料包時會顯得更加高效。

回頁首

Linux 網路裝置驅動最佳化


著科技的不斷髮展,網路裝置所能承載的速率在不斷提升,當前流行的網路裝置普遍都能支援 10Mbps / 100Mbps / 1Gbps
這三種速率。雖然網路裝置的硬體效能在不斷的提升,但是實際在 Linux 系統中其運行效能(收發包速率)真能達到多達 1Gbps
的水平嗎?這和處理器的效能有關,一般來說我們啟動並執行系統中報文的收發速率是達不到 1Gbps
的(因為我們不可能將所有處理器的資源都貢獻給報文的收發),但是我們可以在有限的條件下儘可能的採取一些最佳化手段提高網路裝置的運行效能。

Cache 的應用

Cache
位於儲存系統金字塔的頂層(下面一層是記憶體),Cache 的容量不大(一級 Cache 一般是幾十 KB,二級 Cache 一般是幾
MB),但是它的訪問速率卻是記憶體的幾十倍。因此如果處理器通過 Cache
來訪問記憶體,將會極大的提高訪問速率。在網路裝置的資料收發中,恰當的應用 Cache 可以最佳化驅動的效能。下面列舉幾點 Cache 的最佳化措施。

合理設定記憶體屬性

記憶體的頁表有多種屬性,其中有一項就是是否通過 Cache 訪問。在給 BD 表配置記憶體時,這些被分配的記憶體屬性需要支援 Cache 訪問。

Cache
的訪問還有兩種方式:一種是寫回操作(Write Back),處理器更新記憶體資料時,該資料首先儲存在 Cache 中,Cache
並不及時將資料更新進記憶體,而是等到 Cache 需要再次更新時才會將資料寫回到記憶體中。另一種是寫穿操作(Write
Through),處理器更新記憶體資料時,該資料首先儲存在 Cache 中,Cache
隨即將資料立刻更新進記憶體。顯而易見,寫回操作的效能比寫穿操作更高,通常我們設定記憶體頁表屬性為寫回方式。

資料收發時的 Cache 操作


記憶體支援 Cache 且採用寫回方式的情況下,當發送資料時,處理器先將資料寫進 Cache,如果 DMA
模組直接從記憶體中取出資料發送的話,該資料將與 Cache 並不一致,因此在驅動程式中,需要將 Cache 中的資料更新到記憶體,然後再通知
DMA 進行發送。

當接收資料時,DMA 模組會將資料收到記憶體中,如果這時候處理器從該記憶體接收資料的話,處理器會從
Cache 中取資料,但是 Cache
並不知道記憶體已經被更新,這就會導致接收到的資料與實際不符,因此在驅動程式中,需要在接收資料之前重新整理一下該 Cache,以保證 Cache
與記憶體的一致性。

需要說明的是,並不是所有處理器都需要以上操作,有的處理器所帶的 DMA 控制器是能感知 Cache(IO-Cache Coherence)的,它們能夠自動進行上述的 Cache 操作,因此對於這類處理器,驅動程式中無需關注 Cache。

中斷還是輪詢?

前面曾經介紹過,網路裝置驅動支援兩種接收資料的方式,一種是中斷,另一種是輪詢,在資料流量比較大的情況下,可以考慮採用輪詢的方式以達到更高的效率。


採用輪詢方式時,還有一個不得不考慮的問題,那就是輪詢任務優先順序的選擇,眾所周知,當任務優先順序高時,該任務不會被其他的低優先順序任務所打斷,從而可以
保證處理器能夠專心完成資料接收工作;但如果任務優先順序低時,一旦發生了其他高優先順序的任務,處理器會將當前的資料接收工作暫停,轉而執行別的任務,如此
會影響網路裝置驅動的效率。因此驅動設計者需要結合實際情況,恰當的選擇任務的優先順序。

裝置介面模式

有時候我們會發現雖然網路裝置號稱有 100Mbps 的速率,但是實際資料收發卻非常慢,遇到這種情況,我們首先需要檢查網路裝置介面模式是否設定正確。

PHY 模組介面模式

PHY
模組的介面模式有兩種,強制模式(強制 10M / 100M / 1G 等)和自協商模式。究竟選擇哪種模式需要看當前 PHY 模組所串連的對端
PHY 狀態才行,如果對端設定的是自協商模式,本端的 PHY
模組也需要相應設定成自協商,如此就能夠保證協商出來的結果是當前鏈路所能支援的最大速率。反之,如果對端設定成強制模式,本端也需要設定成強制,且強制
速率要與對端設定的強制速率相同。

MAC 模組介面模式

MAC
模組對於不同的速率(10M / 100M / 1G 等)也會有不同的介面模式選擇,如果設定的模式與 PHY
模組所啟動並執行速率不匹配的話,會極大的影響網路裝置資料收發的速度。因此在初始化 MAC 模組時,需要檢查 PHY
模組的運行速率,從而選擇恰當的介面模式。

每個 PHY / MAC 模組裝置的介面模式選擇都不盡相同,因此在開發網路裝置驅動時,需要明確所使用的裝置,並在該裝置初始化時正確配置其介面模式。

回頁首

結束語

Linux 網路裝置驅動與具體的裝置關聯很大,因此在實際編程中需要結合具體裝置來寫驅動代碼,我們在開發過程中要格外注意驅動的最佳化,因為網路裝置驅動的好壞將直接影響到整個系統的效能。

 

參考資料

  • Linux Device Drivers,Jonathan Corbet、Alessandro Rubini、Greg Kroah-Hartman 著,東南大學出版社
  • 參考MPC8548E PowerQUICC III Integrated Processor Family Reference Manual,FreeScale
  • 在 developerWorks Linux 專區
    尋找為 Linux 開發人員(包括 Linux 新手入門
    )準備的更多參考資料,查閱我們 最受歡迎的文章和教程
  • 在 developerWorks 上查閱所有 Linux 技巧
    和 Linux 教程

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.