![內核模塊的編寫和運行[互聯網+]_第1頁](http://file1.renrendoc.com/fileroot_temp2/2021-2/21/5222a58c-08a3-4b03-8c24-824572d8ed05/5222a58c-08a3-4b03-8c24-824572d8ed051.gif)
![內核模塊的編寫和運行[互聯網+]_第2頁](http://file1.renrendoc.com/fileroot_temp2/2021-2/21/5222a58c-08a3-4b03-8c24-824572d8ed05/5222a58c-08a3-4b03-8c24-824572d8ed052.gif)
![內核模塊的編寫和運行[互聯網+]_第3頁](http://file1.renrendoc.com/fileroot_temp2/2021-2/21/5222a58c-08a3-4b03-8c24-824572d8ed05/5222a58c-08a3-4b03-8c24-824572d8ed053.gif)
![內核模塊的編寫和運行[互聯網+]_第4頁](http://file1.renrendoc.com/fileroot_temp2/2021-2/21/5222a58c-08a3-4b03-8c24-824572d8ed05/5222a58c-08a3-4b03-8c24-824572d8ed054.gif)
下載本文檔
版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
1、 第五章 模塊編程實驗 【實驗目的】 通過學習內核模塊的編寫和運行,了解模塊是Linux OS的一種特有的機制,可根據用戶的實際需要在不需要對內核進行重新編譯的情況下,模塊能在內核中被動態(tài)地加載和卸載。編寫一個模塊,將它作為Linux OS內核空間的擴展來執(zhí)行,并通過insmod命令來手工加載,通過命令rmmod來手工卸載。 【準備知識】 Linux模塊是一些可以作為獨立程序來編譯的函數和數據類型的集合。在裝載這些模塊時,將它的代碼鏈接到內核中。Linux模塊有兩種裝載方式:靜態(tài)裝載(內核啟動時裝載)和動態(tài)裝載(在內核運行過程中裝載)。若在模塊裝載之前就調用了動態(tài)模塊的一個函數,則此調用將失敗
2、;若模塊已被裝載,則內核就可以使用系統(tǒng)調用,并將其傳遞到模塊中的相應函數。模塊通常用來實現設備驅動程序(這要求模塊的API和設備驅動程序的API相一致)。模塊可用來實現所期望的任何功能。 一模塊的組織結構 模塊一旦被裝載進系統(tǒng),就在內核地址空間中管態(tài)下執(zhí)行。它就像任何標準的內核代碼一樣成為內核的一部分,并擁有其它內核代碼相同的權限和職責(當然也會引起系統(tǒng)的崩潰)。若模塊知道內核數據結構的地址,則它可以讀寫內核數據結構。但Linux是一個整體式的內核(monolithic kernel)結構,整個內核是一個單獨的且非常大的程序,從而存在一個普遍的問題:在一個文件中實現的函數可能需要在其它文件中定
3、義的數據。在傳統(tǒng)的程序中,這個問題是通過鏈接編輯器在生成可執(zhí)行對象文件時,使用鏈接編輯器可以解析的外部(全局)變量來解決的。又因為模塊的設計和實現與內核無關,所以模塊不能靠靜態(tài)鏈接通過變量名引用內核數據結構。恰好相反,Linux內核采用了另外一種機制:實現數據結構的文件可以導出結構的符號名(可以從文件/proc/ksyms或文件/kernel/ksyms.c中以文本方式讀取這個公開符號表),這樣在運行時就可以使用這個結構了。不過在編寫模塊的過程中,編寫(修改)導出變量時要格外注意,因為通過修改變量會修改內核的狀態(tài),其結果可能并不是內核設計者所期望的。在確信自己了解修改內核變量的后果之前,應該對
4、這些變量只進行讀操作。 模塊作為一種抽象數據類型,它具有一個可以通過靜態(tài)內核中斷的接口。最小的模塊結構必須包括兩個函數,它們在系統(tǒng)裝載模塊和卸載模塊時調用,分別是init_module()和cleanup_module()??梢跃帉懸粋€只包括這兩個函數的模塊,這樣該模塊中唯一會被調用的函數就是模塊被裝載時所調用的函數init_module()和模塊被卸載時所調用的函數cleanup_module()。并且用函數init_module()來啟動模塊裝載期間的操作,用函數cleanup_module()來停止這些操作。 由于模塊可以實現相當復雜的功能,故可以在模塊中加入很多新函數以實現所要期望的功
5、能。不過加入模塊的每個新函數都必須在該模塊裝載到內核中時進行注冊。若該模塊是靜態(tài)裝載的,則該模塊的所有函數都是在內核啟動時進行注冊的;若該模塊是動態(tài)裝載的,則這些新函數必須在裝載這個模塊時動態(tài)注冊。當然,如果該模塊被動態(tài)卸載了,則該模塊的函數都必須從系統(tǒng)中注銷。通過這種方式,當這個模塊不在系統(tǒng)中時,就不能調用該模塊的函數。其中注冊工作通常是在函數init_module()中完成的,而注銷工作通常是在函數cleanup_module()中完成的。 由上述定義的模塊應有如下的格式: #include #include / 其它header信息 int init_module( ) / 裝載時,初始
6、化模塊的編碼 / 期望該模塊所能實現的一些功能函數,如open()、release()、write()、 / read()、ioctl()等函數 void cleanup_module( ) / 卸載時,注銷模塊的編碼 。 二模塊的編譯 一旦設計并編寫好模塊,必須將其編譯成一個適合內核裝載的對象文件。由于編寫模塊是用C語言來完成的,故采用gcc編譯器來進行編譯。若需要通知編譯程序把這個模塊作為內核代碼而不是普通的用戶代碼來編譯,則就需向gcc編譯器傳遞參數“-D_ _KERNEL_ _”;若需要通知編譯程序這個文件是一個模塊而不是一個普通文件,則就需向gcc編譯器傳遞參數“-DMODULE”;
7、若需要對模塊程序進行優(yōu)化編譯、連接,則就需使用“-O2”參數;若還需要對裝載后的模塊進行調試,則就需使用“-g”參數;同時需要使用“-Wall”參數來向裝載程序傳遞all,使用“-c”開關通知編譯程序在編譯完這個模塊文件后不調用鏈接程序。 一般編譯模塊文件的命令格式如下: #gcc -O2 -g-Wall -DMODULE -D_ _KERNEL_ _ -c filename.c /filename.c為自己編寫的模塊程序源代碼文件 執(zhí)行命令后就會得到文件filename.o,該文件就是一個可裝載的目標代碼文件。 三模塊的裝載 內核模塊的裝載方式有兩種。一種是使用insmod命令手工裝載模塊;
8、另一種是請求裝載demand loading(在需要時裝載模塊),即當有必要裝載某個模塊時,若用戶安裝了核心中不存在的文件系統(tǒng)時,核心將請求內核守護進程kerneld準備裝載適當的模塊。該內核守護進程是一個帶有超級用戶權限的普通用戶進程。此實驗中我們主要采用insmod命令手工裝載模塊。 系統(tǒng)啟動時,kerneld開始執(zhí)行,并為內核打開一個IPC通道,內核通過向kerneld發(fā)送消息請求執(zhí)行各種任務。kerneld的主要功能是裝載和卸載內核模塊,kerneld自身并不執(zhí)行這些任務,它通過某些程序(如insmod)來完成。Kerneld只是內核的代理,只為內核進行調度。 insmod程序必須找到
9、請求裝載的內核模塊(該請求裝載的模塊一般被保存在/lib/modules/kernel-version中)。這些模塊與系統(tǒng)中其它程序一樣是已連接的目標文件,但不同的是它們被連接成可重定位映象(即映象沒有被連接到在特定的地址上運行,其文件格式是a.out或ELF)。亦就是說,模塊在用戶空間(使用適當的標志)進行編譯,結果產生一個可執(zhí)行格式的文件。在用insmod命令裝載一個模塊時,將會發(fā)生如下事件: (1)新模塊(通過內核函數create_module())加入到內核地址空間。 (2)insmod執(zhí)行一個特權級系統(tǒng)調用get_kernel_syms()函數以找到內核的輸出符號(一個符號表示為符號
10、名和符號值,如地址值)。 (3)create_module()為這個模塊分配內存空間,并將新模塊添加在內核模塊鏈表的尾部,然后將新模塊標記為UNINITIALIZED(模塊未初始化)。 (4)通過init_module()系統(tǒng)調用裝載模塊。(該模塊定義的符號在此時被導出,供其它可能后來裝載的模塊使用) (5)insmod為新裝載的模塊調用init_module()函數,然后將新模塊標志為RUNNING(模塊正在運行)。 在執(zhí)行完insmod命令后,就可在/proc/modules文件中看到裝載的新模塊了。(為證實其正確性,可在執(zhí)行insmod命令之前先查看/proc/modules文件,執(zhí)行之
11、后再查看比較) 四模塊的卸載 當一個模塊不需要使用時,可以使用rmmod命令卸載該模塊。由于無需連接,故它的任務比加載模塊要簡單得多。但如果請求裝載模塊在其使用計數為0時,kerneld將自動從系統(tǒng)中卸載該模塊。卸載時調用模塊的cleanup_module()釋放分配給該模塊的內核資源,并將其標志為DELETED(模塊被卸載);同時斷開內核模塊鏈表中的連接,修改它所依賴的其它模塊的引用,重新分配模塊所占的內核內存。 五模塊連接到內核的示意圖 該圖比較明顯地展示了模塊連接到內核所使用的命令和函數,以及各個函數之間的調用關系。通過該圖,可以比較清晰地看出模塊連接到內核的整個連接過程,這也有助于內核
12、模塊的編寫。 cleanup_module() unregister_capability() rmmod 模塊內核核心init_module() register_capability() insmod capabilities 一個函數 其它函數 數據 函數調用 函數指針 數據指針 給數據賦值 printk( ) 六模塊程序中管理模塊的幾個文件操作 在內核內部用一個file結構來識別模塊,而且內核使用file_operatuions結構來訪問模塊程序中的函數。file_operatuions結構是一個定義在中的函數指針表。管理模塊的文件操作,通常也稱為“方法”,它們都為struct fil
13、e_operations提供函數指針。在struct file_operations中的操作一般按如下順序出現,除非說明,它們返回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)()、
14、int (*revalidate)() 下面我們只簡單介紹其中的幾個操作,其它在以后涉及時再介紹: 1、方法int (*read)(struct inode *,struct file *,char *, int) 該方法用來從模塊中讀取數據。當其為NULL指針時將引起read系統(tǒng)調用返回-EINVAL(“非法參數”)。函數返回一個非負值表示成功地讀取了多少字節(jié)。 2、方法int (*write)(struct inode *,struct file *,const char *, int) 該方法用來向模塊發(fā)送數據。當其為NULL指針時將引起write系統(tǒng)調用返回-EINVAL。如果函數返回
15、一個非負值,則表示成功地寫入了多少字節(jié)。 3、方法int (*open)(struct inode *,struct file *) 該方法是用來打開模塊的操作,它是操作在模塊節(jié)點上的第一個操作,即使這樣,該方法還是可以為NULL指針。如果為NULL指針,則表示該模塊的打開操作永遠成功,但系統(tǒng)不會通知你的模塊程序。 4、方法void (*release)(struct inode *,struct file *) 該方法是用來關閉模塊的操作。當節(jié)點被關閉時就調用這個操作。與open類似,release也可以為NULL指針。 當在你的模塊中需要上面這些方法時,相應的方法若沒有,則在struct
16、file_operations中相應的地方將其令為NULL指針。這樣我們需要的大概象下面這樣: struct file_operationsmodulename_fops = NULL, / modulename_lseek modulename_read, modulename_write, NULL, / modulename_readdir NULL, / modulename_select NULL, / modulename_ioctl NULL, / modulename_mmap modulename_open, modulename_release, NULL, / modul
17、ename_fsync NULL, / modulename_fasync NULL, / modulename_check_media_change NULL / modulename_revalidate 【實驗內容】 1、編寫一個簡單的內核模塊,該模塊至少需要有兩個函數:一個是init_module()函數,在把模塊裝載到內核時被調用,它為內核的某些東西注冊一個處理程序,或是用自身的代碼取代某個內核函數;另一個是cleanup_module()函數,在卸載模塊時被調用,其任務是清除init_module()函數所做的一切操作。編寫完成后進行該模塊的編譯、裝載和卸載操作。 2、向上面模塊中
18、再添加一些新函數,如open()、release()、write()和read()函數,并編寫一個函數來測試你的模塊能否實現自己添加的函數的功能。其中open()、release()和write()函數都可以是空操作或較少的操作,它們僅僅為結構file_operations提供函數指針。 【實驗指導】 1一個簡單的內核模塊 1.1必要的header文件: 除了前面講到的頭文件#include 和#include 外,如果你的內核打開了版本檢查,那么我們就還必須增加頭文件#include ,否則就會出錯。 1.2 init_module()函數: 由于題目的要求不高,故可只在該函數里完成一個打印
19、功能,如printk(“Hello! This is a testing module!n”);等。為便于檢查模塊是否裝載成功,我們可以給一個返回值,如return 0;若返回一個非0值,則表示init_module()失敗,從而不能裝載模塊。 1.3 cleanup_module()函數: 只需用一條打印語句來取消init_module()函數所做的打印功能操作就可以了,如printk(“Sorry! The testing module is unloaded now!n”);等。 1.4 模塊的編寫: 此處把該模塊文件取名為testmodule.c #include / 在內核模塊中共享 #include / 一個模塊 /處理CONFIG_MODVERSIONS #if CONFIG_MODVERSIONS = 1 #define MODVERSIONS #include #endif int init_module() / 初始化模塊 printk(“Hello! This is a testi
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 《天子傳奇win98版》劇情攻略
- 項目團支部介紹課件
- 韶關學院工程力學課件
- 2025年輕水堆核電站及配套產品項目合作計劃書
- xx河流排水防澇設施建設項目規(guī)劃設計方案(模板范文)
- 細胞生物學測試試題庫含答案
- 2025年增味劑項目發(fā)展計劃
- 現代商場超市連鎖店星級服務培訓 第三章 商品管理技能培訓
- 衛(wèi)星互聯網行業(yè)市場分析1
- 衛(wèi)生部突發(fā)中毒事件衛(wèi)生應急預案
- 新聞發(fā)布會培訓
- 主管護師競聘匯報
- 公園突發(fā)事件應急管理預案
- 第4單元-單元素養(yǎng)測評(B)-2024-2025學年語文部編版四年級上冊(含答案解析)
- 2025年內蒙古農村信用社招聘筆試參考題庫含答案解析
- 2025年云南省文山州教育局直屬事業(yè)單位招聘27人歷年管理單位筆試遴選500模擬題附帶答案詳解
- GA/T 2150-2024出入境證件智能簽注設備
- 初中數學課后作業(yè)設計問題及優(yōu)化探討
- 檔案庫房安全
- 醫(yī)療機構感染預防與控制基本制度解讀
- 星級綠色建筑評價評分表
評論
0/150
提交評論