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