1、背景 壓縮卡驅(qū)動(dòng)提供給文件系統(tǒng)KAPI,供文件系統(tǒng)對(duì)文件數(shù)據(jù)進(jìn)行壓縮和解壓。在測試中,最初采用的方法是通過文件系統(tǒng)提供的系統(tǒng)調(diào)用,利用文件系統(tǒng)在處理系統(tǒng)調(diào)用時(shí),會(huì)調(diào)用到驅(qū)動(dòng)的KAPI,來完成對(duì)壓縮卡KAPI及其更下層(包含硬件)正確性的測試??紤]到這種方法,可能會(huì)由于文件系統(tǒng)對(duì)KAPI的具體使用方式而屏蔽一些問題的發(fā)現(xiàn),因此展開了對(duì)KAPI的直接測試。由于KAPI是內(nèi)核態(tài)的接口,無法在用戶態(tài)直接調(diào)用,因此要最終完成對(duì)KAPI的更直接測試,需要借助編寫內(nèi)核模塊(Kernel Module),來實(shí)現(xiàn)用戶進(jìn)程對(duì)KAPI的訪問;此外,還要解決用戶態(tài)和內(nèi)核態(tài)兩者的交互。下圖中表示了引入基于內(nèi)核模塊的測試方法,可以使我們的測試程序在調(diào)用關(guān)系上更加接近被測模塊,從而在測試效率和覆蓋性上得到改進(jìn)。
在本文中,將主要介紹實(shí)現(xiàn)基于內(nèi)核模塊的測試的相關(guān)知識(shí)與代碼編寫方式。
2、用戶態(tài)和內(nèi)核態(tài) 操作系統(tǒng)中,虛擬內(nèi)存被分為內(nèi)核空間和用戶空間。內(nèi)核空間被用于內(nèi)核,內(nèi)核擴(kuò)展功能和一些設(shè)備驅(qū)動(dòng)的運(yùn)行;用戶空間是用戶進(jìn)程在用戶態(tài)下運(yùn)行時(shí)所使用。在 linux系統(tǒng)中,內(nèi)核擁有地址為3G到4G的內(nèi)存空間,并且它是被共享的,而用戶進(jìn)程擁有0到3G的用戶空間,每個(gè)用戶進(jìn)程擁有獨(dú)立的空間。
CPU根據(jù)PSW寄存器中的模式bit,可以工作在用戶態(tài)(Ring 3)或內(nèi)核態(tài)(Ring 0),用戶態(tài)能執(zhí)行有限的非特權(quán)的CPU指令。在內(nèi)核態(tài)可以執(zhí)行所有指令。工作在用戶態(tài)的進(jìn)程無法直接訪問硬件和內(nèi)核的內(nèi)存空間,這是出于運(yùn)行安全的考慮。當(dāng)然,操作系統(tǒng)也會(huì)也提供系統(tǒng)調(diào)用或中斷等方法,讓用戶進(jìn)程可以切換到內(nèi)核態(tài),以訪問內(nèi)核空間或硬件。系統(tǒng)調(diào)用或中斷,可以看作是有限度的開放給用戶進(jìn)程對(duì)內(nèi)核和硬件的訪問,從而保證了系統(tǒng)安全。
在驅(qū)動(dòng)項(xiàng)目中,驅(qū)動(dòng)程序工作在內(nèi)核空間中,它直接提供KAPI給內(nèi)核,這樣我們就無法通過用戶進(jìn)程完成對(duì)該KAPI的測試。當(dāng)然,通過執(zhí)行內(nèi)核提供的系統(tǒng)調(diào)用,當(dāng)發(fā)生磁盤讀寫時(shí)可以間接的調(diào)用到該KAPI,但是內(nèi)核對(duì)其有限或具體的調(diào)用方式往往屏蔽了底層驅(qū)動(dòng)的部分缺陷,這種測試方法也帶來發(fā)現(xiàn)問題后追查定位的困難。要改變這些不足,最直接的思路就是跨越對(duì)內(nèi)核特殊邏輯間接調(diào)用的依賴,通過直接對(duì)KAPI進(jìn)行調(diào)用的方法完成測試。
為了實(shí)現(xiàn)對(duì)工作在內(nèi)核態(tài)的KAPI的訪問,必須向內(nèi)核置入內(nèi)核態(tài)的代碼以發(fā)起調(diào)用,這將通過kernel module來完成,會(huì)在第三部分具體介紹。而為了實(shí)現(xiàn)對(duì)測試數(shù)據(jù)輸入,執(zhí)行發(fā)起,結(jié)果輸出采集等的靈活控制,還必須實(shí)現(xiàn)用戶態(tài)和內(nèi)核態(tài)的交互,具體方法將在第四部分介紹。
3. Kernel Module 要達(dá)成對(duì)KAPI的直接調(diào)用和測試,需要添加工作在內(nèi)核態(tài)的代碼實(shí)現(xiàn)對(duì)該函數(shù)的訪問,這里我們借助kernel module來完成。kernel module提供了不需要重新編譯kernel image,就向內(nèi)核引入新代碼和邏輯的支持。Kernel Module通常是以ko為擴(kuò)展名的文件,通過lsmod命令可以查看已經(jīng)加載的Kernel Module,通過insmod/rmmod命令完成指定的的模塊的加載和卸載。Kernel Module最基本的編程模式是編程者通過宏定義module_init和module_exit,指定在模塊加載和卸載時(shí)進(jìn)行的初始化或清理等工作。一個(gè)基本的hello world程序如下。向init中就可以加入對(duì)KAPI進(jìn)行調(diào)用的函數(shù),當(dāng)模塊被insmod時(shí)就會(huì)在內(nèi)核態(tài)被執(zhí)行。但是這與靈活的完成各種測試任務(wù)還有差距,為了能在用戶態(tài)完成測試數(shù)據(jù)的準(zhǔn)備和輸入,測試結(jié)果的獲取和對(duì)執(zhí)行的控制,還需要引入用戶態(tài)和內(nèi)核態(tài)的交互。關(guān)于Kernel Module更詳細(xì)的編譯方式和編程模式介紹,可以參考
http://www.tldp.org/LDP/lkmpg/2.6/html/#include <linux/module.h> /* Needed by all modules */
#include <linux/kernel.h> /* Needed for KERN_INFO */
#include <linux/init.h> /* Needed for the macros */
static int __init hello_init(void){
printk(KERN_INFO "Hello, world 2\n");
return 0;
}
static void __exit hello_exit(void){
printk(KERN_INFO "Goodbye, world 2\n");}
module_init(hello_init);
module_exit(hello_exit);