构造和运行模块
作者:蔡倫輝
寫在前面
作者一直支持GPL的精神。允許任何人自由使用、轉(zhuǎn)載、復(fù)制和再分發(fā),但必須保留作者署名,必須保證全文完整轉(zhuǎn)載,包括完整的版權(quán)聲明。
由于作者水平有限,因此不能保證文章內(nèi)容準(zhǔn)確無誤,請批判閱讀。如果你發(fā)現(xiàn)任何錯(cuò)誤或?qū)ξ恼聝?nèi)容有任何建議,歡迎你與我聯(lián)系:
Email: caiallen@tom.com??QQ群: 14765968
設(shè)置測試系統(tǒng)
在該小節(jié)中,有以下一段話:
“不管內(nèi)核來自哪里,想要為2.6x內(nèi)核構(gòu)造模塊,還必須在自己的系統(tǒng)中配置并構(gòu)造好內(nèi)核樹。這一要求和先前版本的內(nèi)核不同,先前的內(nèi)核只要有一套內(nèi)核頭文件就夠了。但因?yàn)?.6內(nèi)核的模塊要和內(nèi)核源代碼樹中的文件連接,通過這種方式,可得到一個(gè)更加健壯的模塊裝載器,但也需要這些目標(biāo)文件存在于內(nèi)核目錄樹中。”
這段話說出了2.4和2.6兩種版本的驅(qū)動(dòng)模塊的編寫的一個(gè)不同之處。問題來自,我用的操作系統(tǒng)是Fedora Core 5。FC5在安裝時(shí)是不安裝源代碼樹在PC上的。所以我必須在我的FC5上建立內(nèi)核源代碼樹。最好在構(gòu)造內(nèi)核模塊時(shí)運(yùn)行的恰好是目標(biāo)內(nèi)核。書上的例子是在版本2.6.10中構(gòu)造的,用命令uname -r查看,FC5的版本信息為:2.6.15-1.2054_FC5。所以我要建立的內(nèi)核源代碼樹的版本為2.6.15。下面詳細(xì)介紹其建立過程。
1。??下載內(nèi)核rpm包
rpm包名稱:kernel-2.6.15-1.2054_FC5.src.rpm
下載地址:http://download.fedora.redhat.com/pub/fedora/linux/core/5/source/SRPMS/
kernel-2.6.15-1.2054_FC5.src.rpm
2。? ? 安裝rpm包
以root身份登陸,以下步驟都以root身份執(zhí)行。進(jìn)入保存rpm包的目錄下,運(yùn)行命令:
#rpm -Uvh kernel-2.6.15-1.2054_FC5.src.rpm
? ?? ?? ?? ?該命令將rpm的內(nèi)容寫到路徑/usr/src/redhat/SOURSE和/usr/src/redhat/SPECS下。
3。? ?build源碼包
? ?? ?? ?? ?#cd /usr/src/redhat/SPECS
#rpmbuild -bp --target i686 kernel-2.6.spec
該命令將會(huì)把內(nèi)核源碼樹放到目錄
/usr/src/redhat/BUILD/kernel-2.6.15/kernel-2.6.15.686
4。? ? 配置內(nèi)核
Fedora Core 5附帶的內(nèi)核配置文件在內(nèi)核源碼樹的configs/目錄下。
例如,i686 SMP 配置文件被命名為
configs/kernel-version-i686-smp.config。
但我的PC機(jī)為i686,單CPU,所以不是SMP,應(yīng)該選的內(nèi)核配置文件是:kernel-2.6.15-i686.config
注意:如果你的PC是單CPU的,而選 configs/kernel-version-i686-smp.config進(jìn)行內(nèi)核配置,則在建立代碼樹后運(yùn)行后面的insmod hello.ko會(huì)失敗,失敗原因我在文件/var/log/messages中找到如下:Nov 23 04:55:02 localhost kernel: hello: version magic '2.6.15-1.2054_FC5 SMP 686 REGPARM 4KSTACKS gcc-4.1' should be '2.6.15-1.2054_FC5 686 REGPARM 4KSTACKS gcc-4.1'
一對比:
'2.6.15-1.2054_FC5 SMP 686 REGPARM 4KSTACKS gcc-4.1'
'2.6.15-1.2054_FC5 686 REGPARM 4KSTACKS gcc-4.1‘
看出區(qū)別了吧,原因是我選的配置文件不對。我一開始就犯了這個(gè)錯(cuò)誤錯(cuò)誤,結(jié)果不得不又從頭開始進(jìn)行漫長的編譯。
使用下列命令來將需要的配置文件復(fù)制到合適的位置,用來編譯:
#cd /usr/src/redhat/BUILD/kernel-2.6.15/linux-2.6.15.i686?
#cp configs/kernel-version-i686.config .config你也可以在 /lib/modules/version/build/.config 這個(gè)位置找到與您當(dāng)前的內(nèi)核匹配的 .config 文件。因?yàn)閎uild是個(gè)連接,其連接目標(biāo)就是/usr/src/redhat/BUILD/kernel-2.6.15/linux-2.6.15.i686
用以下命令調(diào)出內(nèi)核配置菜單。
#make menuconfig
配置如下:
Loadable module support --->
- Enable loadable module support
- Module unloading
[ ] Module versioning support (EXPERIMENTAL) - Automatic kernel module loading? ?? ??
5。? ? 修改Makefile
每個(gè)內(nèi)核的名字都包含了它的版本號(hào),這也是 uname -r 命令顯示的值。內(nèi)核Makefile 的前四行定義了內(nèi)核的名字。為了保護(hù)官方的內(nèi)核不被破壞,Makefile經(jīng)過了修改,以生成一個(gè)與運(yùn)行中的內(nèi)核不同的名字。在一個(gè)模塊插入運(yùn)行中的內(nèi)核前,這個(gè)模塊必須針對運(yùn)行中的內(nèi)核進(jìn)行編譯。為此,你必須編輯內(nèi)核的Makefile。
例如,如果 uname -r 返回字符串 2.6.15-1.2054_FC5,就將 EXTRAVERSION 定義從:
EXTRAVERSION = -prep
修改為:
EXTRAVERSION = -1.2054_FC5
也就是最后一個(gè)連字符后面的所有內(nèi)容。
6。? ? 編譯內(nèi)核
在目錄/usr/src/redhat/BUILD/kernel-2.6.15/linux-2.6.15.i686下即Makefile所在的目錄使用下面命令編譯內(nèi)核:
#make bzImage? ?? ?? ???編譯內(nèi)核?
#make modules? ?? ?? ???編譯模塊?
#make modules_install? ?安裝模塊這一步可是一個(gè)漫長的過程啊,花去我差不多一個(gè)小時(shí)
。
7。? ? 完成“內(nèi)核樹”的安裝
以上這一步如果沒什么錯(cuò)誤,到此就完成了內(nèi)核代碼樹的建立。
目錄“/usr/src/redhat/BUILD/kernel-2.6.15/kernel-2.6.15.686/”中就是所謂的“內(nèi)核代碼樹”,同時(shí)“/lib/modules/2.6.15-1.2054_FC5/build”是個(gè)符號(hào)鏈接,也指向這個(gè)目錄,所以這里也可以叫做“內(nèi)核代碼樹”。
以上的建立步驟是在參考《在Linux 2.6內(nèi)核下編譯可以加載的內(nèi)核模塊》(原文地址http://blog.csdn.net/wooin/archive/2007/05/21/1619141.aspx)同時(shí)結(jié)合自己的實(shí)踐而得出,增加一些說明并修正了其中的一些錯(cuò)誤。
完成以上的步驟,便為我們后面的實(shí)踐打下了基礎(chǔ)。
Hello World模塊
要想驗(yàn)證我們的內(nèi)核代碼樹是否成功建立,可以寫個(gè)內(nèi)核模塊測試一下,就從Hello world程序開始。以下是名為hello.c的文件的代碼:
? ? ? ? ? ? ? ??
? ? ? ? ? ? ? ? #include linux/init.h>
#include linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");//(1)
static int hello_init(void)//(2)?
{
? ?printk(KERN_ALERT "Hello, World!\n");//(3)
? ?return 0;
}
static void hello_exit(void)?
{
? ?printk(KERN_ALERT "Goodbye, cruel World!\n");
}
module_init(hello_init);//(4)
module_exit(hello_exit);//(5)
代碼說明:
? ? (1):
MODULE_LICENSE是一個(gè)特殊的宏,用來告訴內(nèi)核,該模塊采用自由許可證;可以不要這樣的聲明,但不要的話,運(yùn)行ismod hello.ko時(shí)會(huì)出現(xiàn)"hello: module
license 'unspecified' taints
kernel.",詞典上對taints的解釋是"感染,污點(diǎn)",即內(nèi)核在裝載該模塊時(shí)會(huì)產(chǎn)生抱怨
,內(nèi)核有情緒?!還要注意的一點(diǎn)是,這句不要寫到最后,放到(5)后面,還是會(huì)出現(xiàn)內(nèi)核污染的提示。
? ? (2):對模塊內(nèi)的函數(shù)一般需要加上static關(guān)鍵字進(jìn)行修飾,這樣可防止模塊外訪問該函數(shù),這是應(yīng)該養(yǎng)成的一個(gè)好習(xí)慣。
? ? (3):printk函數(shù)相當(dāng)于C標(biāo)準(zhǔn)庫函數(shù)printf, KERN_ALERT是指的輸出消息的優(yōu)先級(jí)別。
? ? (4)(5):模塊初始化和模塊退出時(shí)有專門的函數(shù),這個(gè)函數(shù)在2.4和2.6兩種版本的內(nèi)核有所區(qū)別。
然后編寫該程序的Makefile:
# Makefile for hello.c
obj-m:=hello.o
KDIR:=/lib/modules/2.6.15-1.2054_FC5/build
PWD:=$(shell pwd)
default:
? ? $(MAKE) -C $(KDIR) M=$(PWD) modules
.PHONY:clean
clean:
? ? rm -f *.o *~ *.ko
該Makefile為何這樣寫,可以參考相關(guān)資料,如我轉(zhuǎn)載的《2.6驅(qū)動(dòng)移植系列之Getting started(2)--模塊編譯》和在內(nèi)核代碼的/Documentation/kbuild目錄下的makefile.txt這篇文檔。簡單解析下,$(MAKE) -C $(KDIR) M=$(PWD) modules命令首先改變目錄到-C選項(xiàng)指定的位置(即內(nèi)核源代碼目錄),其中保存有內(nèi)核的頂層Makefile文件,因?yàn)閎uild腳本會(huì)首先判斷有無必要重新編譯內(nèi)核,所以如果需要先重新編譯內(nèi)核的話,編譯過程會(huì)運(yùn)行一段時(shí)間。M=選項(xiàng)讓該Makefile在構(gòu)造modules目標(biāo)之前返回到模塊源代碼目錄。然后,modules目標(biāo)指向obj-m變量中設(shè)定的模塊。
有一點(diǎn)需要注意的,Makefile文件名一定要大寫,不能寫出makefile,否則在你make時(shí)會(huì)出現(xiàn)“沒有規(guī)則可以創(chuàng)建目標(biāo)。停止”類似的提示。
執(zhí)行make命令進(jìn)行編譯就行了, 執(zhí)行完畢后,會(huì)生成幾個(gè)文件:
hello.ko
hello.mod.c
hello.mod.o
hello.o
注意:2.6內(nèi)核的模塊后綴名為.ko
運(yùn)行命令:
#insmod hello.ko掛載成功,但看不到輸出:Hello World!。
然后再運(yùn)行命令:
#rmmod hello同樣卸載成功但也看不到輸出:Goodbye, cruel world!
后來查看日志文件/var/log/messages,哈哈,原來輸出到這里來了!后來查找原因,原來在GNOME環(huán)境(即圖形界面)信息不會(huì)輸出到終端,在在控制臺(tái)下則可以看到輸出信息。按換到控制臺(tái)模式,以root身份登陸,再運(yùn)行insmod hello.ko,果然看到輸出。再按可以切換回GNOME環(huán)境。
如果你在運(yùn)行insmod hello.ko時(shí)出現(xiàn)以下提示:
insmod: error inserting 'hello.ko': -1 Invalid module format
可能的原因有:
? ? 1。內(nèi)核源代碼樹沒有建立好。
? ? 2。編譯器的版本不對。可以查看內(nèi)核文檔中的Documentation/Changes文件列出的需要的工具版本。
? ? 3。Makefile編寫不對。
核心模塊與應(yīng)用程序的對比
內(nèi)核模塊與應(yīng)用程序的不同之處有:
? ? 1。大多數(shù)小規(guī)模及中規(guī)模應(yīng)用程序是從頭到尾執(zhí)行單個(gè)任務(wù),而模塊只是預(yù)先注冊自己以便服務(wù)于將來的某個(gè)請求,然后它的初始化函數(shù)就立即結(jié)束。
? ? 2。應(yīng)用程序在退出時(shí),可以不管資源的釋放或者其他的清除工作,但模塊的退出卻必須仔細(xì)撤銷初始化函數(shù)所做的一切,否則,在系統(tǒng)重新引導(dǎo)之前某些東西就會(huì)殘留在系統(tǒng)中。
? ? 3。模塊運(yùn)行在所謂的內(nèi)核空間里,而應(yīng)用程序運(yùn)行在所謂的用戶空間中。
用戶空間和內(nèi)核空間
操作系統(tǒng)的作用是為應(yīng)用程序提供一個(gè)對計(jì)算機(jī)硬件的一致視圖,除此之外,還必須負(fù)責(zé)程序的獨(dú)立操作并保護(hù)資源不受非法訪問。
這兩種運(yùn)行模式都有自己的內(nèi)存映射,也即自己的地址空間。
每當(dāng)應(yīng)用程序執(zhí)行系統(tǒng)調(diào)用或者被硬件中斷掛起時(shí),linux將執(zhí)行模式從用戶空間切換到內(nèi)核空間。而處理硬件中斷的內(nèi)核代碼和進(jìn)程是異步的,與任何一個(gè)特定的進(jìn)程無關(guān)。
模塊化代碼在內(nèi)核空間中運(yùn)行,用于擴(kuò)展內(nèi)核功能。通常來講,一個(gè)驅(qū)動(dòng)程序要執(zhí)行兩類任務(wù):模塊中的某些函數(shù)作為系統(tǒng)調(diào)用的一部分而執(zhí)行,而其他函數(shù)則負(fù)責(zé)中斷處理。
當(dāng)前進(jìn)程
內(nèi)核代碼可以通過訪問全局項(xiàng)current來獲得當(dāng)前進(jìn)程。current指針指向當(dāng)前正在運(yùn)行的進(jìn)程。在open,read等系統(tǒng)調(diào)用的執(zhí)行過程中,當(dāng)前進(jìn)程指的是調(diào)用這些系統(tǒng)調(diào)用的進(jìn)程。
與早期linux內(nèi)核版本不同,2.6中current不再是全局變量。然而,設(shè)備驅(qū)動(dòng)程序只要包含頭文件即可以引用當(dāng)前進(jìn)程。
編譯模塊
實(shí)際上hello world的makefile的一個(gè)更容易的寫法是:
# 如果已定義KERNELRELEASE,則說明是從內(nèi)核構(gòu)造系統(tǒng)調(diào)用的。
# 因此可以利用其內(nèi)建語句
ifneq ($(KERNELRELEASE),)?
? ? obj-m := hello.o
# 否則是直接從命令行調(diào)用
# 這時(shí)要調(diào)用內(nèi)核構(gòu)造系統(tǒng)
else?
? ? KERNELDIR ?= /lib/modules/$(shell uname -r)/build?
? ? PWD := $(shell pwd)?
default:?
? ? $(MAKE) -C $(KERNELDIR) M=$(PWD) modulesendif?
KERNELRELEASE是在內(nèi)核源碼的頂層Makefile中定義的一個(gè)變量,在第一次讀取執(zhí)行此Makefile時(shí),KERNELRELEASE沒
有被定義,
所以make將讀取執(zhí)行else之后的內(nèi)容。如果make的目標(biāo)是clean,直接執(zhí)行clean操作,然后結(jié)束。當(dāng)make的目標(biāo)為all時(shí),-C
$(KDIR) 指明跳轉(zhuǎn)到內(nèi)核源碼目錄下讀取那里的Makefile;M=$(PWD)
表明然后返回到當(dāng)前目錄繼續(xù)讀入、執(zhí)行當(dāng)前的Makefile。當(dāng)從內(nèi)核源碼目錄返回時(shí),KERNELRELEASE已被被定義,kbuild也被啟動(dòng)去
解析kbuild語法的語句,make將繼續(xù)讀取else之前的內(nèi)容。else之前的內(nèi)容為kbuild語法的語句,
指明模塊源碼中各文件的依賴關(guān)系,以及要生成的目標(biāo)模塊名。
(continue...)
轉(zhuǎn)載于:https://www.cnblogs.com/dayuhope/p/3286481.html
總結(jié)
- 上一篇: netty源码解解析(4.0)-2 Ch
- 下一篇: IOS - IPhone或IPAD,如何