做內(nèi)核驅(qū)動(dòng)第一步都是學(xué)習(xí)如何添加模塊,這是基礎(chǔ),有了這個(gè)基礎(chǔ),剩下就是寫代碼了。
由于2.4到2.6內(nèi)核版本的更新,無論是系統(tǒng)調(diào)用還是模塊添加機(jī)制都有了巨大的變化,本人也因此飽經(jīng)挫折,最后在3.0.101版本的內(nèi)核下成功。作為開源運(yùn)動(dòng)的支持者,自認(rèn)為有必要把自己的經(jīng)歷分享出來,以供后來學(xué)習(xí)者分享與交流。
再次聲明,本博客只分享我遇到了的問題,沒有交流的不代表不難或者不會(huì)遇到問題,只是我沒遇到,如果有閱讀本博客的朋友遇到了問題,非常歡迎大家一起討論,技術(shù)就是這么成長(zhǎng)的!
關(guān)于添加模塊,步驟上還是那三步。
1.編寫模塊函數(shù)
編寫內(nèi)核模塊時(shí)必須要有的兩個(gè)函數(shù) :
1> 加載 函數(shù):
static int func1_init(void)
2> 卸載函數(shù) 無返回值
static void func2_exit(void)
這里值得注意的是三點(diǎn),一是卸載函數(shù)必須是void,即無返回型,否則會(huì)報(bào)錯(cuò)。其二是推薦把加載函數(shù)改寫成static int __init func1_init(void),卸載函數(shù)同理。這里使用__init是利用2.6內(nèi)核以后的宏機(jī)制,具體機(jī)制。。。還在學(xué)習(xí)中。第三點(diǎn)就是人盡皆知的,加載函數(shù)的名字必須寫成*_init的樣子,卸載函數(shù)必須是*_exit的樣子。
2.其次就是Makefile了。
這方面的文章比較多,網(wǎng)上也講得非常詳細(xì),我就大概講一下模板和每步的意義。
obj-m := hello.o
kernel_path=/usr/src/linux-headers-$(shell uname -r)
all:
make -C $(kernel_path) M=$(PWD) modules
clean:
make -C $(kernel_path) M=$(PWD) clean
obj -m:= hello.o // 產(chǎn)生 hello 模塊的目標(biāo)
kernel_path // 定義內(nèi)核源文件目錄
all :
make -C $(kernel_path) M=$(PWD) modules
// 生成內(nèi)核模塊參數(shù)為內(nèi)核源代碼目錄以及模塊所在目錄
clean:
make -C $(kernel_path) M=$(PWD) clean
// 清除生成的模塊文件以及中間文件
這里就要尤為注意了,在2.6版本以后,引入了如下機(jī)制。2.6中模塊的編譯需要配置過的內(nèi)核源碼;編譯、連接后生成的內(nèi)核模塊后綴為.ko;編譯過程首先會(huì)到內(nèi)核源碼目錄下,讀取頂層的Makefile文件,然后再返回模塊源碼所在目錄。
接下來要講得就是最折磨人的改變了。
2.4內(nèi)核下, 執(zhí)行`cat /proc/ksyms`可看到內(nèi)核符號(hào)在名字后還跟隨著一串校驗(yàn)字符串,此校驗(yàn)字符串與內(nèi)核版本有關(guān)。在內(nèi)核源碼頭文件linux/modules 目錄下存在許多*.ver文件,這些文件起著為內(nèi)核符號(hào)添加校驗(yàn)后綴的作用,如ksyms.ver 文件里有一行 #define printk _set_ver(printk)。linux/modversions.h 文件會(huì)包含全部的 ver文件 。所以當(dāng)模塊包含linux/modversions.h文件后,編譯時(shí),模塊里使用的內(nèi)核符號(hào)實(shí)質(zhì)是帶有校驗(yàn)后綴的內(nèi)核符號(hào)。在加載模塊時(shí),如果模塊中所使用內(nèi)核符號(hào)的校驗(yàn)字符串與當(dāng)前運(yùn)行內(nèi)核所導(dǎo)出的相應(yīng)的內(nèi)核符號(hào)的校驗(yàn)字符串不一致,即當(dāng)前內(nèi)核空間并不存在模塊所使用的內(nèi)核符號(hào),就會(huì)出現(xiàn)"Invalid module format "的錯(cuò)誤。
為內(nèi)核符號(hào)添加校驗(yàn)字符串來驗(yàn)證模塊的版本與內(nèi)核的版本是否匹配是繁雜和浪費(fèi)內(nèi)核空間的;而且隨著SMP(對(duì)稱多處理器)、PREEMPT(可搶占內(nèi)核)等機(jī)制在2.6內(nèi)核的引入和完善,模塊運(yùn)行時(shí)對(duì)內(nèi)核的依賴不僅取決于內(nèi)核版本,還取決于內(nèi)核的配置,此時(shí)內(nèi)核符號(hào)的校驗(yàn)碼是否一致不能成為判斷模塊可否被加載的充分條件。2.6 內(nèi)核下,在linux/vermagic.h中定義有VERMAGIC_STRING,VERMAGIC_STRING不僅包含內(nèi)核版本號(hào),還包含有內(nèi)核使用的gcc版本,SMP與PREEMPT等配置信息。模塊在編譯時(shí),我們可以看到屏幕上會(huì)顯示"MODPOST"。在此階段, VERMAGIC_STRING會(huì)添加到模塊的modinfo段。 在內(nèi)核源碼目錄下scripts/mod/modpost.c文件中可以看到模塊后續(xù)處理部分的代碼。模塊編譯生成后,通過`modinfo mymodule.ko`命令可以查看此模塊的vermagic等信息。2.6 內(nèi)核下的模塊裝載器里保存有內(nèi)核的版本信息,在裝載模塊時(shí),裝載器會(huì)比較所保存的內(nèi)核vermagic與此模塊的modinfo段里保存的vermagic信息是否一致,兩者一致時(shí),模塊才能被裝載。譬如Fedora core 4 與core 2 使用的都是2.6 版本內(nèi)核, 在Fedore Core 2下去加載Fedora Core4下編譯生成的hello.ko,會(huì)出現(xiàn)"invalid module format" 錯(cuò)誤。
因此,細(xì)心的朋友注意到我給出的Makefile模板中編譯目標(biāo)是內(nèi)核源碼樹。這里必須是這樣,否則會(huì)出錯(cuò)。而且必須對(duì)應(yīng),即,如果你當(dāng)前內(nèi)核版本是3.0.101,就必須是以3.0.101的內(nèi)核源碼樹為目標(biāo)進(jìn)行編譯得到*.ko,否則會(huì)報(bào)錯(cuò)。反之亦然。兩者必須一一對(duì)應(yīng),這一點(diǎn)希望朋友們務(wù)必注意,否則會(huì)痛不欲生。
以上就是我學(xué)習(xí)這部分最痛苦最值得分享的部分了,希望可以幫到大家。
由于2.6下各位朋友肯定希望自己的模塊能兼容2.4下的模式,所以,我這里再提供一套Makefile,有IBM官方給出,值得學(xué)習(xí)。
# Makefile2.6 ifneq ($(KERNELRELEASE),) #kbuild syntax. dependency relationshsip of files and target modules are listed here. mymodule-objs := file1.o file2.o obj-m := mymodule.o else PWD := $(shell pwd) KVER ?= $(shell uname -r) KDIR := /lib/modules/$(KVER)/build all: $(MAKE) -C $(KDIR) M=$(PWD) clean: rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions
endif