?? (ldd) ch02-編寫和運(yùn)行模塊(轉(zhuǎn)載).htm
字號(hào):
color=#ffffff size=3>
<P><BR>正如函數(shù)register_symtab的名字所暗示,它用來在內(nèi)核主符號(hào)表中注冊(cè)符號(hào)表。這種方<BR>法要比通過靜態(tài)和全局變量的方法清晰的多,這樣程序員就可以把關(guān)于哪些開放給其他<BR>模塊,哪些不開放的信息集中存放。這種方法比在源文件中到處堆放static聲明要好的<BR>多。<BR> <BR>如果模塊在初始化過程中調(diào)用了register_symtab,全局變量就不再是開放的了;只有那<BR>些顯式羅列在符號(hào)表中的符號(hào)才開放給內(nèi)核。<BR> <BR>填寫一個(gè)符號(hào)表是項(xiàng)挺復(fù)雜的工作,但內(nèi)核開發(fā)人員已經(jīng)寫好了頭文件簡(jiǎn)化這項(xiàng)工作。<BR>下面若干行代碼演示了如何聲明和開放一個(gè)符號(hào)表:<BR> <BR>(代碼)<BR> <BR>有興趣的讀者可以看看<linux/symtab_begin.h>,但它可是內(nèi)核中最難懂的頭文件之一<BR>。事實(shí)上,僅想好好使用宏X的話,根本沒必要讀董它。<BR> <BR>由于register_symtab是在模塊加載到內(nèi)核后被調(diào)用的,它可以覆蓋模塊靜態(tài)或全局聲明<BR>的符號(hào)。此時(shí),register_symtab用顯式符號(hào)表替代模塊默認(rèn)開放的公共符號(hào)。<BR> <BR>這種覆蓋是可能的,因?yàn)閕nsmod命令處理傳遞給系統(tǒng)調(diào)用sys_init_module的全局符號(hào)表<BR>,然后在調(diào)用init_module之前注冊(cè)這個(gè)符號(hào)表。因此這之后的任何一次顯式調(diào)用regist<BR>er_symtab都會(huì)替換相應(yīng)模塊的符號(hào)表。<BR></P></FONT><FONT
color=#ffffff size=3>
<P>er_symtab都會(huì)替換相應(yīng)模塊的符號(hào)表。<BR> <BR>如果你的模塊不需要開放任何符號(hào),而且你也不想把所有的東西都聲明成static的,在i<BR>nit_module里加上下面一行語句就可以了。這次對(duì)register_symtab的調(diào)用通過注冊(cè)一個(gè)<BR>空表覆蓋了模塊默認(rèn)的符號(hào)表:<BR> <BR>(代碼)<BR> <BR>如果源文件不想給堆疊在其上的模塊提供什么接口,用上面那行語句隱藏所有的符號(hào)總<BR>是不錯(cuò)的。<BR> <BR>當(dāng)模塊從內(nèi) 誦對(duì)厥 ,它所聲明的所有公共符號(hào)也就自動(dòng)?主符號(hào)表中注銷了。不過是<BR>全局符號(hào)還是顯式符號(hào)表,這一點(diǎn)都適用。<BR> <BR>初始化和終止<BR>正如前面已述,init_module向內(nèi)核注冊(cè)模塊所能提供的所有設(shè)施。這里我使用了“設(shè)施<BR>”,我的意思是指新功能,是一整個(gè)設(shè)備驅(qū)動(dòng)程序或新軟件抽象,是一個(gè)可以由應(yīng)用程<BR>序使用的新功能。<BR> <BR>通過調(diào)用內(nèi)核函數(shù)完成新設(shè)施的注冊(cè)。傳遞的參數(shù)通常為一個(gè)指向描述這個(gè)新設(shè)施的數(shù)<BR>據(jù)結(jié)構(gòu)和要注冊(cè)的設(shè)施名稱。這個(gè)數(shù)據(jù)結(jié)構(gòu)通常會(huì)包含一些指向模塊函數(shù)的指針,這就<BR>是模塊體內(nèi)的函數(shù)是被調(diào)用的機(jī)制。<BR> <BR></P></FONT><FONT
color=#ffffff size=3>
<P><BR>除了用來標(biāo)別模塊類別(如字符和塊設(shè)備驅(qū)動(dòng)程序)的“主”設(shè)施之外,模塊還可以注<BR>冊(cè)如下項(xiàng)目:<BR> <BR>其他設(shè)備<BR> <BR> 由于這類設(shè)施僅僅用于總線型鼠標(biāo),這些設(shè)備曾一度稱為鼠標(biāo)設(shè)備。它們都是些<BR>不完整的設(shè)備,通常要比那些功能健全的設(shè)備簡(jiǎn)單。<BR> <BR>串行端口<BR> <BR> 可以在運(yùn)行時(shí)向系統(tǒng)里加入串口設(shè)備驅(qū)動(dòng)程序;這也是支持PCMCIA調(diào)治解調(diào)器的<BR>機(jī)制。<BR> <BR>行律<BR> <BR> 行律是處理終端數(shù)據(jù)流的軟件層。模塊可以注冊(cè)新行律,以非標(biāo)準(zhǔn)方式處理終端<BR>事務(wù)。例如,模塊kmouse就使用行律從串口鼠標(biāo)中偷取數(shù)據(jù)。<BR> <BR>終端設(shè)備驅(qū)動(dòng)程序<BR> <BR> 終端設(shè)備驅(qū)動(dòng)程序一組實(shí)現(xiàn)終端底層數(shù)據(jù)處理的函數(shù)。控制臺(tái)和串口設(shè)備驅(qū)動(dòng)程<BR>序?yàn)榱藙?chuàng)建終端設(shè)備,它們都要注冊(cè)自己的驅(qū)動(dòng)程序。而多端口串口則有自己的驅(qū)動(dòng)程<BR></P></FONT><FONT
color=#ffffff size=3>
<P>序?yàn)榱藙?chuàng)建終端設(shè)備,它們都要注冊(cè)自己的驅(qū)動(dòng)程序。而多端口串口則有自己的驅(qū)動(dòng)程<BR>序。<BR> <BR>/proc文件<BR> <BR> /proc包含了用來訪問內(nèi)核信息的文件。由于它們也可以用來調(diào)試,第4章的“使<BR>用/proc文件系統(tǒng)”將講解/proc文件。<BR> <BR>二進(jìn)制文件格式<BR> <BR> 對(duì)于每個(gè)可執(zhí)行文件,內(nèi)核掃描“二進(jìn)制文件格式”列表并按相應(yīng)的格式執(zhí)行它<BR>。模塊可以實(shí)現(xiàn)新的格式,Java模塊就是這樣做的。<BR> <BR>Exec域<BR> <BR> 為了提供與其他流行Unix系統(tǒng)的兼容,必須修改內(nèi)核的某些內(nèi)部表格。一個(gè)“執(zhí)<BR>行域”就是一組從其他操作系統(tǒng)約定到Linux系統(tǒng)的映射。例如,模塊可以定義執(zhí)行SCO<BR>二進(jìn)制文件的執(zhí)行域。<BR> <BR>符號(hào)表<BR> <BR> 這個(gè)已在前面的“注冊(cè)符號(hào)表”小節(jié)中介紹了。<BR> <BR></P></FONT><FONT
color=#ffffff size=3>
<P><BR>上面這些項(xiàng)目都不是前一章所考慮的設(shè)備類型,而且都支持那些通常集成到驅(qū)動(dòng)程序功<BR>能中的設(shè)施,如/proc文件和行律。之所以鼠標(biāo)和其他設(shè)備驅(qū)動(dòng)程序都沒有象“完整”字<BR>符設(shè)備那樣管理,這主要是為了方便。過一會(huì)兒,當(dāng)你讀到第3章“字符設(shè)備”的“主從<BR>設(shè)備號(hào)”小節(jié)時(shí),原因就明了了。<BR> <BR>還可以將模塊注冊(cè)為某些驅(qū)動(dòng)程序的附件,但這樣做就太特殊了,這里就不作討論了;<BR>它們都使用了“注冊(cè)符號(hào)表”中講到的堆疊技術(shù)。如果你想做更深一步的探究,你可以<BR>在內(nèi)核源碼中查查register_symtab,并且找找不同驅(qū)動(dòng)程序的入口點(diǎn)。大部分注冊(cè)函數(shù)<BR>都是以register_開始的,這樣你就可以用“register_”在/proc/ksyms找找它們了。<BR> <BR>init_module中的錯(cuò)誤處理<BR>如果你注冊(cè)時(shí)發(fā)生什么錯(cuò)誤,你必須取消失敗前所有已完成的注冊(cè)。例如,如果系統(tǒng)沒<BR>有足夠內(nèi)存分配新數(shù)據(jù)結(jié)構(gòu)時(shí),可能會(huì)發(fā)生錯(cuò)誤。盡管這不太可能,但確實(shí)會(huì)發(fā)生,好<BR>的程序代碼必須為處理這類事件做好準(zhǔn)備。<BR> <BR>Linux不為每個(gè)模塊保留它都注冊(cè)了那些設(shè)施,因此當(dāng)init_module在某處失敗時(shí),模塊<BR>必須統(tǒng)統(tǒng)收回。如果你在注銷你已經(jīng)注冊(cè)的設(shè)施時(shí)失敗了,內(nèi)核就進(jìn)入一種不穩(wěn)定狀態(tài)<BR>:卸載模塊后,由于它們看起來仍然是“忙”的,你再也不能注冊(cè)那些設(shè)施了,而且你<BR>也無法注銷它們了,因?yàn)槟惚仨毷褂媚阕?cè)時(shí)的那個(gè)指針,而你不太可能得到那個(gè)指針<BR>了。恢復(fù)這種情況非常復(fù)雜,通常,重新啟動(dòng)是最好的解決方法。<BR> <BR>我建議你用goto語句處理錯(cuò)誤恢復(fù)。我討厭使用goto,但以我個(gè)人來看,這是一個(gè)它有<BR></P></FONT><FONT
color=#ffffff size=3>
<P>我建議你用goto語句處理錯(cuò)誤恢復(fù)。我討厭使用goto,但以我個(gè)人來看,這是一個(gè)它有<BR>所做為的地方(而且,是唯一的地方)。在內(nèi)核里,通常都會(huì)象這里處理錯(cuò)誤那樣使用g<BR>oto。<BR> <BR>下面這段樣例在成功和失敗時(shí)都能正確執(zhí)行:<BR> <BR>(代碼)<BR> <BR>返回值(err)是一個(gè)錯(cuò)誤編碼。在Linux內(nèi)核里,錯(cuò)誤編碼是一個(gè)負(fù)值,在<linux/errn<BR>o.h>中定義。如果你不使用其他函數(shù)返回的錯(cuò)誤編碼而要生成自己的,你應(yīng)該包含<linu<BR>x/errno.h>,這樣就可以使用諸如-ENODEV,-ENOMEM之類的符號(hào)值。總是返回相應(yīng)的錯(cuò)<BR>誤編碼是種非常好的習(xí)慣,因?yàn)檫@樣一來用戶程序就利用perror或相似的方法把它們轉(zhuǎn)<BR>換成有意義的字符串了。<BR> <BR>很明顯,cleanup_module要取消所有init_module中完成的注冊(cè)。<BR> <BR>(代碼)<BR> <BR>使用計(jì)數(shù)<BR>為了確定模塊是否可以安全地卸載,系統(tǒng)為每個(gè)模塊保留了一個(gè)使用計(jì)數(shù)。由于模塊忙<BR>的時(shí)候是不能卸載模塊的,系統(tǒng)需要這些信息:當(dāng)文件系統(tǒng)還被安裝在系統(tǒng)上時(shí)就不能<BR>刪除這個(gè)文件系統(tǒng)類型,而且你也不能在還有程序使用某個(gè)字符設(shè)備時(shí)就去掉它。<BR> <BR></P></FONT><FONT
color=#ffffff size=3>
<P>如果忘了更新使用計(jì)數(shù),你就不能再卸載模塊了。在開發(fā)期間這種情況很可能發(fā)生,所<BR>以你一定要牢記。例如,如果進(jìn)程因你的驅(qū)動(dòng)程序引用了NULL指針而終止,驅(qū)動(dòng)程序就<BR>不可能區(qū)關(guān)閉設(shè)備,使用計(jì)數(shù)也就無法回復(fù)到0。一種可能的解決方法就是在調(diào)試期間完<BR>全不使用使用計(jì)數(shù),將MOD_INC_USE_COUNT和MOD_DEC_USE_COUNT重新定義為空操作。另<BR>一個(gè)解決方法就是利用其他方法將計(jì)數(shù)強(qiáng)制復(fù)位為0(在第5章的“使用ioctl參數(shù)”小節(jié)<BR>中介紹)。在編寫成品模塊時(shí),決不能投機(jī)取巧。然而在調(diào)試時(shí)期,有時(shí)候忽略一些問<BR>題可以節(jié)省時(shí)間,是可以接受的。<BR> <BR>使用計(jì)數(shù)的當(dāng)前值可以在/proc/modules中每一項(xiàng)的第3個(gè)域中找到。這個(gè)文件顯式系統(tǒng)<BR>中當(dāng)前共加載了那些模塊,每一項(xiàng)對(duì)應(yīng)一個(gè)模塊。其中的域包括,模塊名,模塊使用的<BR>頁面數(shù)和當(dāng)前使用計(jì)數(shù)。這是一個(gè)/proc/modules樣例:<BR> <BR>(代碼)<BR> <BR>(autoclean)標(biāo)志表明模塊由kerneld管理(見第11章)。較新的內(nèi)核中又加入了一些新<BR>的標(biāo)志,除了一件事外,/proc/modules的基本結(jié)構(gòu)完全相同:在內(nèi)核2.1.18和更新的版<BR>本中,長(zhǎng)度用字節(jié)計(jì)而不是頁面計(jì)。<BR> <BR>卸載<BR>要卸載一個(gè)模塊就要使用rmmod命令。由于無需連編,它的任務(wù)遠(yuǎn)比加載簡(jiǎn)單。這個(gè)命令<BR>調(diào)用系統(tǒng)調(diào)用delete_module,如果使用計(jì)數(shù)為0它又調(diào)用模塊的cleanup_module。<BR> <BR>cleanup_module實(shí)現(xiàn)負(fù)責(zé)注銷所有由模塊已經(jīng)注冊(cè)了的項(xiàng)目。只有符號(hào)表是自動(dòng)刪除的<BR></P></FONT><FONT
color=#ffffff size=3>
<P>cleanup_module實(shí)現(xiàn)負(fù)責(zé)注銷所有由模塊已經(jīng)注冊(cè)了的項(xiàng)目。只有符號(hào)表是自動(dòng)刪除的<BR>。<BR> <BR>使用資源<BR>模塊不使用資源是無法完成自己的任務(wù)的,這些資源包括內(nèi)存,I/O端口和中斷,如果你<BR>要用DMA控制器的話,還得有DMA通道。<BR> <BR>做為一個(gè)程序員,你一定已經(jīng)習(xí)慣了內(nèi)存分配管理,在這方面編寫內(nèi)核代碼沒什么區(qū)別<BR>。你的程序使用kmalloc分配內(nèi)存,使用kfree釋放內(nèi)存。除了kmalloc多一個(gè)參數(shù),優(yōu)先<BR>級(jí),外,它們和malloc,free很相似。很多情況下,用優(yōu)先級(jí)GFP_KERNEL就可以了。縮<BR>寫GFP代表“Get Free Page(獲取空閑頁面)。”<BR> <BR>與此不同,獲取I/O端口和中斷乍聽起來怪怪的,因?yàn)槌绦騿T一般同用顯式的指令訪問它<BR>們,不必讓操作系統(tǒng)了解這些。“分配”端口和中斷與分配內(nèi)存不同,因?yàn)閮?nèi)存是從一<BR>個(gè)資源池中分配,并且每個(gè)地址的行為是一樣的;I/O端口都各有自己的作用,而且驅(qū)動(dòng)<BR>程序需要在特定的端口上工作,而不能隨便使用某個(gè)端口。<BR> <BR>端口<BR>對(duì)于大多數(shù)驅(qū)動(dòng)程序而言,它們的典型工作就是讀寫端口。不管是初始化還是正常工作<BR>的時(shí)候,它們都是這樣的。為了避免其他驅(qū)動(dòng)程序的干擾,必須保證設(shè)備驅(qū)動(dòng)程序以獨(dú)<BR>占方式訪問端口――如果一個(gè)模塊探測(cè)因自己的硬件而寫某個(gè)端口,而恰巧這個(gè)端口又<BR>是屬于另一個(gè)設(shè)備的,這之后一定會(huì)發(fā)生點(diǎn)怪事。<BR> <BR></P></FONT><FONT
color=#ffffff size=3>
<P><BR>為了防止不同設(shè)備間的干擾,Linux的開發(fā)者決定實(shí)現(xiàn)端口的請(qǐng)求/釋放機(jī)制。然而,未<BR>授權(quán)的對(duì)端口的訪問并不會(huì)產(chǎn)生類似于“段失效”那樣的錯(cuò)誤――硬件無法支持端口注<BR>冊(cè)。<BR> <BR>從文件/proc/ioports可以以文本方式獲得已注冊(cè)的端口信息,就象下面的樣子:<BR> <BR>(代碼)<BR> <BR>文件中的每一項(xiàng)是有驅(qū)動(dòng)程序鎖定的范圍(以十六進(jìn)制表示)。在這些被釋放前,其他<BR>驅(qū)動(dòng)程序不允許訪問這些端口。<BR> <BR>避免沖突有兩個(gè)途徑。首先,向系統(tǒng)增加新設(shè)備的用戶檢查/proc/ioports,然后在配置<BR>新設(shè)備使用空閑端口――這種方法假設(shè)設(shè)備可以通過跳線進(jìn)行配置。然后,當(dāng)軟件驅(qū)動(dòng)<BR>程序初始化自己時(shí),它能自動(dòng)探測(cè)新設(shè)備而對(duì)其他設(shè)備無害:驅(qū)動(dòng)程序不會(huì)探測(cè)已由其<BR>他驅(qū)動(dòng)程序使用的I/O端口。<BR> <BR>事實(shí)上,基于I/O注冊(cè)的沖突避免對(duì)于模塊化驅(qū)動(dòng)程序很合適,但對(duì)于連編到內(nèi)核里的驅(qū)<BR>動(dòng)程序來說卻可能失敗。盡管我們不涉及這種驅(qū)動(dòng)程序,但還是很是必要注意到,對(duì)于<BR>一個(gè)在啟動(dòng)時(shí)初始化自己的驅(qū)動(dòng)程序來說,由于它要使用之后會(huì)被注冊(cè)的端口,很可能<BR>會(huì)造成對(duì)其他設(shè)備的誤配置。雖然如此,還是沒有辦法讓一個(gè)符合規(guī)范的驅(qū)動(dòng)程序與已<BR>配置好的硬件交互,除非以前加載的驅(qū)動(dòng)程序不注冊(cè)它的端口。基于以上原因,探測(cè)ISA<BR>設(shè)備是件很危險(xiǎn)的事,而且如果隨正式Linux內(nèi)核發(fā)行的驅(qū)動(dòng)程序?yàn)榱艘蚺c尚未加載的模<BR></P></FONT><FONT
color=#ffffff size=3>
<P>設(shè)備是件很危險(xiǎn)的事,而且如果隨正式Linux內(nèi)核發(fā)行的驅(qū)動(dòng)程序?yàn)榱艘蚺c尚未加載的模<BR>塊對(duì)應(yīng)的設(shè)備交互,拒絕在模塊加載時(shí)執(zhí)行探測(cè)功能。<BR> <BR>設(shè)備探測(cè)的問題是因?yàn)橹挥幸环N方法標(biāo)別設(shè)備,即通過寫目標(biāo)端口然后再讀的方法――<BR>處理器(而且是任何程序)只能查看數(shù)據(jù)線上的電子信號(hào)。驅(qū)動(dòng)程序編寫者知道一旦設(shè)<BR>備連接到某個(gè)特定的端口上,它就會(huì)響應(yīng)相應(yīng)的查詢代碼。但是如果另一個(gè)設(shè)備連到了<BR>端口上,程序仍然會(huì)寫這個(gè)設(shè)備,但天知道它會(huì)怎么響應(yīng)這個(gè)異常的探測(cè)操作。有時(shí)可<BR>以通過讀外設(shè)的BIOS,查看一個(gè)已知的字串來避免端口探測(cè);已有若干SCSI設(shè)備使用了<BR>這種技術(shù),但并不是每個(gè)設(shè)備都要有自己的BIOS。<BR> <BR>一個(gè)符合規(guī)范的驅(qū)動(dòng)程序應(yīng)該調(diào)用check_region查看是否某個(gè)端口區(qū)域已由其他驅(qū)動(dòng)程<BR>序鎖定,之后就用request_region將端口鎖住,當(dāng)驅(qū)動(dòng)程序不再使用端口時(shí)調(diào)用release<BR>_region釋放端口。這些函數(shù)的原型在<linux/ioports.h>中。<BR> <BR>注冊(cè)端口的典型順序如下所示(函數(shù)skull_probe_hw包含了所有設(shè)備相關(guān)代碼,這里沒<BR>有出現(xiàn)):<BR> <BR>(代碼)<BR> <BR>在cleanup_module里釋放端口:<BR> <BR>(代碼)<BR> <BR></P></FONT><FONT
color=#ffffff size=3>
<P><BR>系統(tǒng)也使用了一套類似的請(qǐng)求/釋放策略維護(hù)中斷,但注冊(cè)/注銷中斷比處理端口復(fù)雜,<BR>整個(gè)過程的詳細(xì)解釋將放在第9章“中斷處理”中介紹。<BR> <BR>與前面講到的關(guān)于設(shè)施的注冊(cè)/注銷相似,對(duì)資源的請(qǐng)求/釋放方法也適合使用已勾勒的<BR>基于goto的實(shí)現(xiàn)框架。<BR> <BR>對(duì)于編寫PCI設(shè)備驅(qū)動(dòng)程序的人來說,不存在這里所講的探測(cè)問題。我將在第15章“外部<BR>總線簡(jiǎn)介”中介紹。<BR> <BR>ISA內(nèi)存<BR>本節(jié)技術(shù)性很強(qiáng),如果你對(duì)處理硬件問題不是很有把握,可以簡(jiǎn)單跳過這節(jié)。<BR> <BR>在Intel平臺(tái)上,ISA槽上的目標(biāo)設(shè)備可能會(huì)提供片上內(nèi)存,范圍在640KB到1MB之間(0xA<BR>0000到0xFFFFF);這也是設(shè)備驅(qū)動(dòng)程序可以使用的一類資源。<BR> <BR>這種內(nèi)存部件反映了8086處理器那個(gè)時(shí)代,當(dāng)時(shí)8086的尋址只有一兆的大小。PC設(shè)計(jì)人<BR>員決定,低端的640KB當(dāng)做RAM,而保留另外的384KB用于ROM和內(nèi)存映射設(shè)備。今天,即<BR>便是最強(qiáng)力的個(gè)人電腦也還有這個(gè)在第一兆字節(jié)里的空洞。Linux的PC版保留了這片內(nèi)存<BR>,根本不考慮使用它。本節(jié)給出的代碼可以讓你訪問這個(gè)區(qū)域的內(nèi)存,但它僅限于x86平<BR>臺(tái),而且Linux內(nèi)核要至少是2.0.x的,x是多少都可以。2.1版改變了物理內(nèi)存的訪問方<BR>式,比如,640KB-1MB這段范圍內(nèi)的I/O內(nèi)存就不能再這樣訪問。訪問I/O內(nèi)存的正確方式<BR>是第18章“硬件管理”“低1M內(nèi)的ISA內(nèi)存”小節(jié)中的內(nèi)容,這超出了本章的范圍。<BR></P></FONT><FONT
color=#ffffff size=3>
<P>是第18章“硬件管理”“低1M內(nèi)的ISA內(nèi)存”小節(jié)中的內(nèi)容,這超出了本章的范圍。<BR> <BR>盡管內(nèi)核提供了端口和中斷的請(qǐng)求/釋放機(jī)制,當(dāng)前它還是沒能提供給I/O內(nèi)存類似的機(jī)<BR>制,所以你得自己做了。如果我能理解Linus是如何看待PC體系結(jié)構(gòu)的化,這里給的方法<BR>就不會(huì)變化了。<BR> <BR>有時(shí)某個(gè)驅(qū)動(dòng)程序需要在初始化時(shí)探測(cè)ISA內(nèi)存;例如,我需要告訴視頻截取器(frame<BR>grabber)在哪映射截取的圖象。問題是,如果沒有探測(cè)方法,我將無法辨別那段范圍內(nèi)<BR>哪塊內(nèi)存正在使用。人們需要能夠辨別3種不同的情況:映射了RAM,有ROM(例如,VGA<BR>BIOS),或者那段區(qū)域空閑。<BR> <BR>skull樣例給出一種處理這些內(nèi)存的方法,但由于skull和物理設(shè)備無關(guān),它打印完640KB<BR>-1MB這段內(nèi)存區(qū)域的信息后就退出了。然而,有必要談一談?dòng)糜诜治鰞?nèi)存的代碼,因?yàn)?lt;BR>它必須處理一些競(jìng)爭(zhēng)條件。競(jìng)爭(zhēng)條件就是這樣一種情形,兩個(gè)任務(wù)可以競(jìng)爭(zhēng)同一個(gè)資源<BR>,而且未同步的操作可能會(huì)損壞系統(tǒng)。<BR> <BR>盡管驅(qū)動(dòng)程序編寫者無需處理多任務(wù),我們還是必須記住,中斷可能在你的代碼中間發(fā)<BR>生,而且中斷處理函數(shù)可能會(huì)不提醒你就修改全局量。盡管內(nèi)核提供了許多工具處理競(jìng)<BR>爭(zhēng)條件,下面給得出的簡(jiǎn)單規(guī)則闡述了處理這個(gè)問題的方法;對(duì)這個(gè)問題的徹底對(duì)策將<BR>在第9章的“競(jìng)爭(zhēng)條件”小節(jié)中給出。<BR> <BR>l 如果僅僅是讀取共享的量,而不是寫,將其聲明為volatile,要求編譯器不對(duì)<BR>其進(jìn)行優(yōu)化。這樣,編譯好的代碼在每次源碼讀取它時(shí)讀取這個(gè)量了。<BR></P></FONT><FONT
color=#ffffff size=3>
<P>其進(jìn)行優(yōu)化。這樣,編譯好的代碼在每次源碼讀取它時(shí)讀取這個(gè)量了。<BR> <BR>l 如果代碼需要檢查和修改這個(gè)值,必須在操作期間關(guān)閉中斷,這樣可以防止其<BR>他進(jìn)程在我們檢查過這個(gè)值后,但恰恰又在我們修改這個(gè)量之前修改這個(gè)量。<BR> <BR>我們建議采用如下關(guān)閉中斷的順序:<BR> <BR>(代碼)<BR> <BR>這里cli代表“clear interrupt flag(清除中斷標(biāo)志)”。上面出現(xiàn)的函數(shù)都定義在<a<BR>sm/system.h>中。<BR> <BR>應(yīng)該避免使用經(jīng)典的cli和sti序列,因?yàn)橛袝r(shí)你無法在關(guān)閉中斷前斷定中斷是否打開了<BR>。如果此時(shí)調(diào)用sti就是產(chǎn)生很不規(guī)則的錯(cuò)誤出現(xiàn),很難追蹤這樣的錯(cuò)誤。<BR> <BR>由于那段內(nèi)存只能通過寫物理內(nèi)存和讀取檢查才能標(biāo)別,而且如果測(cè)試期間有中斷的化<BR>,有可能會(huì)被其他程序修改,因此檢查RAM段的代碼同時(shí)利用了volatile聲明和cli。下<BR>面的這段代碼并不是很簡(jiǎn)單,如果一個(gè)設(shè)備正在象它的內(nèi)存寫數(shù)據(jù),而這段代碼又在掃<BR>描這段區(qū)域,它就會(huì)誤認(rèn)為這段區(qū)域是空閑區(qū)。好在這樣的情況很少發(fā)生。<BR> <BR>在下面的源代碼中,每個(gè)printk都帶有一個(gè)KERN_INFO前綴。這個(gè)符號(hào)拼接在格式字串前<BR>面做消息的優(yōu)先級(jí),它定義在<linux/kernel.h>中。這個(gè)符號(hào)展開后與本章開始的hello<BR>..c中使用的<1>字串很相似。<BR></P></FONT><FONT
color=#ffffff size=3>
<P>..c中使用的<1>字串很相似。<BR> <BR>(代碼)<BR> <BR>如果你在探測(cè)時(shí)注意恢復(fù)你所修改的字節(jié),探測(cè)內(nèi)存不會(huì)造成與其他設(shè)備的沖突。*<BR> <BR>作為一個(gè)細(xì)心的讀者,你可能會(huì)知道在15MB-16MB地址域內(nèi)的ISA內(nèi)存是怎么回事。很不<BR>幸,那是個(gè)更棘手的問題,我們將在第8章的“1M以上的ISA內(nèi)存”小節(jié)中討論。<BR> <BR>自動(dòng)和手動(dòng)配置<BR>根據(jù)系統(tǒng)的不同,驅(qū)動(dòng)程序需要了解的若干參數(shù)也會(huì)隨之變化。例如,設(shè)備必須了解硬<BR>件的I/O地址或內(nèi)存區(qū)域。<BR> <BR>注意,本節(jié)所討論的大部分問題并不適用于PCI設(shè)備(第15章介紹)。<BR> <BR>根據(jù)設(shè)備的不同,除了I/O地址外,還有一些其他參數(shù)會(huì)影響系統(tǒng)的驅(qū)動(dòng)程序的行為,如<BR>設(shè)備的品牌和發(fā)行號(hào)。驅(qū)動(dòng)程序?yàn)榱苏_地工作有必要了解這些參數(shù)的具體值。用正確<BR>的數(shù)值設(shè)置驅(qū)動(dòng)程序(即,配置它)是一項(xiàng)需要在初始化期間完成的復(fù)雜的任務(wù)。<BR> <BR>基本說來,有兩種方式可以獲得這些正確的數(shù)值:或者是用戶顯式地給出它們,或者是<BR>驅(qū)動(dòng)程序自己探測(cè)。無疑,自動(dòng)探測(cè)是最好的驅(qū)動(dòng)程序配置方法,而用戶配置則是最好<BR>實(shí)現(xiàn)的;作為驅(qū)動(dòng)程序編寫者的一種權(quán)衡,他應(yīng)該盡可能地實(shí)現(xiàn)自動(dòng)配置,但又允許用<BR>戶配置作為一種可選的方式替代自動(dòng)配置。這種配置方法的另一個(gè)好處就是,在開發(fā)期<BR></P></FONT><FONT
color=#ffffff size=3>
<P>戶配置作為一種可選的方式替代自動(dòng)配置。這種配置方法的另一個(gè)好處就是,在開發(fā)期<BR>間可以給定參數(shù),從而不用自動(dòng)探測(cè),可以在以后實(shí)現(xiàn)它。<BR> <BR>insmod在加載時(shí)接受命令行中給定的整數(shù)和字串值,可以給參數(shù)賦值。這條命令可以修<BR>改在模塊中定義的全局變量。例如,如果你的源碼中包含了這些變量:<BR> <BR>(代碼)<BR> <BR>那么你就可以使用如下命令加載模塊:<BR> <BR>(代碼)<BR> <BR>例子里使用了printk,它可以顯式,當(dāng)init_module被調(diào)用時(shí),賦值已經(jīng)發(fā)生了。注意,<BR>insmod可以給任何整型或字符指針變量賦值,不管它們是否是公共符號(hào)表中的一部分。<BR>但對(duì)于聲明為數(shù)組的串是不能在加載時(shí)賦值的,因?yàn)樗呀?jīng)在編譯時(shí)解析出來了,以后<BR>就不能修改了。<BR> <BR>自動(dòng)配置可以設(shè)計(jì)為按如下方式工作:“如果配置變量是默認(rèn)值,就執(zhí)行自動(dòng)探測(cè);否<BR>則,保留當(dāng)前值。”為了讓這種方法可以工作,“默認(rèn)”值應(yīng)該不是任何用戶可以在加<BR>載時(shí)設(shè)定的值。<BR> <BR>下面這段代碼給出了skull是如何自動(dòng)探測(cè)設(shè)備的端口地址的。在這個(gè)例子中,使用自動(dòng)<BR>探測(cè)查找多個(gè)設(shè)備,而手動(dòng)配置只限于一個(gè)設(shè)備。注意,函數(shù)skull_detect在上面已經(jīng)<BR></P></FONT><FONT
color=#ffffff size=3>
<P>探測(cè)查找多個(gè)設(shè)備,而手動(dòng)配置只限于一個(gè)設(shè)備。注意,函數(shù)skull_detect在上面已經(jīng)<BR>給出了,而skull_init_board負(fù)責(zé)完成設(shè)備相關(guān)的初始化工作,這里沒有給出。<BR> <BR>(代碼)<BR> <BR>為了方便用戶在insmod命令行中給出相應(yīng)的參數(shù),而且如果這些符號(hào)不會(huì)放到主符號(hào)表<BR>中的話,實(shí)際使用的驅(qū)動(dòng)程序可以去掉配置變量的前綴(在本例中就是skull_)。如果<BR>它們確實(shí)要放到主符號(hào)表中,好的辦法就是聲明兩個(gè)符號(hào):一個(gè)沒有前綴,在加載時(shí)賦<BR>值,一個(gè)有前綴,用register_symtab放到符號(hào)表中。<BR> <BR>在用戶空間編寫驅(qū)動(dòng)程序<BR>到現(xiàn)在為止,一個(gè)首次接觸內(nèi)核問題的Unix程序員困難會(huì)對(duì)編寫模塊非常緊<BR>--<BR> <BR><FONT
color=#00ff00>※ 來源:.華南網(wǎng)木棉站 bbs.gznet.edu.cn.[FROM: 202.38.196.234]</FONT><BR>--<BR><FONT
color=#00ffff>※ 轉(zhuǎn)寄:.華南網(wǎng)木棉站 bbs.gznet.edu.cn.[FROM: 211.80.41.106]</FONT><BR>--<BR><FONT
color=#0000ff>※ 轉(zhuǎn)寄:.華南網(wǎng)木棉站 bbs.gznet.edu.cn.[FROM: 211.80.41.106]</FONT><BR>--<BR><FONT
color=#ffff00>※ 轉(zhuǎn)載:.南京大學(xué)小百合站 bbs.nju.edu.cn.[FROM: 211.80.41.106]</FONT><BR> <BR>--<BR><FONT
color=#ff0000>※ 轉(zhuǎn)載:·飲水思源 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/3.htm"><FONT color=#ffffff
size=2>上一頁</FONT></A> | <A
href="http://211.71.69.201/joyfire/lsdp/5.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>
<TABLE cellSpacing=0 cellPadding=0 width="100%" bgColor=#666666
border=0><TBODY>
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -