Linux網(wǎng)絡(luò)設(shè)備及及分析_第1頁
Linux網(wǎng)絡(luò)設(shè)備及及分析_第2頁
Linux網(wǎng)絡(luò)設(shè)備及及分析_第3頁
Linux網(wǎng)絡(luò)設(shè)備及及分析_第4頁
Linux網(wǎng)絡(luò)設(shè)備及及分析_第5頁
已閱讀5頁,還剩20頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

1、Linux網(wǎng)絡(luò)設(shè)備分析摘要 在本文中,首先概括了網(wǎng)絡(luò)設(shè)備總體特征和工作原理,接著在分析了一個重要的數(shù)據(jù)結(jié)構(gòu)device后,重點剖析了網(wǎng)絡(luò)設(shè)備的整個初始化工作過程;簡單地分析了設(shè)備的打開和關(guān)閉的操作后,是有關(guān)數(shù)據(jù)包的傳輸和接收的分析;在最后,本文對寫網(wǎng)絡(luò)設(shè)備驅(qū)動程序做了一個總結(jié)。以上的每部分的分析,都是在NE2000以太網(wǎng)卡的基礎(chǔ)上進(jìn)行的。在附錄中是一個虛擬的字符設(shè)備驅(qū)動程序以及寫這個程序的體會,該程序已成功使用過,它是在網(wǎng)絡(luò)設(shè)備分析之前本人做的一個小小的試驗。網(wǎng)絡(luò)設(shè)備概述在LINUX中,為了簡化對設(shè)備的管理,所有外圍的硬件設(shè)備被歸結(jié)為三類:字符設(shè)備(如鍵盤、鼠標(biāo)等)、塊設(shè)備(如硬盤、光驅(qū)、軟驅(qū)

2、等)和網(wǎng)絡(luò)設(shè)備(也稱為網(wǎng)絡(luò)接口,network inferface),如以太網(wǎng)卡。在本文中,我們將等效使用“網(wǎng)絡(luò)設(shè)備”和“網(wǎng)絡(luò)接口”這兩個概念,而對某個具體的網(wǎng)絡(luò)設(shè)備,我們將稱之為“物理設(shè)備”或“物理網(wǎng)絡(luò)設(shè)備”。為了屏蔽網(wǎng)絡(luò)環(huán)境中物理網(wǎng)絡(luò)設(shè)備的多樣性,LINUX對所有的物理設(shè)備進(jìn)行抽象并定義了一個統(tǒng)一的概念,稱之為接口(Interface)。所有對網(wǎng)絡(luò)硬件的訪問都是通過接口進(jìn)行的,接口提供了一個對所有類型的硬件一致化的操作集合來處理基本數(shù)據(jù)的發(fā)送和接收。一個網(wǎng)絡(luò)接口被看作是一個發(fā)送和接收數(shù)據(jù)包(packets)的實體。對于每個網(wǎng)絡(luò)接口,都用一個device的數(shù)據(jù)結(jié)構(gòu)表示,有關(guān)該數(shù)據(jù)結(jié)構(gòu)的具體內(nèi)

3、容,將在本文的后面詳細(xì)介紹。通常,網(wǎng)絡(luò)設(shè)備是一個物理設(shè)備如以太網(wǎng)卡,但軟件也可以作為網(wǎng)絡(luò)設(shè)備,如回送設(shè)備(loopback)。在內(nèi)核啟動時,通過網(wǎng)絡(luò)設(shè)備驅(qū)動程序,將登記存在的網(wǎng)絡(luò)設(shè)備。設(shè)備用標(biāo)準(zhǔn)的支持網(wǎng)絡(luò)的機(jī)制來轉(zhuǎn)遞收到的數(shù)據(jù)到相應(yīng)的網(wǎng)絡(luò)層。所有被發(fā)送和接收的包都用數(shù)據(jù)結(jié)構(gòu)sk_buff表示。這是一個具有很好的靈活性的數(shù)據(jù)結(jié)構(gòu),可以很容易增加或刪除網(wǎng)絡(luò)協(xié)議數(shù)據(jù)包的首部。網(wǎng)絡(luò)設(shè)備作為其中的三類設(shè)備之一,它有其非常特殊的地方。它與字符設(shè)備及塊設(shè)備都有很大的不同:網(wǎng)絡(luò)接口不存在于Linux的文件系統(tǒng)中,而是在核心中用一個device數(shù)據(jù)結(jié)構(gòu)表示的。每一個字符設(shè)備或塊設(shè)備則在文件系統(tǒng)中都存在一個相應(yīng)的

4、特殊設(shè)備文件來表示該設(shè)備,如/dev/hda1、/dev/sda1、/dev/tty1等。網(wǎng)絡(luò)設(shè)備在做數(shù)據(jù)包發(fā)送和接收時,直接通過接口訪問,不需要進(jìn)行文件的操作;而對字符設(shè)備和塊設(shè)備的訪問都需通過文件操作界面。網(wǎng)絡(luò)接口是在系統(tǒng)初始化時實時生成的,對于核心支持的但不存在的物理網(wǎng)絡(luò)設(shè)備,將不可能有與之相對應(yīng)的device結(jié)構(gòu)。而對于字符設(shè)備和塊設(shè)備,即使該物理設(shè)備不存在,在/dev下也必定有相應(yīng)的特殊文件與之相對應(yīng)。且在系統(tǒng)初始化時,核心將會對所有內(nèi)核支持的字符設(shè)備和塊設(shè)備進(jìn)行登記,初始化該設(shè)備的文件操作界面(struct file_operations),而不管該設(shè)備在物理上是否存在。以上兩點是

5、網(wǎng)絡(luò)設(shè)備與其他設(shè)備之間存在的最主要的不同。然而,它們之間又有一些共同之處,如在系統(tǒng)中一個網(wǎng)絡(luò)設(shè)備的角色和一個安裝的塊設(shè)備相似。一個塊設(shè)備在blk_dev數(shù)組及核心其他的數(shù)據(jù)結(jié)構(gòu)中登記自己,然后根據(jù)請求,通過自己的request_function函數(shù)“發(fā)送”和“接收”數(shù)據(jù)塊。相似地,為了能與外面世界進(jìn)行數(shù)據(jù)交流,一個網(wǎng)絡(luò)接口也必須在一個特殊的數(shù)據(jù)結(jié)構(gòu)中登記自己。在系統(tǒng)內(nèi)核中,存在字符設(shè)備管理表chardevs和塊設(shè)備管理表blkdevs,這兩張保存著指向file_operations結(jié)構(gòu)的指針的設(shè)備管理表,分別用來描述各種字符驅(qū)動程序和塊設(shè)備驅(qū)動程序。類似地,在內(nèi)核中也存在著一張網(wǎng)絡(luò)接口管理表d

6、ev_base,但與前兩張表不同,dev_base是指向device結(jié)構(gòu)的指針,因為網(wǎng)絡(luò)設(shè)備是通過device數(shù)據(jù)結(jié)構(gòu)來表示的。dev_base實際上是一條device結(jié)構(gòu)鏈表的表頭,在系統(tǒng)初始化完成以后,系統(tǒng)檢測到的網(wǎng)絡(luò)設(shè)備將自動地保存在這張鏈表中,其中每一個鏈表單元表示一個存在的物理網(wǎng)絡(luò)設(shè)備。當(dāng)要發(fā)送數(shù)據(jù)時,網(wǎng)絡(luò)子系統(tǒng)將根據(jù)系統(tǒng)路由表選擇相應(yīng)的網(wǎng)絡(luò)接口進(jìn)行數(shù)據(jù)傳輸,而當(dāng)接收到數(shù)據(jù)包時,通過驅(qū)動程序登記的中斷服務(wù)程序進(jìn)行數(shù)據(jù)的接收處理(軟件網(wǎng)絡(luò)接口除外)。以下是網(wǎng)絡(luò)設(shè)備工作原理圖:圖一 Linux網(wǎng)絡(luò)設(shè)備工作原理圖每一個具體的網(wǎng)絡(luò)接口都應(yīng)該有一個名字,以在系統(tǒng)中能唯一標(biāo)識一個網(wǎng)絡(luò)接口。通常一

7、個名字僅表明該接口的類型。Linux對網(wǎng)絡(luò)設(shè)備命名有以下約定:(其中N為一個非負(fù)整數(shù))ethN以太網(wǎng)接口,包括10Mbps和100Mbps;trN令牌環(huán)接口;slN SLIP網(wǎng)絡(luò)接口;pppNPPP網(wǎng)絡(luò)接口,包括同步和異步;plipNPLIP網(wǎng)絡(luò)接口,其中N與打印端口號相同;tunlNIPIP壓縮頻道網(wǎng)絡(luò)接口;nrN NetROM虛擬設(shè)備接口;isdnNISDN網(wǎng)絡(luò)接口;dummyN空設(shè)備;lo回送網(wǎng)絡(luò)接口。重要數(shù)據(jù)結(jié)構(gòu)struct device結(jié)構(gòu)device存儲一個網(wǎng)絡(luò)接口的重要信息,是網(wǎng)絡(luò)驅(qū)動程序的核心。在邏輯上,它可以分割為兩個部分:可見部分和隱藏部分??梢姴糠质怯赏獠抠x值;隱藏部分的

8、域段僅面向系統(tǒng)內(nèi)部,它們可以隨時被改變。下面我們將對之進(jìn)行詳細(xì)的分析和解剖。/* from include/linux/ */struct device 屬性 char*name;設(shè)備的名字。如果第一字符為NULL(即0),register_netdev (drivers/net/將會賦給它一個n最小的可用網(wǎng)絡(luò)設(shè)備名ethn。 unsigned longrmem_end;/* shmem recv end*/ unsigned longrmem_start;/* shmem recv start*/ unsigned longmem_end;/* shared mem end*/ unsign

9、ed longmem_start;/* shared mem start*/這些域段標(biāo)識被設(shè)備使用的共享內(nèi)存的首地址及尾地址。如果設(shè)備用來接收和發(fā)送的內(nèi)存塊不同,則mem域段用來標(biāo)識發(fā)送的內(nèi)存位置,rmem用來標(biāo)識接收的內(nèi)存位置。mem_start和mem_end可在系統(tǒng)啟動時用內(nèi)核的命令行指定,用ifconfig可以查看它們的值。rmem域段從來不被驅(qū)動程序以外的程序所引用。 unsigned longbase_addr;/* device I/O address*/ unsigned charirq;/* device IRQ number*/I/O基地址和中斷號。它們都是在設(shè)備檢測期間被

10、賦值的,但也可以在系統(tǒng)啟動時指定傳入(如傳給LILO)。ifconfig命令可顯示及修改他們的當(dāng)前值。 volatile unsigned charstart;/* start an operation*/ volatile unsigned charinterrupt;/* interrupt arrived*/這是兩個二值的低層狀態(tài)標(biāo)志。通常在設(shè)備打開時置start標(biāo)志,在設(shè)備關(guān)閉時清start標(biāo)志。當(dāng)interrupt置位時,表示有一個中斷已到達(dá)且正在進(jìn)行中斷服務(wù)程序理。 unsigned longtbusy;/* transmitter busy must be long for bi

11、tops */標(biāo)識“發(fā)送忙”。在驅(qū)動程序不能接受一個新的需傳輸?shù)陌鼤r,該域段應(yīng)該為非零。 struct device*next;指向下一個網(wǎng)絡(luò)設(shè)備,用于維護(hù)鏈表。 unsigned charif_port;記錄哪個硬件I/O端口正在被接口所用,如BNC,AUI,TP等(drivers/net/)。 unsigned chardma;設(shè)備用的DMA通道。一些設(shè)備可能需要以上兩個域段,但非必需的。 unsigned longtrans_start;/* Time (in jiffies) of last Tx*/上次傳輸?shù)臅r間點(in jiffies) unsigned longlast_rx;/

12、* Time of last Rx*/上次接收的時間點(in jiffies)。如trans_start可用來幫助內(nèi)核檢測數(shù)據(jù)傳輸?shù)乃梨i(lockup)。 unsigned shortflags;/* interface flags (a la BSD)*/該域描述了網(wǎng)絡(luò)設(shè)備的能力和特性。它包括以下flags:(include/linux/)IFF_UP 表示接口在運行中。當(dāng)接口被激活時,內(nèi)核將置該標(biāo)志位。IFF_BROADCAST 表示設(shè)備中的廣播地址時有效的。以太網(wǎng)支持廣播。IFF_DEBUG 調(diào)試模式,表示設(shè)備調(diào)試打開。當(dāng)想控制printk及其他一些基于調(diào)試目的的信息顯示時,可利用這個標(biāo)

13、志位。雖然當(dāng)前沒有正式的驅(qū)動程序使用它,但它可以在程序中通過ioctl來設(shè)置從而使用它。IFF_LOOPBACK 表示這是一個回送(loopback)設(shè)備,回送接口應(yīng)該置該標(biāo)志位。核心是通過檢查此標(biāo)志位來判斷設(shè)備是否是回送設(shè)備的,而不是看設(shè)備的名字是否是lo。IFF_POINTTOPOINT 表示這是一個點對點鏈接(SLIP and PPP),點對點接口必須置該標(biāo)志位。Ifconfig也可以置此標(biāo)志位及清除它。若置上該標(biāo)志位,則dev-dstaddr應(yīng)也相應(yīng)的置為鏈接對方的地址。IFF_MASTER/* master of a load balancer */IFF_SLAVE/* slave

14、 of a load balancer*/此兩個標(biāo)志位在裝入平等化中要用到。IFF_NOARP 表示不支持ARP協(xié)議。通常的網(wǎng)絡(luò)接口能傳輸ARP包,如果想讓接口不執(zhí)行ARP,可置上該標(biāo)志位。如點對點接口不需要運行ARP。IFF_PROMISC 全局接受模式。在該模式下,設(shè)備將接受所有的包,而不關(guān)這些包是發(fā)給誰的。在缺省情況下,以太網(wǎng)接口會使用硬件過濾,以保證只接受廣播包及發(fā)給本網(wǎng)絡(luò)接口的包。Sniff的原理就是通過設(shè)置網(wǎng)絡(luò)接口為全局接受模式,接受所有到達(dá)本接口媒介的包,來“偷聽”本子網(wǎng)的“秘密”。IFF_MULTICAST 能接收多點傳送的IP包,具有多點傳輸?shù)哪芰?。ether_setup缺省

15、是置該標(biāo)志位的,故若不想支持多點傳送,必須在初始化時清除該標(biāo)志位。IFF_ALLMULTI 接收所有多點傳送的IP包。IFF_NOTRAILERS/*無網(wǎng)絡(luò)TRAILER*/IFF_RUNNING/*資源被分配*/此標(biāo)志在Linux中沒什么用,只是為了與BSD兼容。 unsigned shortfamily;/* address family ID (AF_INET)*/該域段標(biāo)識本設(shè)備支持的協(xié)議地址簇。大部分為AF_INET(英特網(wǎng)IP協(xié)議),接口通常不需要用這個域段或賦值給它。 unsigned shortmetric;/* routing metric (not used)*/ unsi

16、gned shortmtu;不包括數(shù)據(jù)鏈路層幀首幀尾的最大傳輸單位(Maximum Transfer Unit)。網(wǎng)絡(luò)層在包傳輸時要用到。對以太網(wǎng)而言,該域段為1500,不包括MAC幀的幀首和幀尾(MAC幀格式稍后所示)。 unsigned shorttype;/* interface hardware type*/接口的硬件類型,描述了與該網(wǎng)絡(luò)接口綁在一起的媒介類型。Linux網(wǎng)絡(luò)設(shè)備支持許多不同種類的媒介,如以太網(wǎng),令牌環(huán),SLIP,PPP,Apple Localtalk等。ARP在判定接口支持哪種類型的物理地址時要用到該域段。若是以太網(wǎng)接口,則在ether_setup中將之設(shè)為ARPHR

17、D_ETHER(Ethernet 10Mbps)。 unsigned shorthard_header_len;/* hardware hdr length*/在被傳送的包中IP頭之前的字節(jié)數(shù)。對于以太網(wǎng)接口,該域段為14(ETH_HLEN,includelinux),這個值可由MAC幀的格式得出:MAC幀格式:目的地址(6字節(jié))+ 源地址(6字節(jié))+ 數(shù)據(jù)長度(2字節(jié))+ 數(shù)據(jù)(461500)+FCS void*priv;/* pointer to private data*/該指針指向私有數(shù)據(jù),通常該數(shù)據(jù)結(jié)構(gòu)中包括struct enet_statistics。類似于struct file的

18、private_data指針,但priv指針是在設(shè)備初始化時被分配內(nèi)存空間的(而不是在設(shè)備打開時),因為該指針指向的內(nèi)容包括設(shè)備接口的統(tǒng)計數(shù)據(jù),而這些數(shù)據(jù)即使在接口卸下(down)時也應(yīng)可以得到的,如用戶通過ifconfig查看。 unsigned charpad;/* make dev_addr aligned to 8 bytes */ unsigned charbroadcastMAX_ADDR_LEN;/* hw bcast add */廣播地址由六個0 xff構(gòu)成,即表示。memset(dev-broadcast,0 xFF, ETH_ALEN); (drivers/net/) un

19、signed chardev_addrMAX_ADDR_LEN;/* hw address*/設(shè)備的物理地址。當(dāng)包傳送給驅(qū)動程序傳輸時,要用物理地址來產(chǎn)生正確的幀首。 unsigned charaddr_len;/* hardware address length*/物理地址的長度。以太網(wǎng)網(wǎng)卡的物理地址為6字節(jié)(ETH_ALEN)。 unsigned longpa_addr;/* protocol address*/ unsigned longpa_brdaddr;/* protocol broadcast addr*/ unsigned longpa_mask;/* protocol net

20、mask */該三個域段分別描述接口的協(xié)議地址、協(xié)議廣播地址和協(xié)議的網(wǎng)絡(luò)掩碼。若dev-family為AF_INET,則它們即為IP地址。這些域段可用ifconfig賦值。 unsigned shortpa_alen;/* protocol address length*/協(xié)議地址的長度。AF_INET的為4。 unsigned longpa_dstaddr;/* protocol P-P other side addr*/點對點協(xié)議接口(如SLIP、PPP)用這個域記錄連接另一邊的IP值。 struct dev_mc_list*mc_list;/* Multicast mac address

21、es*/ intmc_count;/* Number of installed mcasts*/ struct ip_mc_list*ip_mc_list;/* IP multicast filter chain */這三個域段用于處理多點傳輸。其中mc_count表示mc_list中的項目數(shù)。 _u32tx_queue_len;/* Max frames per queue allowed */一個設(shè)備的傳輸隊列能容納的最大的幀數(shù)。對以太網(wǎng),缺省為100;而plip則為節(jié)省系統(tǒng)資源,僅設(shè)為10。 /* For load balancing driver pair support */ uns

22、igned longpkt_queue;/* Packets queued */ struct device*slave;/* Slave device */ struct net_alias_info*alias_info;/* main dev alias info */ struct net_alias*my_alias;/* alias devs */ struct sk_buff_headbuffsDEV_NUMBUFFS;指向網(wǎng)絡(luò)接口緩沖區(qū)的指針。服務(wù)處理程序以下是一些對網(wǎng)絡(luò)接口的操作,類似與字符設(shè)備和塊設(shè)備。網(wǎng)絡(luò)接口操作可以分為兩部分,一部分為基本操作,即每個網(wǎng)絡(luò)接口都必須有的操

23、作;另一部分是可選操作。/* 基本操作 */ int(*init) (struct device *dev); /* Called only once. */初始化函數(shù)的指針,僅被調(diào)用一次。當(dāng)?shù)怯浺粋€設(shè)備時,核心一般會讓驅(qū)動程序初始化該設(shè)備。初始化函數(shù)功能包括以下內(nèi)容:檢測設(shè)備是否存在;自動檢測該設(shè)備的I/O端口和中斷號;填寫該設(shè)備device結(jié)構(gòu)的大部分域段;用kmalloc分配所需的內(nèi)存空間等。若初始化失敗,該設(shè)備的device結(jié)構(gòu)就不會被鏈接到全局的網(wǎng)絡(luò)設(shè)備表上。在系統(tǒng)啟動時,每個驅(qū)動程序都試圖登記自己,當(dāng)只有那些實際存在的設(shè)備才會登記成功。這與用主設(shè)備號及次設(shè)備號索引的字符設(shè)備和塊設(shè)備

24、不同。 int(*open) (struct device *dev);打開網(wǎng)絡(luò)接口。每當(dāng)接口被ifconfig激活時,網(wǎng)絡(luò)接口都要被打開。Open操作做以下工作:登記一些需要的系統(tǒng)資源,如IRQ、DMA、I/O端口等;打開硬件;將module使用計數(shù)器加一。 int(*stop) (struct device *dev);停止網(wǎng)絡(luò)接口。操作內(nèi)容與open相逆。 int(*hard_start_xmit) (struct sk_buff *skb, struct device *dev);硬件開始傳輸。這個操作請求對一個包的傳輸,這個包原保存在一個socket緩沖區(qū)結(jié)構(gòu)中(sk_buff)。

25、int(*hard_header) (struct sk_buff *skb, struct device *dev, unsigned short type, void *daddr, void *saddr, unsigned len);這個函數(shù)可根據(jù)先前得到的源物理地址和目的物理地址建立硬件頭(hardware header)。以太網(wǎng)接口的缺省函數(shù)是eth_header。 int(*rebuild_header)(void *eth, struct device *dev, unsigned long raddr, struct sk_buff *skb);在一個包被發(fā)送之前重建硬件頭。

26、對于以太網(wǎng)設(shè)備,若有未知的信息,缺省函數(shù)將使用ARP填寫。 struct enet_statistics*(*get_stats)(struct device *dev);當(dāng)一個應(yīng)用程序需要知道網(wǎng)絡(luò)接口的一些統(tǒng)計數(shù)據(jù)時,可調(diào)用該函數(shù),如ifconfig、netstat等。/* 可選操作 */ void(*set_multicast_list)(struct device *dev);設(shè)置多點傳輸?shù)牡刂锋湵恚?mc_list)。 int(*set_mac_address)(struct device *dev, void *addr);改變硬件的物理地址。如果網(wǎng)絡(luò)接口支持改變它的硬件物理地址,就

27、可用這個操作。許多硬件不支持該功能。 int(*do_ioctl)(struct device *dev, struct ifreq *ifr, int cmd);執(zhí)行依賴接口的ioctl命令。 int(*set_config)(struct device *dev, struct ifmap *map);改變接口配置。設(shè)備的I/O地址和中斷號可以通過該函數(shù)進(jìn)行實時修改。 void(*header_cache_bind)(struct hh_cache *hhp, struct device *dev, unsigned short htype, _u32 daddr); void(*head

28、er_cache_update)(struct hh_cache *hh, struct device *dev, unsigned char * haddr); int(*change_mtu) (struct device *dev, int new_mtu);這個函數(shù)負(fù)責(zé)使接口MTU改變后生效。如果當(dāng)MTU改變時驅(qū)動程序要作一些特殊的事情,就應(yīng)該寫這個函數(shù)。 struct iw_statistics*(*get_wireless_stats) (struct device *dev);網(wǎng)絡(luò)設(shè)備的初始化網(wǎng)絡(luò)設(shè)備的初始化主要工作是檢測設(shè)備的存在、初始化設(shè)備的device結(jié)構(gòu)及在系統(tǒng)中登記該設(shè)

29、備。類似于字符設(shè)備和快塊設(shè)備,系統(tǒng)內(nèi)核中也存在著一張網(wǎng)絡(luò)接口管理表dev_base,但與dev_base是指向device結(jié)構(gòu)的,因為網(wǎng)絡(luò)設(shè)備是通過device數(shù)據(jù)結(jié)構(gòu)來表示的。dev_base實際上是一條device結(jié)構(gòu)鏈表的表頭,在系統(tǒng)初始化完成以后,系統(tǒng)檢測到的網(wǎng)絡(luò)設(shè)備將自動地保存在這張鏈表中,其中每一個鏈表單元表示一個存在的物理網(wǎng)絡(luò)設(shè)備。登記成功的網(wǎng)絡(luò)設(shè)備必定可在dev_base鏈表中找到。網(wǎng)絡(luò)設(shè)備的初始化從觸發(fā)角度看可分為兩類:一類是由shell命令insmod觸發(fā)的模塊化驅(qū)動程序(module),只有模塊化的網(wǎng)絡(luò)設(shè)備驅(qū)動程序才能用這種方式對設(shè)備進(jìn)行初始化,稱為“模塊初始化模式”;另

30、一類是系統(tǒng)驅(qū)動時由核心自動檢測網(wǎng)絡(luò)設(shè)備并進(jìn)行初始化,我們稱為“啟動初始化模式”。顯然,這兩種初始化模式存在許多不同之處,以下我們對兩者分別進(jìn)行分析。“模塊初始化模式”的分析概述insmod命令將調(diào)用相應(yīng)模塊的init_module(),裝載模塊。init_module函數(shù)在初始化dev-init函數(shù)指針后,將調(diào)用register_netdev()在系統(tǒng)登記該設(shè)備。若登記成功,則模塊裝載成功,否則返回出錯信息。register_netdev首先檢查設(shè)備名是否已確定,若沒賦值則給它一個缺省的值ethN,N為最小的可用以太網(wǎng)設(shè)備號注 在版本的內(nèi)核中,只有以太網(wǎng)設(shè)備的缺省名是在register_net

31、dev中賦值的。對于其他網(wǎng)絡(luò)設(shè)備,一般在其他地方就賦以缺省值,而無需register_netdev處理。如PLIP,在中就預(yù)定了3個PLIP設(shè)備plip0、plip1和plip2注 在版本的內(nèi)核中,只有以太網(wǎng)設(shè)備的缺省名是在register_netdev中賦值的。對于其他網(wǎng)絡(luò)設(shè)備,一般在其他地方就賦以缺省值,而無需register_netdev處理。如PLIP,在中就預(yù)定了3個PLIP設(shè)備plip0、plip1和plip2。若啟動時或裝載模塊時若無指定參數(shù)傳入,則會依次對三個設(shè)備試圖進(jìn)行初始化:for (i=0; i init函數(shù)指針進(jìn)行賦值,對于任何網(wǎng)絡(luò)設(shè)備這一步必不可少!因為在regist

32、er_netdev中要用到該函數(shù)指針;調(diào)用register_netdev,完成檢測、初始化及設(shè)備登記等工作。/* from drivers/net/ */init_module(void)int this_dev, found = 0;/* 對所有可能存在的以太網(wǎng)接口進(jìn)行檢測并試圖去登記,MAX_NE_CARDS為4, * 即最多可以使用4塊NE2000兼容網(wǎng)卡。 */for (this_dev = 0; this_dev name = namelist+(NAMELEN*this_dev);dev-irq = irqthis_dev;dev-base_addr = iothis_dev;de

33、v-init = ne_probe;/* NE2000的檢測和初始化函數(shù) */dev-mem_end = badthis_dev;if (register_netdev(dev) = 0) /* 試圖登記該設(shè)備 */found+;continue;/* 設(shè)備登記成功,繼續(xù)登記下一個設(shè)備 */* 第一次發(fā)生登記不成功事件 */if (found != 0) /* 在這之前沒有成功登記NE2000接口,返回 */return 0;/* 顯示出錯信息 */if (iothis_dev != 0)printk(KERN_WARNING : No NE*000 card found at i/o = %

34、#xn, iothis_dev);elseprintk(KERN_NOTICE : No PCI cards found. Use io=0 xNNN value(s) for register_netdev該函數(shù)實現(xiàn)對網(wǎng)絡(luò)接口的登記功能。其實現(xiàn)步驟如下:首先檢查設(shè)備名是否已確定,若沒賦值則以以太網(wǎng)設(shè)備待之并給它一個缺省的值ethN,N為最小的可用以太網(wǎng)設(shè)備號;然后,網(wǎng)絡(luò)設(shè)備自己的init_function,即剛在init_module中賦值的dev-init,將被調(diào)用,用來實現(xiàn)對網(wǎng)絡(luò)接口的實際的初始化工作。若初始化成功,則將該網(wǎng)絡(luò)接口加到網(wǎng)絡(luò)設(shè)備管理表dev_base的尾部/* from d

35、rivers/net/ */int register_netdev(struct device *dev)struct device *d = dev_base; /* 取得網(wǎng)絡(luò)設(shè)備管理表的表頭指針 */if (dev & dev-init) /*若設(shè)備名字沒確定,則將之看作是以太網(wǎng)設(shè)備!*/if (dev-name &(dev-name0 = 0) | (dev-name0 = ) /* 找到下一個最小的空閑可用以太網(wǎng)設(shè)備名字 */for (i = 0; i name, eth%d, i);printk(loading device %s.n, dev-name);ethdev_indexi

36、 = dev;break;/* 調(diào)用初始化函數(shù)進(jìn)行設(shè)備的初始化 */if (dev-init(dev) != 0) /* 將設(shè)備加到網(wǎng)絡(luò)設(shè)備管理表中,加在最后 */if (dev_base) /* 找到鏈表尾部 */while (d-next)d = d-next;d-next = dev;elsedev_base = dev;dev-next = NULL;init_function函數(shù)原型:int init_function (struct device *dev);當(dāng)系統(tǒng)登記一個網(wǎng)絡(luò)設(shè)備時,核心一般會請求該設(shè)備的驅(qū)動程序初始化自己。初始化函數(shù)功能包括以下內(nèi)容:1.檢測設(shè)備是否存在,一般和

37、第二步一起作;2.自動檢測該設(shè)備的I/O地址和中斷號;對于可以與其他共享中斷號的設(shè)備,我們應(yīng)盡量避免在初始化函數(shù)中登記I/O地址和中斷號,I/O地址和中斷號的登記最好在設(shè)備被打開的時候,因為中斷號有可能被其他設(shè)備所共享。若不準(zhǔn)備和其他設(shè)備共享,則可在此調(diào)用request_irq和request_region馬上向系統(tǒng)登記。3.填寫傳入的該設(shè)備device結(jié)構(gòu)的大部分域段;對于以太網(wǎng)接口,device結(jié)構(gòu)中許多有關(guān)網(wǎng)絡(luò)接口信息都是通過調(diào)用ether_setup函數(shù)(driver/net/)統(tǒng)一來設(shè)置的,因為以太網(wǎng)卡有很好的共性。對于非以太網(wǎng)接口,也有一些類似于ether_setup的函數(shù),如tr

38、_setup(令牌網(wǎng)),fddi_setup。若添加的網(wǎng)絡(luò)設(shè)備都不屬于這些類型,就需要自己填寫device結(jié)構(gòu)的各個分量。需要的內(nèi)存空間。若初始化失敗,該設(shè)備的device結(jié)構(gòu)就不會被鏈接到全局的網(wǎng)絡(luò)設(shè)備表上。在系統(tǒng)啟動時,每個驅(qū)動程序都試圖登記自己,當(dāng)只有那些實際存在的設(shè)備才會登記成功。這與用主設(shè)備號及次設(shè)備號索引的字符設(shè)備和塊設(shè)備不同。物理設(shè)備NE2000兼容網(wǎng)卡的初始化函數(shù)是由ne_probe和ne_probe1及ethdev_init共同實現(xiàn)。/* from drivers/net/ */int ne_probe(struct device *dev) int base_addr =

39、dev dev-base_addr : 0; /* I/O地址. User knows best. */ if (base_addr 0 x1ff)/* I/O地址有指定值 */ return ne_probe1(dev, base_addr); /* 這個函數(shù)在下面分析 */ else if (base_addr != 0)/* 不自動檢測I/O */ return ENXIO; /* base_addr=0,自動檢測,若有第二塊ISA網(wǎng)卡則是一個冒險! * 對所有NE2000可能的I/O地址都進(jìn)行檢測,可能的I/O地址在存在 * netcard_portlist數(shù)組中: * static

40、unsigned int netcard_portlist= 0 x300, 0 x280, 0 x320, 0 x340, 0 x360, 0; */ for (i = 0; netcard_portlisti; i+) int ioaddr = netcard_portlisti; if (check_region(ioaddr, NE_IO_EXTENT) continue; /* 檢測到一個I/O端口地址 */ if (ne_probe1(dev, ioaddr) = 0) return 0;/* from drivers/net/ */static int ne_probe1(str

41、uct device *dev, int ioaddr) /* 檢測、確認(rèn)I/O地址;初始化8390 */ /* 自動檢測中斷號,非常巧妙! */ if (dev-irq irq = autoirq_report(0);/* 獲得剛才產(chǎn)生的中斷號 */ /* 登記中斷號,中斷服務(wù)程序為ei_interrupt。 * 因為ISA網(wǎng)卡不能和其他設(shè)備共享中斷。*/ int irqval = request_irq(dev-irq, ei_interrupt,pci_irq_line SA_SHIRQ : 0, name, dev); if (irqval) printk ( unable to ge

42、t IRQ %d (irqval=%d).n, dev-irq, irqval); return EAGAIN; dev-base_addr = ioaddr;/* 設(shè)置I/O地址已經(jīng)過確認(rèn) */ /* 調(diào)用ethdev_init初始化dev結(jié)構(gòu) */ if (ethdev_init(dev) /* 該函數(shù)下面將分析 */ printk ( unable to get memory for dev-priv.n); free_irq(dev-irq, NULL); /* 初始化不成功,釋放登記的中斷號! */ return -ENOMEM; /* 向系統(tǒng)登記I/O地址 */ request_r

43、egion(ioaddr, NE_IO_EXTENT, name); /* 將硬件的物理地址賦給dev-dev_add */ for(i = 0; i dev_addri = SA_promi; printk(n%s: %s found at %#x, using IRQ %d.n, dev-name, name, ioaddr, dev-irq); /* 向dev結(jié)構(gòu)登記設(shè)備打開和關(guān)閉函數(shù) */ dev-open = &ne_open; dev-stop = &ne_close;/* from drivers/net/ */int ethdev_init(struct device *dev

44、) if (dev-priv = NULL) struct ei_device *ei_local; /* 申請私有數(shù)據(jù)結(jié)構(gòu)空間,用于記錄設(shè)備的狀態(tài)等 */ dev-priv = kmalloc(sizeof(struct ei_device), GFP_KERNEL); dev-hard_start_xmit = &ei_start_xmit; dev-get_stats = get_stats; dev-set_multicast_list = &set_multicast_list; ether_setup(dev);ether_setupether_setup是一個通用于以太網(wǎng)接口的網(wǎng)

45、絡(luò)接口設(shè)置函數(shù)。由于以太網(wǎng)卡有很好的共性,device結(jié)構(gòu)中許多有關(guān)的網(wǎng)絡(luò)接口信息都是通過調(diào)用ether_setup函數(shù)統(tǒng)一來設(shè)置。那么讓我們看看它到底會缺省設(shè)哪些域段及設(shè)為什么值。若你滿意這些缺省設(shè)置,那么在寫驅(qū)動程序時只要調(diào)用一下這個函數(shù)就可以將這些域段的設(shè)置工作“置之不理了”,否則,也可在調(diào)用該函數(shù)之后再改過。/* from drivers/net/ */void ether_setup(struct device *dev) int i; /* 初始化緩沖隊列鏈表,這是一個雙向鏈表 */ for (i = 0; i buffsi); /* 一些處理函數(shù)的初始化,驅(qū)動程序可以不寫這些函數(shù)

46、了 */ dev-change_mtu= eth_change_mtu; dev-hard_header= eth_header; dev-rebuild_header = eth_rebuild_header; dev-set_mac_address = eth_mac_addr; dev-header_cache_bind = eth_header_cache_bind; dev-header_cache_update= eth_header_cache_update; dev-type= ARPHRD_ETHER; /* Ethernet 10Mbps */ dev-hard_heade

47、r_len= ETH_HLEN;/* MAC層協(xié)議頭的大小 14 */ dev-mtu= 1500; /* 最大傳輸單位 */ dev-addr_len= ETH_ALEN;/* 協(xié)議地址長度 4 */ dev-tx_queue_len= 100;/* 傳輸隊列的長度 */ memset(dev-broadcast,0 xFF, ETH_ALEN);/* 物理地址長度 6 */ /* 廣播地址有效及支持多點傳輸 */ dev-flags= IFF_BROADCAST|IFF_MULTICAST; dev-family= AF_INET; /* 英特網(wǎng)IP協(xié)議簇 */ dev-pa_addr=

48、0;/* 以后用ifconfig命令設(shè)置 */ dev-pa_brdaddr= 0;/* 以后用ifconfig命令設(shè)置 */ dev-pa_mask= 0;/* 以后用ifconfig命令設(shè)置 */ dev-pa_alen= 4;/* 協(xié)議地址長度 4 */至此模塊化網(wǎng)絡(luò)設(shè)備的初始化就完成了?!皢映跏蓟J健钡姆治龀跏蓟呗浴皢映跏蓟J健迸c“模塊初始化模式”不同,前者要對所有內(nèi)核支持的網(wǎng)絡(luò)設(shè)備進(jìn)行檢測和初始化,而后者僅需檢測和初始化被裝載模塊的網(wǎng)絡(luò)設(shè)備。為了實現(xiàn)在啟動時對所有可能存在的設(shè)備進(jìn)行初始化,系統(tǒng)在啟動之前將所有內(nèi)核支持的網(wǎng)絡(luò)設(shè)備的名字及相應(yīng)的初始化函數(shù)都掛在網(wǎng)絡(luò)設(shè)備管理表(d

49、ev_base)上。啟動后,net_dev_int()將依次對網(wǎng)絡(luò)設(shè)備管理表dev_base中的每個設(shè)備,調(diào)用該設(shè)備本身的init_function進(jìn)行初始化。若init_function失敗,即該設(shè)備不存在或I/O、IRQ不能獲得,則將該設(shè)備從dev_base去掉。這樣,最后網(wǎng)絡(luò)設(shè)備管理表中剩下的網(wǎng)絡(luò)接口都是存在的,顯然也已是被初始化過的。我們看一下dev_base的初始化情況。圖三 網(wǎng)絡(luò)設(shè)備表的初始化后的示意圖/* 網(wǎng)絡(luò)設(shè)備管理表的初始化 */* from drivers/net/ */static struct device eth7_dev = eth7, 0,0,0,0,0 xffe

50、0 /* I/O base*/, 0,0,0,0, NEXT_DEV, ethif_probe ;static struct device eth6_dev = eth6, 0,0,0,0,0 xffe0 /* I/O base*/, 0,0,0,0, ð7_dev, ethif_probe ;static struct device eth0_dev = eth0, 0, 0, 0, 0, ETH0_ADDR, ETH0_IRQ, 0, 0, 0, ð1_dev, ethif_probe ;/* 在八個eth接口中,只有eth0將I/O設(shè)為0,讓其進(jìn)行自動檢測,其他* eth接口

51、的I/O都設(shè)為0 xffe0,不進(jìn)行檢測。Linux缺省的內(nèi)核在啟動時* 只能自動檢測到一塊eth網(wǎng)卡,就是這個原因 */# undef NEXT_DEV# define NEXT_DEV(ð0_dev)#if defined(PLIP) | defined(CONFIG_PLIP)extern int plip_init(struct device *);static struct device plip2_dev = plip2, 0, 0, 0, 0, 0 x278, 2, 0, 0, 0, NEXT_DEV, plip_init, ;static struct device pl

52、ip0_dev = plip0, 0, 0, 0, 0, 0 x3BC, 5, 0, 0, 0, &plip1_dev, plip_init, ;extern int loopback_init(struct device *dev);struct device loopback_dev = lo, 0 x0, 0 x0, 0 x0, 0 x0, 0, 0, 0, 0, 0, NEXT_DEV, loopback_init ;struct device dev_base = &loopback_dev;/* 關(guān)鍵的一個語句 */函數(shù)調(diào)用關(guān)系系統(tǒng)轉(zhuǎn)入核心后,start_kernel將會創(chuàng)建一個i

53、nit進(jìn)程,該init進(jìn)程則會通過系統(tǒng)調(diào)用sys_steup進(jìn)行所有尚未初始化的設(shè)備(有一些設(shè)備如內(nèi)存、PCI等系統(tǒng)已先于此進(jìn)行了初始化)。device_setup不僅要初始化內(nèi)核支持的字符設(shè)備、塊設(shè)備,也調(diào)用net_dev_init初始化所有內(nèi)核支持的且實際存在的網(wǎng)絡(luò)設(shè)備。net_dev_init會對每個內(nèi)核支持的網(wǎng)絡(luò)設(shè)備調(diào)用該設(shè)備的init_functions進(jìn)行具體的物理設(shè)備的初始化工作。整個函數(shù)調(diào)用關(guān)系圖如下:圖四 “啟動初始化模式”的函數(shù)調(diào)用關(guān)系圖具體流程LINUX啟動時,完成了實模式下的系統(tǒng)初始化(arch/i386/boot/)與保護(hù)模式下的核心初始化包括初始化寄存器和數(shù)據(jù)區(qū)(a

54、rch/i386/boot/compressed/、核心代碼解壓縮、頁表初始化(arch/i386/kernel/)、初始化idt、gdt和ldt等工作后,系統(tǒng)轉(zhuǎn)入了核心。調(diào)用函數(shù)start_kernel啟動核心(init/)后,將繼續(xù)各方面的初始化工作,其中與網(wǎng)絡(luò)子系統(tǒng)有關(guān)的部分為:調(diào)用sock_init()(net/)初始化網(wǎng)絡(luò)模塊。sock_init()函數(shù)主要做以下動作:初始狀態(tài)設(shè)為不支持任何網(wǎng)絡(luò)協(xié)議:static struct proto_ops *popsNPROTO; n, dev-name);ei_close(dev);/* 下面將分析 */MOD_DEC_USE_COUNT;

55、/* 對應(yīng)于第7項內(nèi)容 */return 0;由上容易看到,ne_close幾乎就是ne_open在鏡中的像。/* from drivers/net/ */int ei_open(struct device *dev)struct ei_device *ei_local = (struct ei_device *) dev-priv;if (ei_local = NULL)/* 只有沒調(diào)用ethdev_init(),才會出現(xiàn)以下的錯誤 */printk(KERN_EMERG %s: ei_open passed a non-existent device!n, dev-name);return

56、 -ENXIO;irq2dev_mapdev-irq = dev;/* 對應(yīng)于上面所列內(nèi)容的第3項 */NS8390_init(dev, 1);/* 下面將分析 */dev-start = 1;/* 對應(yīng)于第6項,表示接口UP */ei_local-irqlock = 0;/* 對應(yīng)于第5項 */return 0;int ei_close(struct device *dev)NS8390_init(dev, 0);dev-start = 0;/* 對應(yīng)于第6項內(nèi)容,表示接口DOWN */return 0;void NS8390_init(struct device *dev, int sta

57、rtp)/* 設(shè)置8390的各種寄存器的狀態(tài) */dev-tbusy = 0;dev-interrupt = 0;/* 對應(yīng)于第6項內(nèi)容 */ei_local-tx1 = ei_local-tx2 = 0;ei_local-txing = 0;/* 對應(yīng)于第5項內(nèi)容 */另外,文件net/core/還提供一系列界面獨立于具體網(wǎng)絡(luò)設(shè)備的操作函數(shù),如dev_ifsioc(void *arg, unsigned int getset)它可以處理許多ioctl SIGNAL:讀取和修改接口網(wǎng)絡(luò)地址(對TCP/IP就是IP地址)、讀取和修改接口的dev-flags、讀取和設(shè)置MTU、讀取和設(shè)置廣播地址等

58、等。ifconfig的功能大部分是通過該文件提供的函數(shù)實現(xiàn)的。數(shù)據(jù)包的傳輸和接收當(dāng)物理網(wǎng)絡(luò)設(shè)備接收到數(shù)據(jù)時,系統(tǒng)是如何知道并讀取數(shù)據(jù)的呢當(dāng)前可通過兩種途徑解決這個問題。一種方法是輪詢方式,系統(tǒng)每隔一定的時間間隔就去檢查一次物理設(shè)備,若設(shè)備“報告”說有數(shù)據(jù)到達(dá),就調(diào)用讀取數(shù)據(jù)的程序。在Linux中,輪詢方式可通過定時器實現(xiàn),但該方法存在一個明顯的缺點:不管設(shè)備是否有數(shù)據(jù),系統(tǒng)總是要固定地花CPU時間去查看設(shè)備,且可能延遲對一些緊急數(shù)據(jù)的處理,因為網(wǎng)絡(luò)設(shè)備有數(shù)據(jù)時可能不能馬上得到CPU的響應(yīng)。在這種方式下,設(shè)備完全處于一種被動的狀態(tài),而CPU又負(fù)擔(dān)過重。無論從資源的利用率上還是從效率上看,這種方法

59、都不是最優(yōu)的。另一種方法是中斷方式,中斷方式利用硬件體系結(jié)構(gòu)的中斷機(jī)制實現(xiàn)設(shè)備和系統(tǒng)的應(yīng)答對話,即當(dāng)物理設(shè)備需要CPU處理數(shù)據(jù)時,設(shè)備就發(fā)一個中斷信號給系統(tǒng),系統(tǒng)則在收到信號后調(diào)用相應(yīng)的中斷服務(wù)程序響應(yīng)對設(shè)備中斷的處理。中斷方式有效地解決了設(shè)備與CPU的對話交流問題,并將CPU從繁重的設(shè)備輪詢中解脫出來,大大提高了CPU的利用率。當(dāng)前不管是Linux平臺還是Windows平臺,它們的網(wǎng)絡(luò)設(shè)備驅(qū)動程序幾乎都是使用中斷方式的。故在此我們主要討論基于中斷方式的網(wǎng)絡(luò)設(shè)備驅(qū)動程序。網(wǎng)絡(luò)分層引起的一個問題是,每層的協(xié)議在發(fā)送數(shù)據(jù)包時要加協(xié)議頭和協(xié)議尾到原數(shù)據(jù)中,在收到數(shù)據(jù)包時則要將本層的協(xié)議頭和協(xié)議尾從數(shù)

60、據(jù)包中去掉。這使得在不同層協(xié)議間傳輸時,每層都需要知道自己這一層的協(xié)議頭和協(xié)議尾在數(shù)據(jù)包的哪里。一種解決方法是在每層都復(fù)制緩沖區(qū),但顯然效率太低。Linux的做法是用一種數(shù)據(jù)結(jié)構(gòu)sk_buff在不同協(xié)議層及網(wǎng)絡(luò)設(shè)備驅(qū)動程序之間傳送數(shù)據(jù)。sk_buff 包括指針和長度域段,允許每個協(xié)議層通過標(biāo)準(zhǔn)的函數(shù)操作傳送的數(shù)據(jù)包。該數(shù)據(jù)結(jié)構(gòu)在整個Linux的網(wǎng)絡(luò)子系統(tǒng)包括網(wǎng)絡(luò)設(shè)備中扮演了一個十分重要的角色,故我們在分析數(shù)據(jù)包的傳輸和接收之前,首先來看看sk_buff這個數(shù)據(jù)結(jié)構(gòu)的內(nèi)容及系統(tǒng)提供的相關(guān)操作。因為對該數(shù)據(jù)結(jié)構(gòu)的了解將大大有助于對Linux整個網(wǎng)絡(luò)子系統(tǒng)的理解。Socket緩沖區(qū)及相關(guān)操作與塊設(shè)備

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論