Linux設(shè)備驅(qū)動(dòng)程序原理及框架內(nèi)核模塊入門(mén)篇_第1頁(yè)
Linux設(shè)備驅(qū)動(dòng)程序原理及框架內(nèi)核模塊入門(mén)篇_第2頁(yè)
Linux設(shè)備驅(qū)動(dòng)程序原理及框架內(nèi)核模塊入門(mén)篇_第3頁(yè)
Linux設(shè)備驅(qū)動(dòng)程序原理及框架內(nèi)核模塊入門(mén)篇_第4頁(yè)
Linux設(shè)備驅(qū)動(dòng)程序原理及框架內(nèi)核模塊入門(mén)篇_第5頁(yè)
已閱讀5頁(yè),還剩37頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

Linux設(shè)備驅(qū)動(dòng)程序原理及框架內(nèi)核模塊入門(mén)篇課程內(nèi)容內(nèi)核模塊介紹應(yīng)用層加載模塊操作過(guò)程內(nèi)核如何支持可安裝模塊內(nèi)核提供的接口及作用模塊實(shí)例內(nèi)核模塊內(nèi)核模塊介紹Linux采用的是整體式的內(nèi)核結(jié)構(gòu),這種結(jié)構(gòu)的內(nèi)核一般不能動(dòng)態(tài)的增加新的功能。為此,Linux提供了一種全新的機(jī)制,叫(可安裝)“模塊”(module)。利用這個(gè)機(jī)制,可以根據(jù)需要,在不必對(duì)內(nèi)核重新編譯鏈接的條件下,將可安裝模塊動(dòng)態(tài)的插入運(yùn)行中的內(nèi)核,成為內(nèi)核的一個(gè)有機(jī)組成部分;或者從內(nèi)核移走已經(jīng)安裝的模塊。正是這種機(jī)制,使得內(nèi)核的內(nèi)存映像保持最小,但卻具有很大的靈活性和可擴(kuò)充性。內(nèi)核模塊內(nèi)核模塊介紹可安裝模塊是可以在系統(tǒng)運(yùn)行時(shí)動(dòng)態(tài)地安裝和卸載的內(nèi)核軟件。嚴(yán)格來(lái)說(shuō),這種軟件的作用并不限于設(shè)備驅(qū)動(dòng),例如有些文件系統(tǒng)就是以可安裝模塊的形式實(shí)現(xiàn)的。但是,另一方面,它主要用來(lái)實(shí)現(xiàn)設(shè)備驅(qū)動(dòng)程序或者與設(shè)備驅(qū)動(dòng)密切相關(guān)的部分(如文件系統(tǒng)等)。內(nèi)核模塊介紹應(yīng)用層加載模塊操作過(guò)程內(nèi)核如何支持可安裝模塊內(nèi)核提供的接口及作用模塊實(shí)例課程內(nèi)容內(nèi)核模塊應(yīng)用層加載模塊操作過(guò)程內(nèi)核引導(dǎo)的過(guò)程中,會(huì)識(shí)別出所有已經(jīng)安裝的硬件設(shè)備,并且創(chuàng)建好該系統(tǒng)中的硬件設(shè)備的列表樹(shù):/sys文件系統(tǒng)。(udev服務(wù)就是通過(guò)讀取該文件系統(tǒng)內(nèi)容來(lái)創(chuàng)建必要的設(shè)備文件的。)。根據(jù)/sys文件系統(tǒng),內(nèi)核讀取modules.alias文件(位于/lib/modules/2.6.5-1.358/目錄下,2.6.5-1為內(nèi)核版本號(hào),請(qǐng)?zhí)鎿Q為你的系統(tǒng)版本號(hào)),找到對(duì)應(yīng)的模塊,加載。我們可以看到modules.alias文件中都是類似如下的行:

aliaspci:v*d00008139sv00001186sd00001300bc*sc*i*8139too

內(nèi)核模塊應(yīng)用層加載模塊操作過(guò)程中間的若干符號(hào)包含了很多信息,例如該設(shè)備的制造商編號(hào)、設(shè)備編號(hào)等。例如:

aliaspci:v00008086d00007190sv000015ADsd00001976bc06sc00i00

表示該設(shè)備的設(shè)備編號(hào)是0x7190,制造商編號(hào)是x8086,模塊子系統(tǒng)提供商編號(hào)0x15ad等等,v即是代表vendor,sv代表subsystem-vendor,sd代表subsystem-device。

內(nèi)核模塊應(yīng)用層加載模塊操作過(guò)程通過(guò)這些信息和modules.alias某一行匹配之后,便加載后面指定的模塊名稱。加載模塊使用的是modprobe工具。我們可以發(fā)現(xiàn),modules.alias文件中給出的模塊只是個(gè)名稱,并沒(méi)有模塊文件的絕對(duì)路徑。那么modprobe工具如何定位模塊文件本身呢?它需要另一個(gè)有用的文件:modules.dep。該文件和modules.alias在同一個(gè)目錄下。該文件的內(nèi)容是像下面一樣的行文本:

/lib/modules/2.6.16.27/kernel/drivers/net/8139too.ko:/lib/modules/2.6.16.27/kernel/drivers/net/mii.ko內(nèi)核模塊應(yīng)用層加載模塊操作過(guò)程

當(dāng)內(nèi)核通過(guò)modules.alias文件確定需要加載8139too模塊時(shí),調(diào)用modprobe8139too,modprobe工具再通過(guò)讀取modules.dep文件找到8139too.ko行,冒號(hào)前的文本給出了該模塊的絕對(duì)路徑,而后面的文本則是在加載該模塊前需要先行加載的模塊,也就是它所依賴的模塊。(模塊的依賴性后面講解)

以上討論的是內(nèi)核啟動(dòng)中,發(fā)現(xiàn)硬件,并且成功匹配模塊名稱并加載的情況。但如果內(nèi)核沒(méi)有成功匹配合適的模塊呢?我們就需要作一些工作自己加載了。

當(dāng)然,我們可以每次手動(dòng)調(diào)用insmod加載某個(gè)模塊,但如果是硬件設(shè)備驅(qū)動(dòng)的話,我們自然更愿意讓它在系統(tǒng)引導(dǎo)時(shí)便加載。內(nèi)核模塊應(yīng)用層加載模塊操作過(guò)程

Fedora在core3之后的版本中,加入了/etc/modprobe.conf文件,我們可以在該文件中加入我們自己的模塊的alias行。該文件的作用是在modprobe運(yùn)行時(shí),添加必要的模塊參數(shù)、修正模塊加載方式或使用更為靈活的模塊加載辦法,例如加入安裝腳本。

而LFS則使用了特意訂制的一個(gè)服務(wù)S05modules,讀取配置文件/etc/sysconfig/modules,加載其中以行指定的模塊名稱,不過(guò)只是簡(jiǎn)單地調(diào)用modprobe。如果我們要加入非系統(tǒng)直接支持的硬件的話,則需要修改相應(yīng)的modules.dep文件,以便modprobe工具可以正確的識(shí)別模塊的絕對(duì)路徑。

內(nèi)核模塊介紹應(yīng)用層加載模塊操作過(guò)程內(nèi)核如何支持可安裝模塊內(nèi)核提供的接口及作用模塊實(shí)例課程內(nèi)容內(nèi)核模塊內(nèi)核如何支持可安裝模塊每個(gè)已安裝模塊在內(nèi)核中都有一個(gè)module數(shù)據(jù)結(jié)構(gòu)(通過(guò)系統(tǒng)調(diào)用create_module()創(chuàng)建)內(nèi)核模塊內(nèi)核如何支持可安裝模塊structmodule{ enummodule_statestate; /*Memberoflistofmodules*/

structlist_headlist; /*Uniquehandleforthismodule*/

charname[MODULE_NAME_LEN];內(nèi)核模塊內(nèi)核如何支持可安裝模塊 /*Sysfsstuff.*/

structmodule_kobjectmkobj; structmodule_param_attrs*param_attrs; structmodule_attribute*modinfo_attrs; constchar*version; constchar*srcversion; structkobject*drivers_dir; /*Exportedsymbols*/

conststructkernel_symbol*syms; unsignedintnum_syms; constunsignedlong*crcs;內(nèi)核模塊內(nèi)核如何支持可安裝模塊 … /*Startupfunction.*/

int(*init)(void); …#ifdefCONFIG_MODULE_UNLOAD /*Referencecounts*/

structmodule_refref[NR_CPUS]; /*Whatmodulesdependonme?*/

structlist_headmodules_which_use_me; /*Whoiswaitingforustobeunloaded*/ structtask_struct*waiter; /*Destructionfunction.*/

void(*exit)(void);#endif …};內(nèi)核模塊內(nèi)核如何支持可安裝模塊structkernel_symbol{ unsignedlongvalue; constchar*name;};structmodule_ref{ local_tcount;}____cacheline_aligned;內(nèi)核模塊內(nèi)核如何支持可安裝模塊在內(nèi)核的源代碼module.c中的靜態(tài)全局變量modules,所有安裝的模塊會(huì)鏈入到這個(gè)鏈表上,從modules開(kāi)始,所有已安裝模塊的module結(jié)構(gòu)鏈接在一起成為一條鏈。nextprevmodulesnextprev……nextprev……module1module2內(nèi)核模塊介紹應(yīng)用層加載模塊操作過(guò)程內(nèi)核如何支持可安裝模塊內(nèi)核提供的接口及作用模塊實(shí)例課程內(nèi)容內(nèi)核模塊應(yīng)用層接口介紹create_module在內(nèi)核中創(chuàng)建一個(gè)module實(shí)例,以便內(nèi)核能對(duì)其進(jìn)行管理init_module對(duì)剛創(chuàng)建的module進(jìn)行初始化query_module查詢有關(guān)module的各種信息,比如模塊、依賴模塊、被依賴模塊、模塊導(dǎo)出的符號(hào)等等delete_module從內(nèi)核中刪除符號(hào)內(nèi)核提供了哪些接口來(lái)支持模塊呢??jī)?nèi)核模塊應(yīng)用層接口介紹打開(kāi)待安裝模塊并將其讀入到用戶空間。所謂“模塊”就是經(jīng)過(guò)編譯但未經(jīng)連接的.ko文件。模塊中必定有些在模塊內(nèi)部無(wú)法落實(shí)的符號(hào)(函數(shù)或變量),對(duì)這些符號(hào)的引用必須連接到內(nèi)核中的相應(yīng)符號(hào),也就是必須把這些符號(hào)在內(nèi)核映像中的地址填入模塊中需要訪問(wèn)這些符號(hào)的指令中以及數(shù)據(jù)結(jié)構(gòu)中。為此目的,需要通過(guò)系統(tǒng)調(diào)用query_module()向內(nèi)核詢問(wèn)這些符號(hào)在內(nèi)核中的地址。如果內(nèi)核允許“移出”這些符號(hào)的地址,就會(huì)返回有關(guān)的“符號(hào)表”。有些符號(hào)可能并不屬于內(nèi)核本身,而屬于已經(jīng)安裝的其他模塊。sbin/insmod過(guò)程內(nèi)核模塊應(yīng)用層接口介紹取得了內(nèi)核“移出”的符號(hào)表以后,就應(yīng)該可以使用模塊中所有的符號(hào)引用都得到了落實(shí)了。這部分操作與編譯后的連接相似。不過(guò)。常規(guī)的“連接”常常是雙向的。而現(xiàn)在只是在模塊中引用內(nèi)核里的符號(hào)或者某些已經(jīng)安裝的模塊中的符號(hào),而內(nèi)核卻并不要求(也不能)反過(guò)來(lái)引用這個(gè)待安裝模塊中的符號(hào)。當(dāng)然,內(nèi)核最后一定會(huì)要訪問(wèn)模塊中的某些變量或調(diào)用模塊中的某些函數(shù),否則模塊中的函數(shù)就得不到執(zhí)行,模塊的存在也就失去了意義。從這個(gè)意義上說(shuō),模塊與內(nèi)核的連接只完成了一半,我不妨稱之為“完成了單向連接”的模塊映像。內(nèi)核模塊應(yīng)用層接口介紹然后,通過(guò)系統(tǒng)調(diào)用create_module()在內(nèi)核中創(chuàng)建一個(gè)module數(shù)據(jù)結(jié)構(gòu),并且“預(yù)訂”所需的系統(tǒng)(內(nèi)核)空間。最后,通過(guò)系統(tǒng)調(diào)用init_module()把用戶空間中完成了單向連接的模塊映像裝入內(nèi)核空間,再調(diào)用模塊中init_module()的函數(shù)。內(nèi)核模塊應(yīng)用層接口介紹注意,不要可安裝模塊中的函數(shù)init_module()與系統(tǒng)調(diào)用init_module()搞混淆了,這完全是兩碼事。系統(tǒng)調(diào)用init_module()在內(nèi)核中的實(shí)現(xiàn)時(shí)sys_init_module(),這是由內(nèi)核提供的,整個(gè)內(nèi)核中只有這么一個(gè)。而模塊中的函數(shù)init_module(),則可是有可安裝模塊本身提供的,每個(gè)模塊都有這樣一個(gè)函數(shù)。通常,每個(gè)模塊的init_module()負(fù)責(zé)向內(nèi)核“登記”本模塊中的一些包含著函數(shù)指針的數(shù)據(jù)結(jié)構(gòu)(例如結(jié)構(gòu))。完成了這種登記之后,模塊與內(nèi)核之間的連接(另一個(gè)方向上的連接)才真正的完成了。內(nèi)核模塊介紹應(yīng)用層加載模塊操作過(guò)程內(nèi)核如何支持可安裝模塊內(nèi)核提供的接口及作用模塊實(shí)例課程內(nèi)容內(nèi)核模塊代碼講解用到的工具nmobjdumpdmesglsmodinsmodrmmod內(nèi)核模塊代碼講解#include<linux/init.h>#include<linux/module.h>#include<linux/kernel.h>staticint__inithello_init(void){printk(KERN_ALERT“Hello,world!\n");return0;}staticvoid__exithello_exit(void){printk(KERN_ALERT“Bye!\n");}module_init(hello_init);module_exit(hello_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("Shakespeare");內(nèi)核模塊代碼講解為定義了MODULE情況下#definemodule_init(x) __initcall(x);#definemodule_exit(x) __exitcall(x);#define__initcall(fn)device_initcall(fn)#define__exitcall(fn)\ staticexitcall_t__exitcall_##fn__exit_call=fn#definedevice_initcall(fn) __define_initcall("6",fn,6)#define__define_initcall(level,fn,id)\ staticinitcall_t__initcall_##fn##id__attribute_used__\ __attribute__((__section__(".initcall"level".init")))=fn宏module_init和module_exit內(nèi)核模塊代碼講解宏module_init和module_exit__arrtibute__是gcc的關(guān)鍵字,它告訴編譯器如何如何給變量分配空間。module_init(x)的意思是在.initcall6.init段的未使用空間定義了一個(gè)指向x的函數(shù)指針。那么系統(tǒng)是如何調(diào)用這些初始化函數(shù)的呢?在vmlinux.lds.S文件中定義了內(nèi)核代碼各段的空間,其中.initcall.init被定義為:

.initcall.init:AT(ADDR(.initcall.init)-0xC0000000){__initcall_start=.;*(.initcall0.init)*(.initcall0s.init)*(.initcall1.init)*(.initcall1s.init)*(.initcall2.init)*(.initcall2s.init)*(.initcall3.init)*(.initcall3s.init)*(.initcall4.init)*(.initcall4s.init)*(.initcall5.init)*(.initcall5s.init)*(.initcallrootfs.init)*(.initcall6.init)*(.initcall6s.init)*(.initcall7.init)*(.initcall7s.init)__initcall_end=.;}內(nèi)核模塊代碼講解宏module_init和module_exit也就是說(shuō),系統(tǒng)依靠全局變量__initcall_start和__initcall_end記錄。initcall.init段的起始和結(jié)束的地址。在init/main.c中定義了函數(shù)do_initcallsstaticvoid__initdo_initcalls(void){ initcall_t*call; … for(call=__initcall_start;call<__initcall_end;call++){ … result=(*call)(); … } …}內(nèi)核模塊代碼講解定義了MODULE情況下typedefint(*initcall_t)(void);typedefvoid(*exitcall_t)(void);/*Eachmodulemustuseonemodule_init(),oroneno_module_init*/#definemodule_init(initfn) \ staticinlineinitcall_t__inittest(void) \ {returninitfn;} \intinit_module(void)__attribute__((alias(#initfn)));/*Thisisonlyrequiredifyouwanttobeunloadable.*/#definemodule_exit(exitfn) \ staticinlineexitcall_t__exittest(void) \ {returnexitfn;} \voidcleanup_module(void)__attribute__((alias(#exitfn)));宏module_init和module_exit內(nèi)核模塊代碼講解宏module_init和module_exit在這中情況下,module_init(x)的意思是在模塊中定義一個(gè)別名為x的module_init函數(shù)。這樣,模塊被動(dòng)態(tài)加載時(shí),init_module()系統(tǒng)調(diào)用會(huì)根據(jù)module_init這個(gè)函數(shù)名稱來(lái)導(dǎo)出此函數(shù)的地址,進(jìn)行調(diào)用。內(nèi)核模塊代碼講解宏__init和__exit#define__init __attribute__((__section__(".init.text")))#define__exit __attribute__((__section__(".exit.text")))通過(guò)__init,會(huì)把函數(shù)中的代碼放到.init.text段中,系統(tǒng)啟動(dòng)完成后會(huì)釋放此空間。因?yàn)?,被__init修飾的代碼只在啟動(dòng)時(shí)執(zhí)行一次,以后不會(huì)被用到。內(nèi)核模塊代碼講解函數(shù)printk#define KERN_EMERG "<0>" /*systemisunusable*/#define KERN_ALERT "<1>" /*actionmustbetakenimmediately*/#define KERN_CRIT "<2>" /*criticalconditions*/#define KERN_ERR "<3>" /*errorconditions*/#define KERN_WARNING "<4>" /*warningconditions*/#define KERN_NOTICE "<5>" /*normalbutsignificantcondition*/#define KERN_INFO "<6>" /*informational*/#define KERN_DEBUG "<7>" /*debug-levelmessages*/內(nèi)核模塊makefileifneq($(KERNELRELEASE),)obj-m:=hello.oelseKERNELDIR?=/lib/modules/$(shelluname-r)/buildPWD:=$(shellpwd)default:$(MAKE)-C$(KERNELDIR)M=$(PWD)modulesendifclean:@rm*.o*.ko*.mod.c-f內(nèi)核模塊makefile目標(biāo)文件定義obj-m:=hello.o

obj-m則表示該文件要作為模塊編譯,而obj-y表示要由hello.c文件編譯得到hello.o并鏈接進(jìn)內(nèi)核,而在此目錄樹(shù)以外,只能用obj-m。編譯

我們將模塊相關(guān)文件目錄放在代碼樹(shù)以外的位置,用如下命令來(lái)編譯模塊:

make-C$(KERNELDIR)M=$PWDmodules

-C指定代碼樹(shù)的位置,M=$PWD或M=`PWD`告訴kbuild回到當(dāng)前目錄來(lái)執(zhí)行build操作。

內(nèi)核模塊加載和卸載模塊insmodhello-module.kormmodhello-module.ko內(nèi)核模塊模塊參數(shù)對(duì)于如何向模塊傳遞參數(shù),Linux

kernel

提供了一個(gè)簡(jiǎn)單的框架。其允許驅(qū)動(dòng)程序聲明參數(shù),并且用戶在系統(tǒng)啟動(dòng)或模塊裝載時(shí)為參數(shù)指定相應(yīng)值,在驅(qū)動(dòng)程序里,參數(shù)的用法如同全局變量。這些模塊參數(shù)也能夠在sysfs中顯示出來(lái)。結(jié)果,有許許多的方法用來(lái)創(chuàng)建和管理模塊參數(shù)。

內(nèi)核模塊模塊參數(shù)宏module_param(name,

type,

perm)name既是用戶看到的參數(shù)名,又是模塊內(nèi)接受參數(shù)的變量;

type表示參數(shù)的數(shù)據(jù)類型,是下列之一:byte,

short,

ushort,

int,

uint,

long,

ulong,

charp,

bool,

invbool。perm指定了在sysfs中相應(yīng)文件的訪問(wèn)權(quán)限。訪問(wèn)權(quán)限用通常的八進(jìn)制格式來(lái)表示,例如,用0644(表示ower具有讀寫(xiě)權(quán)限,group和everyone只讀權(quán)限),

或者用通常的S_Ifoo定義,例如,S_IRUGO

|

S_IWUSR

(表示everyone具有讀權(quán)限,用戶具有寫(xiě)權(quán)限)。用0表示完全關(guān)閉在sysfs中相對(duì)應(yīng)的項(xiàng)。內(nèi)核模塊模塊參數(shù)宏module_param_named(name,

variable,

type,

perm)我們也可以使模塊源文件內(nèi)部的變量名與外部的參數(shù)名有不同的名字。

name是外部可見(jiàn)的參數(shù)名。variable是源文件內(nèi)部的全局變量名。例如:

stati

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論