新聞中心

EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > Linux網(wǎng)卡驅(qū)動程序編寫

Linux網(wǎng)卡驅(qū)動程序編寫

作者: 時間:2012-05-09 來源:網(wǎng)絡(luò) 收藏

方法都很短小以盡量減少系統(tǒng)負(fù)荷。(translatedfromarticlewrittenbyAlanCox)

常用的方法包括:

.alloc_skb()申請一個sk_buff并對它初始化。返回就是申請到的sk_buff。

.dev_alloc_skb()類似alloc_skb,在申請好緩沖區(qū)后,保留16字節(jié)的幀頭空間。主要用在Ethernet。

.kfree_skb()釋放一個sk_buff。

.skb_clone()復(fù)制一個sk_buff,但不復(fù)制數(shù)據(jù)部分。

.skb_copy()完全復(fù)制一個sk_buff。

.skb_dequeue()從一個sk_buff鏈表里取出第一個元素。返回取出的sk_buff,如果鏈表空則返回NULL。這是常用的一個操作。

.skb_queue_head()在一個sk_buff鏈表頭放入一個元素。

.skb_queue_tail()在一個sk_buff鏈表尾放入一個元素。這也是常用的一個操作。網(wǎng)絡(luò)數(shù)據(jù)的處理主要是對一個先進(jìn)先出隊(duì)列的管理,skb_queue_tail()

和skb_dequeue()完成這個工作。

.skb_insert()在鏈表的某個元素前插入一個元素。

.skb_append()在鏈表的某個元素后插入一個元素。一些協(xié)議(如TCP)對沒按順序到達(dá)的數(shù)據(jù)進(jìn)行重組時用到skb_insert()和skb_append()。

.skb_reserve()在一個申請好的sk_buff的緩沖區(qū)里保留一塊空間。這個空間一般是用做下一層協(xié)議的頭空間的。

.skb_put()在一個申請好的sk_buff的緩沖區(qū)里為數(shù)據(jù)保留一塊空間。在

alloc_skb以后,申請到的sk_buff的緩沖區(qū)都是處于空(free)狀態(tài),有一個tail指針指向free空間,實(shí)際上開始時tail就指向緩沖區(qū)頭。skb_reserve()

在free空間里申請協(xié)議頭空間,skb_put()申請數(shù)據(jù)空間。見下面的圖。

.skb_push()把sk_buff緩沖區(qū)里數(shù)據(jù)空間往前移。即把Headroom中的空間移一部分到Dataarea。

.skb_pull()把sk_buff緩沖區(qū)里Dataarea中的空間移一部分到Headroom中。

--------------------------------------------------

|Tailroom(free)|

--------------------------------------------------

Afteralloc_skb()

--------------------------------------------------

|Headroom|Tailroom(free)|

--------------------------------------------------

Afterskb_reserve()

--------------------------------------------------

|Headroom|Dataarea|Tailroom(free)|

--------------------------------------------------

Afterskb_put()

--------------------------------------------------

|Head|skb_|Data|Tailroom(free)|

|room|push|||

||Dataarea||

--------------------------------------------------

Afterskb_push()

--------------------------------------------------

|Head|skb_|Dataarea|Tailroom(free)|

||pull|||

|Headroom|||

--------------------------------------------------

Afterskb_pull()

三.網(wǎng)絡(luò)中需要注意的問題

3.1中斷共享

系統(tǒng)運(yùn)行幾個設(shè)備共享同一個中斷。需要共享的話,在申請的時候指明共享方式。系統(tǒng)提供的request_irq()調(diào)用的定義:

intrequest_irq(unsignedintirq,

void(*handler)(intirq,void*dev_id,structpt_regs*regs),

unsignedlongirqflags,

constchar*devname,

void*dev_id);

如果共享中斷,irqflags設(shè)置SA_SHIRQ屬性,這樣就允許別的設(shè)備申請同一個中斷。需要注意所有用到這個中斷的設(shè)備在調(diào)用request_irq()都必須設(shè)置這個屬性。系統(tǒng)在回調(diào)每個中斷處理程序時,可以用dev_id這個參數(shù)找到相應(yīng)的設(shè)備。一般dev_id就設(shè)為device結(jié)構(gòu)本身。系統(tǒng)處理共享中斷是用各自的dev_id參數(shù)依次調(diào)用每一個中斷處理程序。

3.2硬件發(fā)送忙時的處理

主CPU的處理能力一般比網(wǎng)絡(luò)發(fā)送要快,所以經(jīng)常會遇到系統(tǒng)有數(shù)據(jù)要發(fā),但上一包數(shù)據(jù)網(wǎng)絡(luò)設(shè)備還沒發(fā)送完。因?yàn)樵?a class="contentlabel" href="http://m.butianyuan.cn/news/listbylabel/label/Linux">Linux里網(wǎng)絡(luò)設(shè)備一般不做數(shù)據(jù)緩存,不能發(fā)送的數(shù)據(jù)都是通知系統(tǒng)發(fā)送不成功,所以必須要有一個機(jī)制在硬件不忙時及時通知系統(tǒng)接著發(fā)送下面的數(shù)據(jù)。

一般對發(fā)送忙的處理在前面設(shè)備的發(fā)送方法(hard_start_xmit)里已經(jīng)描述過,即如果發(fā)送忙,置tbusy為1。處理完發(fā)送數(shù)據(jù)后,在發(fā)送結(jié)束中斷里清tbusy,同時用mark_bh()調(diào)用通知系統(tǒng)繼續(xù)發(fā)送。

但在具體實(shí)現(xiàn)我的驅(qū)動程序時發(fā)現(xiàn),這樣的處理系統(tǒng)好象并不能及時地知道硬件已經(jīng)空閑了,即在mark_bh()以后,系統(tǒng)要等一段時間才會接著發(fā)送。造成發(fā)送效率很低。2M線路只有10%不到的使用率。內(nèi)核版本為2.0.35。

我最后的實(shí)現(xiàn)是不把tbusy置1,讓系統(tǒng)始終認(rèn)為硬件空閑,但是報(bào)告發(fā)送不成功。系統(tǒng)會一直嘗試重發(fā)。這樣處理就運(yùn)行正常了。但是遍循內(nèi)核源碼中的網(wǎng)絡(luò)驅(qū)動程序,似乎沒有這樣處理的。不知道癥結(jié)在哪里。

3.3流量控制(flowcontrol)

網(wǎng)絡(luò)數(shù)據(jù)的發(fā)送和接收都需要流量控制。這些控制是在系統(tǒng)里實(shí)現(xiàn)的,不需要驅(qū)動程序做工作。每個設(shè)備數(shù)據(jù)結(jié)構(gòu)里都有一個參數(shù)dev->tx_queue_len,這個參數(shù)標(biāo)明發(fā)送時最多緩存的數(shù)據(jù)包。在Linux系統(tǒng)里以太網(wǎng)設(shè)備(10/100Mbps)tx_queue_len一般設(shè)置為100,串行線路(異步串口)為10。實(shí)際上如果看源碼可以知道,設(shè)置了dev->tx_queue_len并不是為緩存這些數(shù)據(jù)申請了空間。這個參數(shù)只是在收到協(xié)議層的數(shù)據(jù)包時判斷發(fā)送隊(duì)列里的數(shù)據(jù)是不是到了tx_queue_len的限度,以決定這一包數(shù)據(jù)加不加進(jìn)發(fā)送隊(duì)列。發(fā)送時另一個方面的流控是更高層協(xié)議的發(fā)送窗口(TCP協(xié)議里就有發(fā)送窗口)。達(dá)到了窗口大小,高層協(xié)議就不會再發(fā)送數(shù)據(jù)。

接收流控也分兩個層次。netif_rx()緩存的數(shù)據(jù)包有限制。另外高層協(xié)議也會有一個最大的等待處理的數(shù)據(jù)量。

發(fā)送和接收流控處理在net/core/dev.c的do_dev_queue_xmit()和netif_rx()中。

3.4調(diào)試

很多Linux的驅(qū)動程序都是編譯進(jìn)內(nèi)核的,形成一個大的內(nèi)核文件。但對調(diào)試來說,這是相當(dāng)麻煩的。調(diào)試驅(qū)動程序可以用module方式加載。支持模塊方式的驅(qū)動程序必須提供兩個函數(shù):intinit_module(void)和voidcleanup_module(void)。init_module()在加載此模塊時調(diào)用,在這個函數(shù)里可以register_netdev()注冊設(shè)備。init_module()返回0表示成功,返回負(fù)表示失敗。cleanup_module()在驅(qū)動程序被卸載時調(diào)用,清除占用的資源,調(diào)用unregister_netdev()。

linux操作系統(tǒng)文章專題:linux操作系統(tǒng)詳解(linux不再難懂)


評論


相關(guān)推薦

技術(shù)專區(qū)

關(guān)閉