?? (ldd) ch14-網絡驅動程序(下)(轉載).htm
字號:
<P>raw,從包數據中刪去硬件包頭,并設置skb->pkt_type。最后一項在skb分配時缺省為PA<BR>CKET_HOST(表明包被指向這個主機),當然它也可以改為符合以太網目的地址得其它值<BR>。<BR> <BR>如果你的接口是點到點連接,你將無法收到未想到的選播包。為避免這個,你必須記住<BR>那些第一個八元組的最低位(LSB)是0的目的地址將被指向單個主機(也就是說,它是P<BR>ACKET_HOST或PACKET_OTHERHOST)。plip驅動程序用0xfc作為它的硬件地址的第一個八<BR>元組,而snull用0x00。這兩個地址都導致一個可工作的以太網類的點到點連接。<BR> <BR>非以太網包頭<BR> <BR>這節簡要地介紹硬件包頭是如何用來封裝相關信息的。如果你希望了解細節,你可以從<BR>核心源碼或特別傳輸介質的技術文檔中得到。我們剛才已經看到硬件包頭除了含有目的<BR>地址外,還有一些信息,其中最重要的是通信協議。<BR> <BR>不過,并不是每個協議都要提供所有的信息。象plip或snull之類的點到點連接可以避免<BR>傳送整個以太網包頭,同時不失去一般性。hard_header設備方法從核心接收傳送信息--<BR>--包括協議級和硬件地址。它也收到16位的協議號。例如IP由ETH_P_IP標志。驅動程序<BR>應能正確地象接收主機傳送包數據和協議號。點到點連接可以在硬件包頭中省略地址,<BR>只傳送協議號,因為傳送是有保證的,與源和目的地址無關。一個只有IP的連接甚至什<BR>么硬件頭都不傳送。兩種情況下,所有的工作都由hard_header完成,rebuild_header除<BR>了返回0外什么都不做。<BR> <BR></P></FONT><FONT
color=#ffffff size=3>
<P><BR>當包在連接的另一端被撿起時,接收函數將正確地設置skb->protocol,skb->pkt_type<BR>,和skb->mac.raw。<BR> <BR>skb->mac.raw是一個被網絡高層代碼實現的地址解析機制使用的字符指針(例如,net/i<BR>pv4/arp.c)。它必須指向一個與dev->type匹配的機器地址。設備類型的可能值被定義<BR>在<linux/if_arp.h>;以太網接口用ARPHRD_ETHER。例如,下面是eth_type_trans處理<BR>收到包的以太網包頭的方法:<BR> <BR> skb->mac.raw=skb->data;<BR> <BR> skb_pull(skb, dev->hard_header_len);<BR> <BR>在最簡單的情況下(無包頭的點到點連接),skb->mac.raw可以指向一個含有這個接口<BR>的硬件地址的靜態緩沖區,protocol可以被置為ETH_P_IP,packet_type仍維持其缺省值<BR>PACKET_HOST。<BR> <BR> <BR> <BR>加載時配置<BR> <BR> <BR> <BR></P></FONT><FONT
color=#ffffff size=3>
<P><BR>用戶可以用幾個標準的關鍵字來配置接口。任何新的網絡模塊都應遵循這個標準:<BR> <BR>io=<BR> <BR>為接口設置I/O端口的基地址。如果系統中安裝了不只一個接口,那么可以用一個由逗號<BR>分隔的列表來指定。<BR> <BR>irq=<BR> <BR>設置中斷號。和上面一樣,可以指定不止一個值。<BR> <BR> <BR> <BR>換句話說,一個裝了兩個own_eth接口的Linux用戶可能用下面的命令行來加載模塊:<BR> <BR> insmod own_eth.o io=0x300, 0x320 irq=5,7<BR> <BR>如果指定0值,那么io=和irq=選項都要被探測。因此用戶可以通過指定io=0來強制探測<BR>。如果用戶名優指定任何選項,多數驅動程序通常都探測一個接口,但有時,模塊可能<BR>被禁止探測。(見ne.c中關于NE2000設備的探測)。<BR> <BR>設備驅動程序應該象剛才描述的這樣工作。ISA設備的典型實現如下所示,假設驅動程序<BR></P></FONT><FONT
color=#ffffff size=3>
<P>設備驅動程序應該象剛才描述的這樣工作。ISA設備的典型實現如下所示,假設驅動程序<BR>最多可以支持四個接口:<BR> <BR>(代碼331)<BR> <BR>這段代碼缺省探測一個板子,并總是自動探測中斷,但用戶可以改變這種行為。例如,i<BR>o=0,0,0將探測三塊板子。<BR> <BR>除了使用io和irq外,驅動程序的作者可以隨意增加其它加載時配置參數。也沒有已建立<BR>的命名標準。<BR> <BR> <BR> <BR>運行時配置<BR> <BR> <BR> <BR>用戶有時可能希望在運行時改變接口的配置。例如,當中斷號無法探測時,想對它正確<BR>配置的唯一辦法就是“嘗試—錯誤”技術。一個用戶空間的程序可以獲取設備的當前配<BR>置,并通過在一個打開的套接字上調用ioctl來設置一個新的配置。例如,應用ifconfig<BR>使用ioctl為接口設置I/O端口。<BR> <BR>我們前面知道一個為網絡接口定義的方法中的一個是set_config。這個方法被用來在運<BR></P></FONT><FONT
color=#ffffff size=3>
<P>我們前面知道一個為網絡接口定義的方法中的一個是set_config。這個方法被用來在運<BR>行時設置或改變一些接口特征。<BR> <BR>當一個程序詢問當前配置時,核心從結構device中抽取相關信息,而不通知驅動程序;<BR>另一方面,當一個新的配置被傳遞給接口時,set_config被調用,這樣驅動程序就可以<BR>檢查這些值并采取相應的動作。這個驅動程序方法對應下面的原形:<BR> <BR> int (*set_config)(struct device *dev, struct ifmap *map);<BR> <BR>map參數指向一個由用戶程序傳遞的結構的拷貝;這個拷貝已經在核心空間,所以驅動程<BR>序不需要調用memcpy_from _fs。<BR> <BR>結構ifmap的域是:<BR> <BR>unsigned long mem_start;<BR> <BR>unsigned long mem_end;<BR> <BR>unsigned short base_addr;<BR> <BR>unsigned char irq;<BR> <BR>unsigned char dma;<BR></P></FONT><FONT
color=#ffffff size=3>
<P>unsigned char dma;<BR> <BR>這些域對應著結構device中的域。<BR> <BR>Unsigned char prot;<BR> <BR>這個域對應著dev中的if_port。map->port的含義是設備特定的。<BR> <BR> <BR> <BR>當一個進程為設備發出ioctl(SIOCSIFMAP)(即Socket I/O Control Set InterFace<BR>MAP)時,set_config設備方法被調用。這個進程在時強制使用新值之前,應該發出ioctl<BR>(SIOCGIFMAP)(即Socket I/O Control Get InterFace MAP),這樣驅動程序只需要查看d<BR>ev和ifmap結構不匹配的地方。Map中任何不被驅動程序使用的域均可以略過。例如,一<BR>個不使用DMA的網絡設備可以忽略map->dma。<BR> <BR>snull實現被設計成可以顯示驅動程序是如何針對配置改變而動作的。對snull來說,沒<BR>有一個域由物理意義。但出于說明的目的,代碼禁止改變I/O地址,允許改變IRQ號,并<BR>忽略其它選項,從而顯示這些改變是如何被響應、拒絕、或是忽略的。<BR> <BR>(代碼333 #1)<BR> <BR>這個方法的返回值被作為發出的ioctl系統調用的返回值,對于沒有實現set_config的驅<BR></P></FONT><FONT
color=#ffffff size=3>
<P>這個方法的返回值被作為發出的ioctl系統調用的返回值,對于沒有實現set_config的驅<BR>動程序則返回-EOPNOTSUPP。<BR> <BR>如果你對接口配置如何從用戶空間訪問感到好奇,請看misc-progs/netifconfig.c,它<BR>可以用來與set_config比較。下面是一個示例運行的輸出:<BR> <BR>(代碼333 #2)<BR> <BR> <BR> <BR>自定義ioctl命令<BR> <BR> <BR> <BR>我們已經看到ioctl系統調用是為套接字實現的。SIOCSIFADDR和SIOCSIFMAP是“套接字i<BR>octl”的例子現在讓我們看看這個系統調用的第三個參數是如何被網絡代碼使用的。<BR> <BR>當ioctl系統調用在套接字上被調用時,其命令號是在<linux/sockios.h>定義的符號之<BR>一,并且函數sock_ioctl直接調用一個協議特定的函數(這里協議指使用的主要網絡協<BR>議,如IP或AppleTalk)。<BR> <BR>任何協議層不認識的ioctl命令被傳遞給設備層。這些設備相關的ioctl命令從用戶空間<BR>接收第三個參數,即結構ifreq*;這個結構在<linux/if.h>中定義。SIOCSIFADDR和SIOC<BR></P></FONT><FONT
color=#ffffff size=3>
<P>接收第三個參數,即結構ifreq*;這個結構在<linux/if.h>中定義。SIOCSIFADDR和SIOC<BR>SIFMAP命令實際上是工作在ifreq結構上。SIOCSIFMAP的額外參數,盡管定義為ifmap,<BR>是ifreq的一個域。<BR> <BR>除了使用標準的調用,每個接口可以定義它自己的ioctl命令。例如plip接口允許通過io<BR>ctl修改其內部超時值。套接字的ioctl實現將16個命令看作對接口是私有的:從SIOCDEV<BR>PRIVATE到SIOCDEVPRIVATE+15。<BR> <BR>當這些命令中的一個被認識時,dev->do_ioctl在相關的接口驅動程序里被調用。這個函<BR>數接收與通用目的的ioctl函數使用的一樣的ifreq*指針。<BR> <BR> Int (*do_ioctl)(struct device *dev, struct ifreq *ifr, int cmd);<BR> <BR>Ifr指針指向核心空間的一個地址,放有被用戶傳來結構的一個拷貝。在do_ioctl返回后<BR>,這個結構又被拷貝回用戶空間;這樣,驅動程序可以使用私有命令來接收和返回數據<BR>。<BR> <BR>設備特定的命令可以選擇使用結構ifreq中的域,但它們已經帶有標準的含義,驅動程序<BR>不太可能根據自己的需要適配這個結構。域ifr_data是個caddr_t項(一個指針),用于<BR>設備特定的需要。驅動程序和調用ioctl命令的程序應在ifr_data的使用上取得一致。例<BR>如,pppstats使用設備特定的命令來從ppp接口驅動程序中獲取信息。<BR> <BR>在這里不值得給出do_ioctl的一個實現,但根據本章的信息和核心的例子,你應能在需<BR></P></FONT><FONT
color=#ffffff size=3>
<P>在這里不值得給出do_ioctl的一個實現,但根據本章的信息和核心的例子,你應能在需<BR>要的時候寫出一個。不過注意,plip實現不正確地使用了ifr_data,不應做為ioctl實現<BR>的一個例子。<BR> <BR> <BR> <BR>統計信息<BR> <BR> <BR> <BR>一個驅動程序需要的最后一個方法是get_stats。這個方法返回指向設備統計信息的一個<BR>指針。它的實現相當容易:<BR> <BR>(代碼335)<BR> <BR>返回有意義的統計信息所需的實際工作散布在驅動程序中,不同的域分別被更新。下表<BR>給出enet_statistics結構中最有趣的幾個域。<BR> <BR>int rx_packets;<BR> <BR>int tx_packets;<BR> <BR>這兩個域含有接口成功傳送的進來和外出包的總數。<BR></P></FONT><FONT
color=#ffffff size=3>
<P>這兩個域含有接口成功傳送的進來和外出包的總數。<BR> <BR>int rx_errors;<BR> <BR>int tx_errors;<BR> <BR>出錯的接收和發送的數目。接收錯可能是錯誤的校驗和、錯誤的包大小,以及其它問題<BR>的結果。發送錯誤不太常見,一般都是線纜的問題。<BR> <BR>int rx_dropped;<BR> <BR>int tx_dropped;<BR> <BR>在接收和發送時被丟棄的包的個數。當包數據沒有可用內存時,包便被丟棄了。tx_drop<BR>ped很少使用。<BR> <BR> <BR> <BR>這個結構還有幾個域,可以用來細分發送和接收時發生的錯誤。感興趣的讀者可以看<li<BR>nux/if_ether.h>中結構的定義。<BR> <BR> <BR> <BR></P></FONT><FONT
color=#ffffff size=3>
<P><BR>選播<BR> <BR> <BR> <BR>“選播”包是指一個網絡包,它將要被多于一個,但又不是全部的主機接收。<BR> <BR>這個功能是通過給一組主機賦以特殊的硬件地址來獲得的。指向這些特殊地址中的一個<BR>的包將被這個組中所有的主機收到。在以太網的情況下,一個選播地址是將目的地址中<BR>第一個八元組的最低位設置而得到,而所有的設備板子都在其硬件地址中將這一位清除<BR>。<BR> <BR>處理主機組以及硬件地址的棘手部分都有應用或核心完成了,接口驅動程序并不需要處<BR>理這些問題。<BR> <BR>選播包的發送很簡單,與其它包完全一樣。接口在傳輸介質上發送它們,根本不管目的<BR>地址。核心必須設置一個正確的硬件目的地址;rebuild_header設備方法(如果被定義<BR>)并不需要查看它整理的數據。<BR> <BR>而另一方面,接收選播包需要設備的一些合作。當一個“有趣的”選播包被收到時(也<BR>就是一個包的目的地址確定一組主機,其中包含了這個接口),硬件應該通知操作系統<BR>。這意味著硬件過濾器應被設計為能夠區別不同的選播地址。這個過濾器在接口的一般<BR>操作中,將網絡包的地址與其自己的硬件地址進行匹配。<BR></P></FONT><FONT
color=#ffffff size=3>
<P>操作中,將網絡包的地址與其自己的硬件地址進行匹配。<BR> <BR>典型地,在考慮選播的情況下,硬件可分為一下三類:<BR> <BR>l 不能處理選播的接口。這類接口要么接收指向自己硬件地址的包(包括播送<BR>包),要么節收所有的包。它們接收選播包是通過接收所有的包實現的,這樣,操作系<BR>統中會充斥著大量“無意義”的包。一般我們不認為這類接口為支持選播的,驅動程序<BR>不在dev->flags中置IFF_MULTICAST。<BR>點到點接口是一種特殊情況,它們通常接收所有的包,根本不進行任何硬件過濾。<BR> <BR>l 能區別選播包和其它包(主機到主機或播送)的接口。這類接口可以被要求<BR>接收所有的選播包,然后用軟件來判斷自己是否是有效的接收者。這種情形引入的開銷<BR>是可以接收的,因為一個典型的網絡中選播包的數量都很少。<BR> <BR>l 能夠進行選播地址硬件檢測的接口。可以給這類接口一組需要接收的選播地<BR>址,它們會忽略其它的選播包。這對核心來說是最優化的情況,因為不會浪費處理器事<BR>件去丟棄接口收到的“無意義”的包。<BR> <BR> <BR> <BR>核心試圖利用高級接口的能力,能最好地支持第三類接口(用途最廣)。因此,當有效<BR>的選播地址被改變時核心應通知驅動程序,它把新的一組地址傳給驅動程序,這樣它可<BR>以按照新的信息更新硬件過濾器。<BR></P></FONT><FONT
color=#ffffff size=3>
<P>以按照新的信息更新硬件過濾器。<BR> <BR> <BR> <BR>核心對選播的支持<BR> <BR>下面是與驅動程序的選播能力相關的數據結構和函數的概括:<BR> <BR>void (*dev->set_multicast_list)(struct device *dev)<BR> <BR>當與設備相關的機器地址表改變時調用這個設備方法。當dev->flags被修改時它也被調<BR>用,因為有些標志也要求你重新配置硬件過濾器。這個方法接收一個指向device結構的<BR>指針作為參數,返回void。對實現這個方法不感興趣的驅動程序可以留域為NULL。<BR> <BR>struct dev_mc_list *dev->mc_list<BR> <BR>這是域設備相關的所有選播地址的鏈表。這個結構的實際定義在本節結束時介紹。<BR> <BR>int dev->mc_count<BR> <BR>鏈表項數。這個信息有點冗余,但檢查mc_count是否為0是優于列表檢查的一個有用的快<BR>捷方式。<BR> <BR></P></FONT><FONT
color=#ffffff size=3>
<P><BR>IFF_MULTICAST<BR> <BR>除非驅動程序在dev->flags中設置了這個標志,接口將不必處理選播包。當dev->flags<BR>改變時,至少set_multicast_list會被調用。<BR> <BR>IFF_ALLMULTI<BR> <BR>dev->flags中的這個標志被網絡軟件設置以告訴驅動程序從網絡中抽取所有播送包。這<BR>在multicast-routing被使用時發生。如果這個標志被置位,dev->mc_list將不再使用去<BR>過濾選播包。<BR> <BR>IFF_PROMISC<BR> <BR>當接口被置為雜亂模式時,dev->flags中的這個標志被設置。所有的包都被接口抽取,<BR>不考慮dev->mc_list。<BR> <BR> <BR> <BR>驅動程序開發者需要的最后一點信息是結構dev_mc_list的定義,它居于<linux/netdevi<BR>ce.h>中。<BR> <BR>(代碼337)<BR></P></FONT><FONT
color=#ffffff size=3>
<P>(代碼337)<BR> <BR>由于選播和硬件地址與包的實際發送無關,這個結構可在不同的網絡實現上移植,每個<BR>地址由一串八元組和一個長度確定,就象dev->dev_addr。<BR> <BR> <BR> <BR>一個典型的實現<BR> <BR>描述set_multicast_list設計的最號辦法是給出一些偽代碼。<BR> <BR>下面的函數是這個函數在一個全特征(ff)驅動程序中的一個典型實現。說這個驅動程序<BR>是全特征的是因為它控制的接口有一個復雜的硬件包過濾器,它可以存放一個由本機接<BR>收的選播地址表。這個表的最大尺寸是FF_TABLE_SIZE。<BR> <BR>所由帶有前綴ff_的函數是放置硬件特定操作的地方。<BR> <BR>(代碼338)<BR> <BR>如果這個接口不能在硬件過濾器中存儲到來包的選播表,那么這個實現還可以簡化。在<BR>這種情況下,FF_TABLE_SIZE減為0,代碼的最后四行也不需要了。<BR> <BR>現在,接口板一般不能存儲選播表。不過,這并不是一個大問題,因為網絡代碼的高層<BR></P></FONT><FONT
color=#ffffff size=3>
<P>現在,接口板一般不能存儲選播表。不過,這并不是一個大問題,因為網絡代碼的高層<BR>會負責將不需要的包丟棄。<BR> <BR>如我前面建議的,即使不能處理選播包的接口也需要實現set_multicast_list方法,這<BR>樣當dev->flags發生改變時可以被通知。我稱這個為“無特征”(nf)的實現。它非常簡<BR>單,如下面所示:<BR> <BR>(代碼339)<BR> <BR>處理IFF_PROMISC是很重要的,因為不然的話,用戶將無法運行tcpdump或其它一些網絡<BR>分析工具。另一方面,如果接口運行一個點到點的連接,則沒有任何實現set_multicast<BR>_list的必要,因為它們接收所有的包。<BR> <BR> <BR> <BR>快速參考<BR> <BR> <BR> <BR>本節提供在本章中介紹的概念的參考。它也解釋了驅動程序應包含的每個頭文件的作用<BR>。不過,device和sk_buff的每個域的列表,就不再重復了。<BR> <BR>#include <linux/netdevice.h><BR></P></FONT><FONT
color=#ffffff size=3>
<P>#include <linux/netdevice.h><BR> <BR>這個頭文件含有struct device的定義,還包含了網絡驅動程序需要的幾個其它頭文件。<BR> <BR> <BR>void netif_rx(struct sk_buff *skb);<BR> <BR>這個函數在中斷時可以被調用通知核心一個包被收到了,并且封裝在一個套接字緩沖區<BR>中。<BR> <BR>#include <linux/if.h><BR> <BR>被netdevice.h包含,這個文件聲明接口標志(IFF_macros)和結構ifmap,它在網絡驅動<BR>程序的ioctl實現中用重要的作用。<BR> <BR>#include <linux/if_ether.h><BR> <BR>ETH_ALEN<BR> <BR>ETH_P_IP<BR> <BR>struct ethhdr;<BR> <BR></P></FONT><FONT
color=#ffffff size=3>
<P><BR>struct enet_statistics;<BR> <BR>被netdevice.h包含,if_ether.h定義所有的ETH_macros,用來表示八元組長度(象地址<BR>長度)和網絡協議(象IP)。它也定義了結構ethhdr和enet_statistics。注意,不要看<BR>enet_statistics的名字和包含它的頭文件,事實上,所有的接口都要用到它,不僅僅是<BR>以太網。<BR> <BR>#include <linux/skbuff.h><BR> <BR>結構sk_buff和一些相關結構的定義,以及在緩沖區上操作的線入函數。這個頭文件包含<BR>在netdevice.h中。<BR> <BR>#include <linux/etherdevice.h><BR> <BR>void ether_setup(struct device *dev);<BR> <BR>這個函數為以太網驅動程序設置大多數設備方法為通用目的的實現。它同樣設置dev->fl<BR>ags,并且在名字中的第一個字符是空格或空字符時,將下一個可用的ethx名賦給dev->n<BR>ame。<BR> <BR>unsigned short eth_type_trans(struct sk_buff *skb, struct device *dev)<BR> <BR></P></FONT><FONT
color=#ffffff size=3>
<P><BR>unsigned short eth_type_trans(struct sk_buff *skb, struct device *dev)<BR> <BR>當以太網接口收到一個包,這個函數將被調用來設置skb->pkt_type。返回值是一個協議<BR>號,通常被存在skb->protocol中。<BR> <BR>#include <linux/sockios.h><BR>SIOCDEVPRIVATE<BR>這是16個ioctl命令的第一個,可以被每個驅動程序實現以供私用。所有的網絡ioctl命<BR>令都在sockios.h中定義。<BR> <BR>--<BR><FONT
color=#00ff00>※ 來源:.華南網木棉站 bbs.gznet.edu.cn.[FROM: 202.38.196.234]</FONT><BR>--<BR><FONT
color=#00ffff>※ 轉寄:.華南網木棉站 bbs.gznet.edu.cn.[FROM: 211.80.41.106]</FONT><BR>--<BR><FONT
color=#0000ff>※ 轉寄:.華南網木棉站 bbs.gznet.edu.cn.[FROM: 211.80.41.106]</FONT><BR>--<BR><FONT
color=#ffff00>※ 轉載:.南京大學小百合站 bbs.nju.edu.cn.[FROM: 211.80.41.106]</FONT><BR>--<BR><FONT
color=#ff0000>※ 轉載:·飲水思源 bbs.sjtu.edu.cn·[FROM: 211.80.41.106]</FONT><BR></P></FONT>
<P align=center><A
href="http://211.71.69.201/joyfire/lsdp/index.htm"><FONT color=#ffffff
size=2>目錄頁</FONT></A> | <A
href="http://211.71.69.201/joyfire/lsdp/17.htm"><FONT color=#ffffff
size=2>上一頁</FONT></A> | <A
href="http://211.71.69.201/joyfire/lsdp/19.htm"><FONT color=#ffffff
size=2>下一頁</FONT></A></P></SPAN></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width="90%" align=center border=0>
<TBODY>
<TR>
<TD colSpan=3 height=2>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -