从 2.4 到 2.6:Linux 内核可装载模块机制的改变对设备驱动的影响(一)
生活随笔
收集整理的這篇文章主要介紹了
从 2.4 到 2.6:Linux 内核可装载模块机制的改变对设备驱动的影响(一)
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
從 2.4 到 2.6:Linux 內(nèi)核可裝載模
? 塊機(jī)制的改變對(duì)設(shè)備驅(qū)動(dòng)的影響
?? <?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
(此文章非常精彩,強(qiáng)烈推薦)?
從 2.4 到 2.6,Linux 內(nèi)核在可裝載模塊機(jī)制、設(shè)備模型、一些核心 API 等方面發(fā)生較大改變,設(shè)備驅(qū)動(dòng)開發(fā)人員面臨著將驅(qū)動(dòng)從 2.4 移植到 2.6 內(nèi)核,或是使驅(qū)動(dòng)同時(shí)支持2.4 與 2.6 內(nèi)核的任務(wù)。站在設(shè)備驅(qū)動(dòng)開發(fā)人員的角度,驅(qū)動(dòng)由一個(gè)或幾個(gè)外部可加載內(nèi)核模塊組成,本文針對(duì) 2.6 內(nèi)核里模塊機(jī)制的改變對(duì)編寫設(shè)備驅(qū)動(dòng)程序的影響,從內(nèi)核模塊的編譯、裝載時(shí)的版本檢查、初始化與退出、模塊使用計(jì)數(shù)、輸出內(nèi)核符號(hào)、命令行輸入?yún)?shù)、許可證聲明等方面比較了 2.4 與 2.6 內(nèi)核的區(qū)別;并總結(jié)了使設(shè)備驅(qū)動(dòng)同時(shí)支持 2.4 與 2.6 內(nèi)核的一系列模板。 1. 獲取內(nèi)核版本 當(dāng)設(shè)備驅(qū)動(dòng)需要同時(shí)支持不同版本內(nèi)核時(shí),在編譯階段,內(nèi)核模塊需要知道當(dāng)前使用的內(nèi)核源碼的版本,從而使用相應(yīng)的內(nèi)核 API。2.4 與 2.6 內(nèi)核下,源碼頭文件 linux/version.h 定義有: LINUX_VERSION_CODE ― 內(nèi)核版本的二進(jìn)制表示,主、從、修訂版本號(hào)各對(duì)應(yīng)一個(gè)字節(jié); KERNEL_VERSION(major, minor, release) - 由主、從、修訂版本號(hào)構(gòu)造二進(jìn)制版本號(hào)。 在同時(shí)支持2.4與2.6 內(nèi)核的設(shè)備驅(qū)動(dòng)程序中,經(jīng)常可以看到以下代碼段:清單1:判斷內(nèi)核版本的代碼段。
| #include <linux/version.h> #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) #define LINUX26 #endif #ifdef LINUX26 /*code in 2.6 kernel*/ #else /*code in 2.4 kernel */ #endif |
?
|
清單2:2.4 內(nèi)核模塊的Makefile模板
| ??????? #Makefile2.4 KVER=$(shell uname -r) KDIR=/lib/modules/$(KVER)/build OBJS=mymodule.o CFLAGS=-D__KERNEL__ -I$(KDIR)/include -DMODULE -D__KERNEL_SYSCALLS__ -DEXPORT_SYMTAB ? -O2 -fomit-frame-pointer? -Wall? -DMODVERSIONS -include $(KDIR)/include/linux/modversions.h all: $(OBJS) mymodule.o: file1.o file2.o ??????? ld -r -o $@ $^ clean: ??????? rm -f *.o ??????? |
?
在2.4 內(nèi)核下,內(nèi)核模塊的Makefile與普通用戶程序的Makefile在結(jié)構(gòu)和語法上都相同,但是必須在CFLAGS中定義-D__KERNEL__-DMODULE,指定內(nèi)核頭文件目錄-I$(KDIR)/include。 有一點(diǎn)需注意,之所以在CFLAGS中定義變量,而不是在模塊源碼文件中定義,一方面這些預(yù)定義變量可以被模塊中所有源碼文件可見,另一方面等價(jià)于將這些預(yù)定義變量定義在源碼文件的起始位置。在模塊編譯中,對(duì)于這些全局的預(yù)定義變量,一般在CFLAGS中定義。清單3:2.6 內(nèi)核模塊的Makefile模板
| # 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 |
?
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)模塊名。mymodule-objs := file1.o file2.o表示mymoudule.o 由file1.o與file2.o 連接生成。obj-m := mymodule.o表示編譯連接后將生成mymodule.o模塊。 補(bǔ)充一點(diǎn),"$(MAKE) -C $(KDIR) M=$(PWD)"與"$(MAKE) -C $(KDIR) SUBDIRS =$(PWD)"的作用是等效的,后者是較老的使用方法。推薦使用M而不是SUBDIRS,前者更明確。 通過以上比較可以看到,從Makefile編寫來看,在2.6內(nèi)核下,內(nèi)核模塊編譯不必定義復(fù)雜的CFLAGS,而且模塊中各文件依賴關(guān)系的表示簡潔清晰。清單4: 可同時(shí)在2.4 與 2.6 內(nèi)核下工作的Makefile
| #Makefile for 2.4 & 2.6 VERS26=$(findstring 2.6,$(shell uname -r)) MAKEDIR?=$(shell pwd) ifeq ($(VERS26),2.6) include $(MAKEDIR)/Makefile2.6 else include $(MAKEDIR)/Makefile2.4 endif ? |
?
2.2模塊裝載時(shí)的版本檢查 Linux內(nèi)核一直在更新、完善,在a版本內(nèi)核源碼下編譯的模塊在b版本內(nèi)核下通常不能運(yùn)行,所以必須有一種機(jī)制,限制在a版本內(nèi)核下編譯生成的模塊在b版本內(nèi)核下被加載。 2.4與2.6內(nèi)核在可裝載內(nèi)核模塊的版本檢查機(jī)制方面發(fā)生了根本性的改變,不過這些改變對(duì)設(shè)備驅(qū)動(dòng)開發(fā)人員而言基本是透明的。為了使模塊裝載時(shí)的版本檢查機(jī)制生效,2.4 內(nèi)核下,只需在CFLAGS中定義?
| -DMODVERSIONS -include $(KDIR)/include/linux/modversions.h; |
?
2.6內(nèi)核下,開發(fā)人員無須采用任何操作。 不過,在此仍有必要闡明2.4與2.6內(nèi)核對(duì)可加載模塊的版本檢查機(jī)制。 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ò)誤。?
| #insmod hello.ko Invalid module format hello: version magic '2.6.11-1.1369_FC4 686 REGPARM 4KSTACKS gcc-4.0' should be '2.6.5-1.358 686 REGPARM 4KSTACKS gcc-3.3' |
?
2.3模塊的初始化與退出 在2.6內(nèi)核中,內(nèi)核模塊必須調(diào)用宏module_init 與module_exit() 去注冊(cè)初始化與退出函數(shù)。在2.4 內(nèi)核中,如果初始化函數(shù)命名為init_module()、退出函數(shù)命名為cleanup_module(),可以不必使用module_init 與module_exit 宏。推薦使用module_init 與module_exit宏,使代碼在2.4與2.6內(nèi)核中都能工作。清單5:適用于2.4與2.6內(nèi)核的模塊的初始化與退出模板
| #include <linux/module.h>? /* Needed by all modules */ #include <linux/init.h>??? /* Needed for init&exit macros */ static int mod_init_func(void) { /*code here*/ return 0; } static void mod_exit_func(void) { /*code here*/ } module_init(mod_init_func); module_exit(mod_exit_func); |
?
需要注意的是初始化與退出函數(shù)必須在宏module_init和module_exit使用前定義,否則會(huì)出現(xiàn)編譯錯(cuò)誤。?
?轉(zhuǎn)載于:https://blog.51cto.com/zyg0227/270373
總結(jié)
以上是生活随笔為你收集整理的从 2.4 到 2.6:Linux 内核可装载模块机制的改变对设备驱动的影响(一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: js 运算符 || 妙用
- 下一篇: 2010版CCNP教材一览【图文】