內(nèi)核模塊的編寫和運行_第1頁
內(nèi)核模塊的編寫和運行_第2頁
內(nèi)核模塊的編寫和運行_第3頁
內(nèi)核模塊的編寫和運行_第4頁
內(nèi)核模塊的編寫和運行_第5頁
已閱讀5頁,還剩5頁未讀 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

第五章模塊編程實驗【實驗?zāi)康摹客ㄟ^學(xué)習(xí)內(nèi)核模塊的編寫和運行,了解模塊是LinuxOS的一種特有的機制,可根據(jù)用戶的實際需要在不需要對內(nèi)核進行重新編譯的情況下,模塊能在內(nèi)核中被動態(tài)地加載和卸載。編寫一個模塊,將它作為LinuxOS內(nèi)核空間的擴展來執(zhí)行,并通過insmod命令來手工加載,通過命令rmmod來手工卸載?!緶?zhǔn)備知識】Linux模塊是一些可以作為獨立程序來編譯的函數(shù)和數(shù)據(jù)類型的集合。在裝載這些模塊時,將它的代碼鏈接到內(nèi)核中°Linux模塊有兩種裝載方式:靜態(tài)裝載(內(nèi)核啟動時裝載)和動態(tài)裝載(在內(nèi)核運行過程中裝載)。若在模塊裝載之前就調(diào)用了動態(tài)模塊的一個函數(shù),則此調(diào)用將失??;若模塊已被裝載,則內(nèi)核就可以使用系統(tǒng)調(diào)用,并將其傳遞到模塊中的相應(yīng)函數(shù)。模塊通常用來實現(xiàn)設(shè)備驅(qū)動程序(這要求模塊的API和設(shè)備驅(qū)動程序的API相一致)。模塊可用來實現(xiàn)所期望的任何功能。模塊的組織結(jié)構(gòu)模塊一旦被裝載進系統(tǒng),就在內(nèi)核地址空間中管態(tài)下執(zhí)行。它就像任何標(biāo)準(zhǔn)的內(nèi)核代碼一樣成為內(nèi)核的一部分,并擁有其它內(nèi)核代碼相同的權(quán)限和職責(zé)(當(dāng)然也會引起系統(tǒng)的崩潰)。若模塊知道內(nèi)核數(shù)據(jù)結(jié)構(gòu)的地址,則它可以讀寫內(nèi)核數(shù)據(jù)結(jié)構(gòu)。但Linux是一個整體式的內(nèi)核(monolithickernel)結(jié)構(gòu),整個內(nèi)核是一個單獨的且非常大的程序,從而存在一個普遍的問題:在一個文件中實現(xiàn)的函數(shù)可能需要在其它文件中定義的數(shù)據(jù)。在傳統(tǒng)的程序中,這個問題是通過鏈接編輯器在生成可執(zhí)行對象文件時,使用鏈接編輯器可以解析的外部(全局)變量來解決的。又因為模塊的設(shè)計和實現(xiàn)與內(nèi)核無關(guān),所以模塊不能靠靜態(tài)鏈接通過變量名引用內(nèi)核數(shù)據(jù)結(jié)構(gòu)。恰好相反,Linux內(nèi)核采用了另外一種機制:實現(xiàn)數(shù)據(jù)結(jié)構(gòu)的文件可以導(dǎo)出結(jié)構(gòu)的符號名(可以從文件/proc/ksyms或文件/???/kernel/ksyms.c中以文本方式讀取這個公開符號表),這樣在運行時就可以使用這個結(jié)構(gòu)了。不過在編寫模塊的過程中,編寫(修改)導(dǎo)出變量時要格外注意,因為通過修改變量會修改內(nèi)核的狀態(tài),其結(jié)果可能并不是內(nèi)核設(shè)計者所期望的。在確信自己了解修改內(nèi)核變量的后果之前,應(yīng)該對這些變量只進行讀操作。模塊作為一種抽象數(shù)據(jù)類型,它具有一個可以通過靜態(tài)內(nèi)核中斷的接口。最小的模塊結(jié)構(gòu)必須包括兩個函數(shù),它們在系統(tǒng)裝載模塊和卸載模塊時調(diào)用,分別是init_module()和cleanup_module()??梢跃帉懸粋€只包括這兩個函數(shù)的模塊,這樣該模塊中唯一會被調(diào)用的函數(shù)就是模塊被裝載時所調(diào)用的函數(shù)init_module()和模塊被卸載時所調(diào)用的函數(shù)cleanup_module()。并且用函數(shù)init_module()來啟動模塊裝載期間的操作,用函數(shù)cleanup_module()來停止這些操作。由于模塊可以實現(xiàn)相當(dāng)復(fù)雜的功能,故可以在模塊中加入很多新函數(shù)以實現(xiàn)所要期望的功能。不過加入模塊的每個新函數(shù)都必須在該模塊裝載到內(nèi)核中時進行注冊。若該模塊是靜態(tài)裝載的,則該模塊的所有函數(shù)都是在內(nèi)核啟動時進行注冊的;若該模塊是動態(tài)裝載的,則這些新函數(shù)必須在裝載這個模塊時動態(tài)注冊。當(dāng)然,如果該模塊被動態(tài)卸載了,則該模塊的函數(shù)都必須從系統(tǒng)中注銷。通過這種方式,當(dāng)這個模塊不在系統(tǒng)中時,就不能調(diào)用該模塊的函數(shù)。其中注冊工作通常是在函數(shù)init_module()中完成的,而注銷工作通常是在函數(shù)cleanup_module()中完成的。由上述定義的模塊應(yīng)有如下的格式:#include<linux/kernel.h>#include<linux/module.h> //其它header信息intinit_module(){ //裝載時,初始化模塊的編碼} //期望該模塊所能實現(xiàn)的一些功能函數(shù),如open()、release。、write()、//read()、ioctl()等函數(shù)voidcleanup_module(){ //卸載時,注銷模塊的編碼}。模塊的編譯一旦設(shè)計并編寫好模塊,必須將其編譯成一個適合內(nèi)核裝載的對象文件。由于編寫模塊是用C語言來完成的,故采用gcc編譯器來進行編譯。若需要通知編譯程序把這個模塊作為內(nèi)核代碼而不是普通的用戶代碼來編譯,則就需向gcc編譯器傳遞參數(shù)“-D__KERNEL__”;若需要通知編譯程序這個文件是一個模塊而不是一個普通文件,則就需向gcc編譯器傳遞參數(shù)“-DMODULE”;若需要對模塊程序進行優(yōu)化編譯、連接,則就需使用“-O2”參數(shù);若還需要對裝載后的模塊進行調(diào)試,則就需使用“-g”參數(shù);同時需要使用“-Wall”參數(shù)來向裝載程序傳遞all,使用“-c”開關(guān)通知編譯程序在編譯完這個模塊文件后不調(diào)用鏈接程序。一般編譯模塊文件的命令格式如下:#gcc-O2-g-Wall-DMODULE-D__KERNEL__-c//為自己編寫的模塊程序源代碼文件執(zhí)行命令后就會得到文件,該文件就是一個可裝載的目標(biāo)代碼文件。模塊的裝載內(nèi)核模塊的裝載方式有兩種。一種是使用insmod命令手工裝載模塊;另一種是請求裝載demandloading(在需要時裝載模塊),即當(dāng)有必要裝載某個模塊時,若用戶安裝了核心中不存在的文件系統(tǒng)時,核心將請求內(nèi)核守護進程kerneld準(zhǔn)備裝載適當(dāng)?shù)哪K。該內(nèi)核守護進程是一個帶有超級用戶權(quán)限的普通用戶進程。此實驗中我們主要采用insmod命令手工裝載模塊。系統(tǒng)啟動時,kerneld開始執(zhí)行,并為內(nèi)核打開一個IPC通道,內(nèi)核通過向kerneld發(fā)送消息請求執(zhí)行各種任務(wù)。kerneld的主要功能是裝載和卸載內(nèi)核模塊,kerneld自身并不執(zhí)行這些任務(wù),它通過某些程序(如insmod)來完成。Kerneld只是內(nèi)核的代理,只為內(nèi)核進行調(diào)度。insmod程序必須找到請求裝載的內(nèi)核模塊(該請求裝載的模塊一般被保存在/lib/modules/kernel-version中)。這些模塊與系統(tǒng)中其它程序一樣是已連接的目標(biāo)文件,但不同的是它們被連接成可重定位映象(即映象沒有被連接到在特定的地址上運行,其文件格式是a.out或ELF)。亦就是說,模塊在用戶空間(使用適當(dāng)?shù)臉?biāo)志)進行編譯,結(jié)果產(chǎn)生一個可執(zhí)行格式的文件。在用insmod命令裝載一個模塊時,將會發(fā)生如下事件:新模塊(通過內(nèi)核函數(shù)create_module())加入到內(nèi)核地址空間。insmod執(zhí)行一個特權(quán)級系統(tǒng)調(diào)用get_kernel_syms()函數(shù)以找到內(nèi)核的輸出符號(一個符號表示為符號名和符號值,如地址值)。create_module()為這個模塊分配內(nèi)存空間,并將新模塊添加在內(nèi)核模塊鏈表的尾部,然后將新模塊標(biāo)記為UNINITIALIZED]模塊未初始化)。通過init_module()系統(tǒng)調(diào)用裝載模塊。(該模塊定義的符號在此時被導(dǎo)出,供其它可能后來裝載的模塊使用)(5) insmod為新裝載的模塊調(diào)用init_module()函數(shù),然后將新模塊標(biāo)志為RUNNING(模塊正在運行)。在執(zhí)行完insmod命令后,就可在/proc/modules文件中看到裝載的新模塊了。(為證實其正確性,可在執(zhí)行insmod命令之前先查看/proc/modules文件,執(zhí)行之后再查看比較)模塊的卸載當(dāng)一個模塊不需要使用時,可以使用rmmod命令卸載該模塊。由于無需連接,故它的任務(wù)比加載模塊要簡單得多(但如果請求裝載模塊在其使用計數(shù)為0時,kerneld將自動從系統(tǒng)中卸載該模塊。卸載時調(diào)用模塊的cleanup_module()釋放分配給該模塊的內(nèi)核資源,并將其標(biāo)志為DELETED(模塊被卸載);同時斷開內(nèi)核模塊鏈表中的連接,修改它所依賴的其它模塊的引用,重新分配模塊所占的內(nèi)核內(nèi)存。模塊連接到內(nèi)核的示意圖該圖比較明顯地展示了模塊連接到內(nèi)核所使用的命令和函數(shù),以及各個函數(shù)之間的調(diào)用關(guān)系。通過該圖,可以比較清晰地看出模塊連接到內(nèi)核的整個連接過程,這也有助于內(nèi)核模塊的編寫。模塊程序中管理模塊的幾個文件操作在內(nèi)核內(nèi)部用一個file結(jié)構(gòu)來識別模塊,而且內(nèi)核使用結(jié)構(gòu)來訪問模塊程序中的函數(shù)。結(jié)構(gòu)是一個定義在<linux/fs.h>中的函數(shù)指針表。管理模塊的文件操作,通常也稱為“方法”,它們都為struct提供函數(shù)指針。在struct中的操作一般按如下順序出現(xiàn),除非說明,它們返回0值時表示訪問成功;發(fā)生錯誤時返回一個負的錯誤值(目前共有13個操作):int(*lseek)()、int(*read)()、int(*write)()、int(*readdir)()、int(*select)()、int(*ioctl)()、int(*mmap)()、int(*open)()、void(*release)()、int(*fsync)()、int(*fasync)()、int(*check_media_change)()、int(*revalidate)()下面我們只簡單介紹其中的幾個操作,其它在以后涉及時再介紹:1、 方法int(*read)(structinode*,structfile*,char*,int)該方法用來從模塊中讀取數(shù)據(jù)。當(dāng)其為NULL指針時將引起read系統(tǒng)調(diào)用返回-EINVAL(“非法參數(shù)”)。函數(shù)返回一個非負值表示成功地讀取了多少字節(jié)。2、 方法int(*write)(structinode*,structfile*,constchar*,int)該方法用來向模塊發(fā)送數(shù)據(jù)。當(dāng)其為NULL指針時將引起write系統(tǒng)調(diào)用返回-EINVAL。如果函數(shù)返回一個非負值,則表示成功地寫入了多少字節(jié)。3、 方法int(*open)(structinode*,structfile*)該方法是用來打開模塊的操作,它是操作在模塊節(jié)點上的第一個操作,即使這樣,該方法還是可以為NULL指針。如果為NULL指針,則表示該模塊的打開操作永遠成功,但系統(tǒng)不會通知你的模塊程序。4、 方法void(*release)(structinode*,structfile*)該方法是用來關(guān)閉模塊的操作。當(dāng)節(jié)點被關(guān)閉時就調(diào)用這個操作。與open類似,release也可以為NULL指針。當(dāng)在你的模塊中需要上面這些方法時,相應(yīng)的方法若沒有,則在struct中相應(yīng)的地方將其令為NULL指針。這樣我們需要的大概象下面這樣:structmodulename_fops={NULL,//modulename_lseekmodulename_read,modulename_write,NULL,//modulename_readdirNULL,//modulename_selectNULL,//modulename_ioctlNULL,//modulename_mmapmodulename_open,modulename_release,NULL,//modulename_fsyncNULL,//modulename_fasyncNULL,//modulename_check_media_changeNULL//modulename_revalidate【實驗內(nèi)容】1、編寫一個簡單的內(nèi)核模塊,該模塊至少需要有兩個函數(shù):一個是init_module()函數(shù),在把模塊裝載到內(nèi)核時被調(diào)用,它為內(nèi)核的某些東西注冊一個處理程序,或是用自身的代碼取代某個內(nèi)核函數(shù);另一個是cleanup_module()函數(shù),在卸載模塊時被調(diào)用,其任務(wù)是清除init_module()函數(shù)所做的一切操作。編寫完成后進行該模塊的編譯、裝載和卸載操作。2、向上面模塊中再添加一些新函數(shù),如open()、release。、write()和read()函數(shù),并編寫一個函數(shù)來測試你的模塊能否實現(xiàn)自己添加的函數(shù)的功能。其中open()、release()和write()函數(shù)都可以是空操作或較少的操作,它們僅僅為結(jié)構(gòu)提供函數(shù)指針?!緦嶒炛笇?dǎo)】1一個簡單的內(nèi)核模塊1.1必要的header文件:除了前面講到的頭文件#include<linux/kernel.h>和#include<linux/module.h>外,如果你的內(nèi)核打開了版本檢查,那么我們就還必須增加頭文件#include<linux/modversions.h>,否則就會出錯。init_module()函數(shù):由于題目的要求不高,故可只在該函數(shù)里完成一個打印功能,如printk(“Hello!Thisisatestingmodule!\n);等。為便于檢查模塊是否裝載成功,我們可以給一個返回值,如return0;若返回一個非0值,則表示init_module()失敗,從而不能裝載模塊。cleanup_module()函數(shù):只需用一條打印語句來取消init_module()函數(shù)所做的打印功能操作就可以了,如printk(“Sorry!Thetestingmoduleisunloadednow!\n”);等。1.4模塊的編寫:此處把該模塊文件取名為testmodule.c#include<linux/kernel.h> //在內(nèi)核模塊中共享#include<linux/module.h> //一個模塊//處理CONFIG_MODVERSIONS#ifCONFIG_MODVERSIONS==1#defineMODVERSIONS#include<linux/modversions.h>#endifintinit_module()//初始化模塊{printk(“Hello!Thisisatestingmodule!\n”);return0;}voidcleanup_module()//取消init_module()函數(shù)所做的打印功能操作{printk(“Sorry!Thetestingmoduleisunloadingnow!\n”);}1.5模塊的編譯、裝載和卸載:[root@linux/]#gcc-o2-Wall-DMODULE-D_KERNEL__-ctestmodule.c[root@linux/]#ls-s //在當(dāng)前目錄下查看生成的目標(biāo)文件testmodule.o現(xiàn)在,模塊testmodule已經(jīng)編譯好了。用下面命令將它裝載到系統(tǒng)中:[root@linux/]#insmod-ftestmodule.o如果裝載成功,則在/proc/modules文件中就可看到模塊testmodule,并可看到它的主設(shè)備號。同時在終端顯示:Hello!Thisisatestingmodule!如果要卸載,就用如下命令:[root@linux/]#rmmodtestmodule如果卸載成功,則在/proc/devices文件中就可看到模塊testmodule已經(jīng)不存在了。同時在終端顯示:Sorry!Thetestingmoduleisunloadingnow!2向testmodule模塊中添加新函數(shù)open()、release。、write()和read()函數(shù)open()intopen(structinode*inode,structfile*filp){MOD_INC_USE_COUNT;//增加該模塊的用戶數(shù)目printk(“Thismoduleisinopen!\n”);return0;}函數(shù)release()voidrelease(structinode*inode,structfile*filp){MOD_DEC_USE_COUNT; //該模塊的用戶數(shù)目減1printk(“Thismoduleisinrelease!\n”);return0;#ifdefDEBUGprintk(“release(%p,%p)\n”,inode,filp);#endif}函數(shù)read()intread(structinode*inodestructfile*filpchar*buf,intcount){intleave;if(verify_area(VERIFY_WRITE,buf,count)==DEFAULT)returnDEFAULT;for(leave=count;leave>0;

溫馨提示

  • 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)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論