【正文】
development technology under the Linux environment is more and more noticeable in puter industry. One of the many advantages of free operating systems, as typified by Linux, is that their internals are open for all to view. The Linux kernel remains large and plex body of code. User activities are performed by means of a set of standardized calls that are independent of the specific driver。1992年1月,大概只有100人開(kāi)始使用Linux,但他們?yōu)長(zhǎng)inux的發(fā)展壯大作出了巨大貢獻(xiàn)。Linux系統(tǒng)的設(shè)備主要分為字符設(shè)備(char device),塊設(shè)備(block device)和網(wǎng)絡(luò)設(shè)備(network device)三種。 Linux內(nèi)核簡(jiǎn)介在最開(kāi)始的時(shí)候,Linux系統(tǒng)并沒(méi)有現(xiàn)在所看到的Linux系統(tǒng)的體積這么龐大,各種免費(fèi)開(kāi)放的驅(qū)動(dòng)代碼也還沒(méi)有來(lái)得及加入到系統(tǒng)中,所以,之初的Linux實(shí)際意義上就是Linux內(nèi)核。另外,基于過(guò)程的結(jié)構(gòu)也有助于不同的人參與不同過(guò)程的開(kāi)發(fā),從這個(gè)角度來(lái)說(shuō),Linux內(nèi)核又是開(kāi)放式的結(jié)構(gòu),它允許任何人對(duì)其進(jìn)行修正、改進(jìn)和完善。網(wǎng)絡(luò)部分采用了面向?qū)ο蟮脑O(shè)計(jì)思想,使得Linux內(nèi)核支持多種協(xié)議、多種網(wǎng)卡驅(qū)動(dòng)程序變得更加的容易,為驅(qū)動(dòng)的開(kāi)發(fā)提供了便捷性,減少了工作量,提高了工作效率。當(dāng)然,也存在具有數(shù)據(jù)特性的字符設(shè)備,訪問(wèn)它們時(shí)可以前后移動(dòng)訪問(wèn)位置。下面簡(jiǎn)單介紹一下網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)程序的一些基本的也是最重要的概念。內(nèi)核中的網(wǎng)絡(luò)驅(qū)動(dòng)程序接口是為不同模式的操作而精心設(shè)計(jì)的。unsigned long rmem_end;unsigned long rmem_start;unsigned long mem_end;unsigned long mem_start;設(shè)備的內(nèi)存信息.。unsigned char dma;為設(shè)備分配的DMA通道。網(wǎng)絡(luò)層使用該成員變量驅(qū)動(dòng)數(shù)據(jù)包的傳輸。 網(wǎng)卡驅(qū)動(dòng)程序的基本方法每個(gè)網(wǎng)絡(luò)設(shè)備都聲明了很多能操作它的函數(shù)。int (*rebuild_header)(struct sk_buff *skb);該函數(shù)用來(lái)在ARP[4]解析完成之后、在報(bào)文發(fā)送之前重新建立硬件頭。h中含有傳輸層頭部指針(例如struct tcphdr *th);nh包含網(wǎng)絡(luò)層頭部(例如struct iphdr *iph);以及mac包含鏈路層頭部指針(例如struct ethkr * ethernet)。dev_alloc_skb函數(shù)以GFP_ATOMIC的優(yōu)先級(jí)調(diào)用 alloc_skb函數(shù),并且在skbhead和skbdata之間保留了一些空間,網(wǎng)絡(luò)層使用這一數(shù)據(jù)空間進(jìn)行優(yōu)化工作,驅(qū)動(dòng)程序不應(yīng)該訪問(wèn)它。unsigned char *skb_pull(struct sk_buff *skb, int len);從報(bào)文的頭部刪除數(shù)據(jù)。如果內(nèi)核認(rèn)為模塊仍然在使用狀態(tài)(例如,某個(gè)程序正打開(kāi)由該模塊導(dǎo)出的設(shè)備文件),或者內(nèi)核被配置為禁止移除該模塊,則無(wú)法移除該模塊。每條信道都有自己的數(shù)據(jù)、時(shí)鐘和控制信號(hào)。不同的影子寄存器的訪問(wèn)方式又是不相同的,比如24號(hào)寄存器(輔助控制寄存器)和28號(hào)寄存器可以直接通過(guò)影子寄存器的來(lái)實(shí)現(xiàn)寄存器值的讀寫(xiě)操作;但擴(kuò)展寄存器卻要通過(guò)23號(hào)寄存器和21號(hào)寄存器聯(lián)合才能實(shí)現(xiàn)讀寫(xiě)操作。內(nèi)核接收到模塊的符號(hào)引用以后,為驅(qū)動(dòng)程序模塊分配內(nèi)核內(nèi)存來(lái)容納該模塊,并將網(wǎng)卡驅(qū)動(dòng)的模塊添加到內(nèi)核模塊鏈表的尾部。為了性能和靈活性方面的考慮,linux沒(méi)有直接返回網(wǎng)卡私有數(shù)據(jù)結(jié)構(gòu)。正如我們所看到的一樣,廣播地址是48bit的全1(0xFF)的地址。devtx_queue_len = 10000。privinstance = phnx_mac_devices[i].instance。在驅(qū)動(dòng)程序中,為了能在多處理器上同步執(zhí)行,在設(shè)備的私有數(shù)據(jù)結(jié)構(gòu)中設(shè)置了privspill_configured這個(gè)特征值,在初始化時(shí),該值設(shè)置為0,表示還沒(méi)有被初始化,而一但設(shè)備驅(qū)動(dòng)程序被執(zhí)行后,該字段的值被重新設(shè)置為1,任何時(shí)候要執(zhí)行設(shè)備的初始化,都必須先檢測(cè)該字段,如果該值為1,則表明已經(jīng)初始化,否則進(jìn)行初始化操作。struct timer_list { … unsigned long expires。設(shè)備的關(guān)閉與設(shè)備的打開(kāi)的操作是互逆的,要完成的工作很少,不象打開(kāi)時(shí)那么的復(fù)雜,但是有些操作卻不是和打開(kāi)時(shí)一樣的。在進(jìn)行數(shù)據(jù)傳輸時(shí),負(fù)責(zé)傳輸?shù)姆椒ㄒ紫却_認(rèn)當(dāng)前的是哪一個(gè)線程在進(jìn)行數(shù)據(jù)的傳輸。前面曾提到,設(shè)備端口的打開(kāi)是在自旋鎖的保護(hù)下進(jìn)行的,現(xiàn)在已經(jīng)完成了設(shè)備的初始化急打開(kāi)任務(wù),就必須解除自旋鎖。通常情況下,網(wǎng)絡(luò)接口只是接收MAC目的地址是自己或者廣播的數(shù)據(jù)包,但在專用網(wǎng)絡(luò)設(shè)備上,我們可以通過(guò)改變網(wǎng)卡的這一能力,讓它接收任何其它的數(shù)據(jù)包,例如,我們?cè)谶M(jìn)行網(wǎng)絡(luò)偵測(cè)的時(shí)候,就需要網(wǎng)卡可以接收流經(jīng)網(wǎng)絡(luò)的所有數(shù)據(jù)包。開(kāi)發(fā)板上芯片在生產(chǎn)制造時(shí),確立了phnx_mac各個(gè)字段的值,我們只能引用它們,而不應(yīng)該試圖去修改,但是中斷號(hào)irq是可以通過(guò)用戶空間的ifconfig命令修改的。因?yàn)閾碛凶孕i的時(shí)間越長(zhǎng),其他處理器就不得不自旋以等待該自旋鎖的時(shí)間就越長(zhǎng),而它不得不永遠(yuǎn)自旋的可能性就越大。devdo_ioctl = rmi_phnx_mac_do_ioctl。ether_setup(dev);上面這個(gè)步驟是以太網(wǎng)網(wǎng)卡設(shè)備指針在進(jìn)行自己特有的初始化之前,對(duì)設(shè)備結(jié)構(gòu)體的大部分值使用以太網(wǎng)默認(rèn)值進(jìn)行初始化,具體的包括:devchange_mtu= eth_change_mtu;devhard_header= eth_header;devrebuild_header = eth_rebuild_header;devset_mac_address= eth_mac_addr;devhard_header_cache = eth_header_cache;devheader_cache_update= eth_header_cache_update;devhard_header_parse = eth_header_parse;devtype= ARPHRD_ETHER;devhard_header_len= ETH_HLEN;devmtu= ETH_DATA_LEN;devaddr_len= ETH_ALEN;devtx_queue_len = 1000;devflag= IFF_BROADCAST|IFF_MULTICAST; memset(devbroadcast,0xFF, ETH_ALEN);分析上面的代碼,很容易的看出,在ether_setup中所進(jìn)行的初始化操作。在設(shè)備完成注冊(cè)以后,%d將被一個(gè)從0開(kāi)始的可用數(shù)字替代,使之成為唯一的名字。 網(wǎng)卡驅(qū)動(dòng)實(shí)現(xiàn)由于網(wǎng)卡驅(qū)動(dòng)的代碼量大,文章不會(huì)將所有的代碼全部列出進(jìn)行討論,而只是將最具有代表意義和普遍意義的代碼挑選出來(lái)進(jìn)行解釋和分析。 寄存器的訪問(wèn)方式上面已經(jīng)說(shuō)過(guò),MAC對(duì)PHY寄存器的讀寫(xiě)訪問(wèn)是通過(guò)MII接口實(shí)現(xiàn)的。,目前正在做驅(qū)動(dòng)程序,相信很快就可以完成移植。如果注冊(cè)成功,則調(diào)用init函數(shù)指針?biāo)赶虻某跏蓟瘮?shù)來(lái)對(duì)設(shè)備初始化,將設(shè)備的net_device數(shù)據(jù)結(jié)構(gòu)插入到鏈表的末尾。返回data前面的可用空間數(shù)量,也即是有多少字節(jié)能夠保存在該緩沖區(qū)中。(2) 作用于緩沖區(qū)的函數(shù)使用sk_buff[11]結(jié)構(gòu)的網(wǎng)絡(luò)驅(qū)動(dòng)程序通過(guò)一些正式的接口函數(shù)來(lái)操作該結(jié)構(gòu)。(1). 重要成員變量這里描述的成員都是驅(qū)動(dòng)程序需要訪問(wèn)的。完整的數(shù)據(jù)包(協(xié)議頭和所有)包含在一個(gè)套接字緩存區(qū)(sk_buff)結(jié)構(gòu)中。另外,設(shè)備地址必須以特定于設(shè)備的方式從接口板卡中讀出,驅(qū)動(dòng)程序應(yīng)當(dāng)將它拷貝到dev_addr中。如果設(shè)備是一個(gè)嶄新的類(lèi),就需要手工設(shè)置下面的成員:unsigned short hard_header_len。這個(gè)值常常在啟動(dòng)或者加載時(shí)設(shè)置,其后可以通過(guò)ifconfig命令修改。如果設(shè)置了這個(gè)指針,這個(gè)函數(shù)被 register_netdev 調(diào)用來(lái)完成對(duì)net_device結(jié)構(gòu)的初始化。如某些協(xié)議里的超時(shí)處理,沒(méi)有中斷機(jī)制的硬件的輪詢等,操作系統(tǒng)應(yīng)為驅(qū)動(dòng)程序提供定時(shí)機(jī)制,一般是在預(yù)定的時(shí)間過(guò)了以后,系統(tǒng)自動(dòng)回調(diào)注冊(cè)的時(shí)鐘函數(shù)。Unix訪問(wèn)網(wǎng)絡(luò)接口的方法仍然是給他們分配一個(gè)唯一的名字(比如eth0),但這個(gè)名字在文件系統(tǒng)中不存在對(duì)應(yīng)的節(jié)點(diǎn)。device)三種:字符設(shè)備:字符(char)設(shè)備是個(gè)能夠像字節(jié)流一樣被訪問(wèn)的設(shè)備,由字符設(shè)備的驅(qū)動(dòng)程序來(lái)實(shí)現(xiàn)這種特性。Linux的模塊機(jī)制使得內(nèi)核保持獨(dú)立而又易于擴(kuò)充。作為Unix類(lèi)操作系統(tǒng),Linux內(nèi)核具有下列基本特征:Linux內(nèi)核的組織形式為整體式結(jié)構(gòu)。 Linux概述Linux操作系統(tǒng)是UNIX操作系統(tǒng)的一種克隆版本,最早是由芬蘭大學(xué)的學(xué)生Linus Torvalds于1991年開(kāi)始開(kāi)發(fā)的,并于1991年的10月5日第一次正式向外公布,以后借助于互聯(lián)網(wǎng),經(jīng)過(guò)一群遍布于全世界的Internet上的自愿參加的程序員的不懈努力,加上計(jì)算機(jī)公司的支持,Linux的影響和應(yīng)用日益廣泛,發(fā)展成為目前世界上用戶最多的一種類(lèi)UNIX操作系統(tǒng)。Linux核心已經(jīng)實(shí)現(xiàn)了OSI參考模型的網(wǎng)絡(luò)層及更上層部分。文章在分析了Linux 網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)程序的結(jié)構(gòu)組成和工作原理之后,重點(diǎn)探討了Linux環(huán)境下網(wǎng)卡驅(qū)動(dòng)程序的開(kāi)發(fā)技術(shù),詳細(xì)討論了實(shí)用的網(wǎng)卡驅(qū)動(dòng)的開(kāi)發(fā)流程及具體實(shí)現(xiàn)細(xì)節(jié)。闡述了該驅(qū)動(dòng)程序在開(kāi)發(fā)時(shí)的需求分析。第三對(duì)Linux環(huán)境下網(wǎng)卡驅(qū)動(dòng)程序的設(shè)計(jì)與實(shí)現(xiàn)作了理論上的探討,重點(diǎn)從網(wǎng)卡驅(qū)動(dòng)模塊的加載、網(wǎng)絡(luò)設(shè)備的初始化、設(shè)備打開(kāi)與關(guān)閉、數(shù)據(jù)的發(fā)送與接收、信息統(tǒng)計(jì)、網(wǎng)卡驅(qū)動(dòng)模塊的卸載等方面按步驟的進(jìn)行了詳細(xì)的討論。 國(guó)內(nèi)外研究現(xiàn)狀、目的及意義Linux是由芬蘭的赫爾辛基大學(xué) (Helsinki)學(xué)生Linus Torvalds把Minix 系統(tǒng)向x86移植的結(jié)果。網(wǎng)絡(luò)層的實(shí)現(xiàn)依靠于數(shù)據(jù)鏈路層的有效工作。Linux 目前是計(jì)算機(jī)技術(shù)的一大熱點(diǎn)之一,最近幾年在我國(guó)得到迅猛發(fā)展,被廣泛應(yīng)用在嵌入式系統(tǒng)、安全產(chǎn)品、服務(wù)器和桌面應(yīng)用等領(lǐng)域。整個(gè)Linux內(nèi)核由很多過(guò)程組成,每個(gè)過(guò)程可以獨(dú)立編譯,然后用連接程序?qū)⑵溥B接在一起成為一個(gè)完整的目標(biāo)程序。模塊機(jī)制可以使內(nèi)核很容易地增加一個(gè)新的模塊(如一個(gè)新的設(shè)備驅(qū)動(dòng)程序),而無(wú)需重新編譯內(nèi)核;同時(shí),模塊機(jī)制還可以把一個(gè)模塊按需添加到內(nèi)核或從內(nèi)核中卸下,這使得我們可以按需要定制自己的內(nèi)核。字符終端(/dev/console)和串口(/dev/ttys0以及類(lèi)似設(shè)備)就是兩個(gè)字符設(shè)備,他們能夠良好的說(shuō)明“流”這種抽象概念。內(nèi)核和網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)程序間的通信,完全不同于內(nèi)核和字符設(shè)備以及塊驅(qū)動(dòng)程序之間的通信,內(nèi)核調(diào)用一套和數(shù)據(jù)傳輸相關(guān)的函數(shù)而不是read、write等。在網(wǎng)絡(luò)驅(qū)動(dòng)程序中,如果硬件沒(méi)有中斷功能,定時(shí)器可以提供輪詢(poll)方式對(duì)硬件進(jìn)行存取,或者是實(shí)現(xiàn)某些協(xié)議時(shí)需要的超時(shí)重傳等。大部分現(xiàn)代的網(wǎng)絡(luò)驅(qū)動(dòng)程序不再使用這個(gè)函數(shù)了,相反,它們是在注冊(cè)接口前完成初始化工作的。unsigned char if_port;指定在多端口設(shè)備中使用哪個(gè)端口。硬件頭部長(zhǎng)度,即數(shù)據(jù)包中位于IP頭、或者其他協(xié)議信息之前的octet數(shù)目。unsigned short flags;int features;這個(gè)flags 成員是一個(gè)位掩碼。int (*hard_header) (struct sk_buff *skb, struct net_device *dev, unsigned short type, void *daddr, void *saddr, unsigned len);該方法根據(jù)之前取到的源和目的硬件地址來(lái)建立硬件頭,并在 hard_start_xmit前被調(diào)用。struct net_device *dev;接收或發(fā)送該緩沖區(qū)的設(shè)備。下面是最常用的幾個(gè)函數(shù)。void skb_reserve(struct sk_buff *skb, int len);這個(gè)函數(shù)遞增data和tail。初始化結(jié)束后,就可以打開(kāi)設(shè)備進(jìn)行數(shù)據(jù)包的發(fā)送和接收。MII(Media Independent Interface介質(zhì)無(wú)關(guān)接口)[13],或稱為媒體獨(dú)立接口。標(biāo)準(zhǔn)寄存器都可以通過(guò)MII接口直接訪問(wèn),但是擴(kuò)展的寄存器中,大部分是不能直接訪問(wèn)的。 模塊的加載及設(shè)備初始化驅(qū)動(dòng)程序在linux系統(tǒng)內(nèi)核里被編譯成模塊,并在系統(tǒng)啟動(dòng)的時(shí)候加載到系統(tǒng)里面。priv = netdev_priv(dev);privdev = dev;以太網(wǎng)設(shè)備申請(qǐng)成功以后,調(diào)用系統(tǒng)函數(shù)netdev_priv函數(shù)初始化網(wǎng)卡的私有數(shù)據(jù)結(jié)構(gòu),并將私有數(shù)據(jù)結(jié)構(gòu)中的設(shè)備指針指向自己。其實(shí)這個(gè)初始化操作比較的簡(jiǎn)單,沒(méi)有我們想象的那么復(fù)雜,函數(shù)指針的初始化主要集中在了對(duì)最大傳輸單元改變的控制函數(shù),以及對(duì)物理地址進(jìn)行分析、處理、更新、解析等等。devtx_timeout = rmi_phnx_mac_tx_timeout。長(zhǎng)的鎖擁有時(shí)間將阻止對(duì)當(dāng)前處理器的調(diào)度,這意味著更高優(yōu)先級(jí)的進(jìn)程,也即是系統(tǒng)真正應(yīng)該獲得CPU的進(jìn)程不得不等待。端口類(lèi)型type都設(shè)置為了TYPE_GMAC,表示千兆的通信能力,而實(shí)際上芯片還具有TYPE_XGMAC的通信能力,它是TYPE_GMAC的十倍通信能力,不過(guò)在此次開(kāi)發(fā)中并未使用TYPE_XGMAC,因?yàn)閷?duì)于開(kāi)發(fā)來(lái)說(shuō),這意味著具有更大的挑戰(zhàn)性和難度,而且在實(shí)際的主設(shè)備中,暫時(shí)還不需