05年本科畢業(yè)設(shè)計(jì)做的是Linux下驅(qū)動(dòng)的剖析,,當(dāng)時(shí)就買(mǎi)了一本《Linux設(shè)備驅(qū)動(dòng)程序(第二版)》,但是沒(méi)有實(shí)現(xiàn)將最簡(jiǎn)單的helloworld程
序編譯成模塊,,加載到kernel里,。不過(guò),現(xiàn)在自己確實(shí)打算做一款芯片的Linux的驅(qū)動(dòng),,因此,,又開(kāi)始看了《Linux設(shè)備驅(qū)動(dòng)程序》這本書(shū),不過(guò)已
經(jīng)是第三版了,。第二版講的是2.4的內(nèi)核,,第三版講的是2.6的內(nèi)核。兩個(gè)內(nèi)核版本之間關(guān)于編譯內(nèi)核以及加載模塊的方法都有所變化,。 本文是基于2.6的內(nèi)核,,也建議各位可以先看一下《Linux內(nèi)核設(shè)計(jì)與實(shí)現(xiàn)(第二版)》作為一個(gè)基礎(chǔ)知識(shí)的鋪墊。當(dāng)然,,從實(shí)踐角度來(lái)看,,只要按著以下的步驟去做也應(yīng)該可以實(shí)現(xiàn)成功編譯內(nèi)核及加載模塊,。 個(gè)人用的Linux版本為:Debian GNU/Linux,內(nèi)核版本為:2.6.20-1-686. 第一步,,下載Linux內(nèi)核的源代碼,,即構(gòu)建LDD3(Linux Device Drivers 3rd)上面所說(shuō)的內(nèi)核樹(shù)。 如過(guò)安裝的Linux系統(tǒng)中已經(jīng)自帶了源代碼的話(huà),,應(yīng)該在/usr/src目錄下,。如果該目錄為空的話(huà),,則需要自己手動(dòng)下載源代碼,。下載代碼的方法和鏈接很多,也可以在CU上通過(guò)http://download./search/?key=&;q=kernel&frmid=53去下載,。不過(guò),,下載的內(nèi)核版本最好和所運(yùn)行的Linux系統(tǒng)的內(nèi)核版本一致。當(dāng)然,,也可以比Linux系統(tǒng)內(nèi)核的版本低,,但高的話(huà)應(yīng)該不行(個(gè)人尚未實(shí)踐)。 Debian下可以很方便的通過(guò)Debian源下載: 首先查找一下可下載的內(nèi)核源代碼: # apt-cache search linux-source 其中顯示的有:linux-source-2.6.20,沒(méi)有和我的內(nèi)核版本完全匹配,,不過(guò)也沒(méi)關(guān)系,,直接下載就可以了: # apt-get install linux-source-2.6.20 下載完成后,安裝在/usr/src下,,文件名為:linux-source-2.6.20.tar.bz2,是一個(gè)壓縮包,,解壓縮既可以得到整個(gè)內(nèi)核的源代碼: # tar jxvf linux-source-2.6.20.tar.bz2 解壓后生成一個(gè)新的目錄/usr/src/linux--source-2.6.20,所有的源代碼都在該目錄下,。 注:該目錄會(huì)因內(nèi)核版本的不同而不同,,各位動(dòng)手實(shí)踐的朋友只需知道自己的源代碼所在的具體位置即可。 第二步:配置及編譯內(nèi)核,。 進(jìn)入/usr/src/linux--source-2.6.20目錄下,,可以看到Makefile文件,它包含了整個(gè)內(nèi)核樹(shù)編譯信息,。該文件最上面四行是關(guān)于內(nèi)核版本的信息,。對(duì)于整個(gè)Makefile可以不用做修改,,采用默認(rèn)的就可以了。 一般情況下,需要先用命令諸如"make menuconfig", "make xconfig"或者"make oldcofig"對(duì)內(nèi)核進(jìn)行配置,,這幾個(gè)都是對(duì)內(nèi)核進(jìn)行配置的命令,只是它們運(yùn)行的環(huán)境不一樣,,執(zhí)行一下這幾個(gè)命令中的任何一個(gè)即可對(duì)內(nèi)核進(jìn)行配置: make menuconfig是基于界面的內(nèi)核配置方法,,make xconfig應(yīng)該是基于QT庫(kù)的,還有make gcofig也是基于圖形的配置方法,,應(yīng)該是需要GTK的環(huán)境,,make oldcofig就是對(duì)內(nèi)核樹(shù)原有的.config文件進(jìn)行配置一下即可,。 其實(shí)內(nèi)核的配置部分,主要是保證內(nèi)核啟動(dòng)模塊可動(dòng)態(tài)加載的配置,,默認(rèn)配置里面應(yīng)該已經(jīng)包含了這樣的內(nèi)容,,因此,我用的是make oldconfig. 內(nèi)核的詳細(xì)配置請(qǐng)見(jiàn)另外一位網(wǎng)友的帖子,,這里給出鏈接: http://linux./bbs/viewthread.php?tid=885597&extra=page%3D1%26amp%3Bfilter%3Ddigest 在內(nèi)核源碼的目錄下執(zhí)行: # make # make bzImage 其中,,第一個(gè)make也可以不執(zhí)行,直接make bzImage,。這個(gè)過(guò)程可能要持續(xù)一個(gè)小時(shí)左右,,因此是對(duì)整個(gè)內(nèi)核重新編譯了。執(zhí)行結(jié)束后,,可以看到在當(dāng)前目錄下生成了一個(gè)新的文件: vmlinux, 其屬性為-rwxr-xr-x,。 然后執(zhí)行: # make modules # make modules_install 對(duì)內(nèi)核的所有模塊進(jìn)行編譯和安裝。 執(zhí)行結(jié)束之后,,會(huì)在/lib/modules下生成新的目錄/lib/modules/2.6.20/,。 在隨后的編譯模塊文件時(shí),要用到這個(gè)路徑下的build目錄,。至此,,內(nèi)核編譯完成??梢灾貑⒁幌孪到y(tǒng),。 第三步:編寫(xiě)模塊文件及Makefile 以LDD3上的hello.c為例: //hello.c #include <linux/init.h> #include <linux/module.h> MODULE_LICENSE("Dual BSD/GPL"); static int hello_init(void) { printk(KERN_ALERT "Hello, world\n"); return 0; } static void hello_exit(void) { printk(KERN_ALERT"Goodbye, cruel world\n"); } module_init(hello_init); module_exit(hello_exit); Makefile文件的內(nèi)容為: obj-m := hello.o KERNELDIR := /lib/modules/2.6.20/build PWD := $(shell pwd) modules: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules modules_install: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install clean: rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions 其中,hello.c和Makefile文件應(yīng)該位于同一個(gè)目錄下,可以放在/home下,我的兩個(gè)文件都位于/home/david/. 第四步:編譯和裝載模塊 在文件所處的目錄下,執(zhí)行: debian:/home/david # make 然后查看該目錄下有哪些文件生成: debian:/home/david # ls -l 總計(jì) 28 drwxr-xr-x 2 david david 4096 2007-02-07 17:49 Desktop -rw-r--r-- 1 david david 462 2007-07-20 13:42 hello.c -rw-r--r-- 1 root root 2432 2007-07-20 13:55 hello.ko -rw-r--r-- 1 root root 607 2007-07-20 13:55 hello.mod.c -rw-r--r-- 1 root root 1968 2007-07-20 13:55 hello.mod.o -rw-r--r-- 1 root root 1140 2007-07-20 13:55 hello.o -rw-r--r-- 1 david david 267 2007-07-20 13:48 Makefile -rw-r--r-- 1 root root 0 2007-07-05 14:11 Module.symvers 可見(jiàn),已經(jīng)生成模塊文件hello.ko. 然后,就可以加載該模塊: debian:/home/david # insmod hello.ko 查看模塊是否加載進(jìn)內(nèi)核: debian:/home/david # lsmod Module Size Used by hello 1344 0 nfs 219468 0 nfsd 202224 17 ... ... 其中Module名為hello的即為我們所加載的模塊. 卸載模塊: debian:/home/david # rmmod hello 同樣可以通過(guò)lsmod來(lái)查看該模塊是否被卸載. 這里有兩個(gè)問(wèn)題,其一就是printk()輸出的問(wèn)題.LDD3上也說(shuō),在加載和卸載模塊的時(shí)候都會(huì)有信息輸出在屏幕上,如果通過(guò)終端仿真器(,則在屏幕 上看不到任何輸出.我同時(shí)在虛擬機(jī)和和物理機(jī)都運(yùn)行了該模塊,均未看到有"Hello, world"(加載模塊時(shí)printk的輸出)或"Goodby, cruel world"(卸載模塊時(shí)printk的輸出). 這個(gè)不知道是我操作系統(tǒng)發(fā)行版的原因還是系統(tǒng)配置的問(wèn)題,請(qǐng)了解這個(gè)問(wèn)題的朋友指點(diǎn)一下. 其二,書(shū)上講到如果屏幕上看不到信息,可能輸出在某個(gè)日志文件里面了,并說(shuō)可能在/var/log/messages文件中.并且看到網(wǎng)上很多網(wǎng)友也說(shuō)是 輸出到這個(gè)文件里面.我不知道有沒(méi)有發(fā)現(xiàn)輸出在其他日志文件里的,不過(guò)我的這個(gè)信息輸出在/var/log/syslog里面.在加載和卸載完該模塊后, 執(zhí)行命令: debian:/home/david # cat /var/log/syslog | grep world 可以看到有兩行內(nèi)容.當(dāng)然,也可以不用grep world, 應(yīng)該會(huì)出現(xiàn)在最后兩行. Jul 20 14:15:29 localhost kernel: Hello, world Jul 20 14:15:34 localhost kernel: Goodbye, cruel world 這就是printk應(yīng)該輸出的信息. 這里有另外一個(gè)方法,可以實(shí)現(xiàn)printk的信息輸出在屏幕上,即更改printk輸出的優(yōu)先級(jí).例子中的優(yōu)先級(jí)為:KERN_ALERT,優(yōu)先級(jí)為<1>,如果將優(yōu)先級(jí)改為KERN_EMERG即<0>,則可以看到屏幕的輸出信息. 修改的方法只是修改一下hello.c中兩句printk()的內(nèi)容,修改后的hello.c如下: #include <linux/init.h> #include <linux/module.h> MODULE_LICENSE("Dual BSD/GPL"); static int hello_init(void) { printk(KERN_EMERG "Hello, world\n"); /*改動(dòng)部分*/ return 0; } static void hello_exit(void) { printk(KERN_EMERG"Goodbye, cruel world\n"); /*改動(dòng)部分*/ } module_init(hello_init); module_exit(hello_exit); 同樣的方法編譯生成模塊,再次用insmod和rmmod,則在屏幕上看到的輸出信息為: debian:/home/david# insmod hello.ko debian:/home/david# Message from syslogd@localhost at Fri Jul 20 14:27:32 2007 ... localhost kernel: Hello, world debian:/home/david# rmmod hello debian:/home/david# Message from syslogd@localhost at Fri Jul 20 14:27:42 2007 ... localhost kernel: Goodbye, cruel world debian:/home/david 但是,是否能夠?qū)rintk()的優(yōu)先級(jí)改為KERN_EMERG值得商榷.因?yàn)樵贚inux Kernel Development中,對(duì)該優(yōu)先級(jí)的描述為: An emergency condition; the system is probably dead. 以上就是整個(gè)2.6內(nèi)核編譯步驟以及模塊動(dòng)態(tài)加載的方法.理解和解釋有誤的地方,也請(qǐng)各位瀏覽本文的朋友指點(diǎn),也希望能和對(duì)內(nèi)核和驅(qū)動(dòng)感興趣的朋友交流. 本文也參考了一位網(wǎng)友博客上一篇關(guān)于編譯2.6內(nèi)核的文章,這里給出鏈接: http://blog.csdn.net/wooin/archive/2007/05/21/1619141.aspx 【08-10】補(bǔ)充:本文最后所提到的模塊加載和卸載時(shí)沒(méi)有看到屏幕的輸出信息,即printk輸出的問(wèn)題,。經(jīng)過(guò)實(shí)踐之后,,本人發(fā)現(xiàn)了個(gè)人的原因,和大家 交流一下,。LDD3書(shū)中所說(shuō)的看不到輸出指的是在終端仿真器,,就是我們平時(shí)在X-Window下用的終端,譬如我用的時(shí)Gnome-Terminal,,在 這種情況下看不到屏幕的輸出,。而我上面的例程正是在終端下運(yùn)行的。當(dāng)時(shí)沒(méi)想到切換到控制臺(tái)Console運(yùn)行一下試試,。今天在控制臺(tái)運(yùn)行了,,發(fā)現(xiàn)無(wú)論是在 虛擬機(jī)還是物理機(jī)下,只要在Console下面運(yùn)行,,是可以看到屏幕輸出的,。 [ 本帖最后由 Godbach 于 2007-8-10 11:45 編輯 ] |
|
來(lái)自: ShangShujie > 《linux》