china_long
ldd3之模塊學(xué)習(xí)總結(jié)
ldd3之模塊學(xué)習(xí)總結(jié) 一個(gè)學(xué)習(xí)Linux設(shè)備驅(qū)動(dòng)程序都會(huì)碰到的第一個(gè)例程: #include linux/init.h> #include linux/module.h> MODULE_LICENSE("Dual BSD/GPL"); static int hello_init(void) { printk(KERN_ALERT "Hello, Tekkaman Ninja !\n"); return 0; } static [color=#ff0000]void[/color] hello_exit(void) { printk(KERN_ALERT "Goodbye, Tekkaman Ninja !\n Love Linux !Love ARM ! Love KeKe !\n"); } module_init(hello_init); module_exit(hello_exit); 1、Makefile書寫: KERNELDIR = /usr/src/linux_2.6.26/linux-2.6.26 #內(nèi)核原代碼樹位置 PWD = $(shell pwd) INSTALLDIR = /lib/modules/2.6.26linux2.6.26 #1 obj-m := hello.o 模塊只有一個(gè)文件 #2 obj-m := hello.o hello-objs := one.o two.o three.o 模塊有多個(gè)文件 #3 如果模塊是由一個(gè)目錄(online)組成的寫法如下 a、在這個(gè)目錄的上一級(jí)目錄中的makefile中加入 [color=#000000]obj-m += online/ #告訴在編譯的時(shí)候找到online目錄[/color] [color=#000000] obj-$(CONFIG_USB_GADGET_ONLINE) += online/ 這種方式是用配置選項(xiàng)(kconfig文件中添加配置選項(xiàng))來判斷是否編譯這個(gè)子目錄(即模塊)[/color] [color=#000000] b、最后,我們在online/下創(chuàng)建新文件Makefile,并且添加下面一行到其內(nèi)。 obj-m += netmeeting.o[/color] [color=#000000] netmeeting-objs := one.o two.o three.o 如果有多個(gè)文件[/color] modules: [TAB] $(MAKE) -C $(KERNELDIR) M=$(PWD) modules modules_install: [TAB] cp hello.ko $(INSTALLDIR) clean: [TAB] rm -rf *.o *.ko *.mod.c *.o.cmd .tmp_versions *.mod.o .cmd .PHONY :modules modules_install clean 2、編譯運(yùn)行 insmod rmmod lsmod 3、調(diào)試查看輸出信息 a、在模塊中添加調(diào)試輸出語句 printk cat /proc/kmsg (rsyslog沒有運(yùn)行) dmesg (syslogd進(jìn)程已經(jīng)運(yùn)行 /etc/rc.d/init/rsyslog 開啟) cat /var/log/message 注意: [color=#000000]printk[/color]([color=#000000]KERN_ALERT[/color]"xxxx“); 宏的定義等級(jí)如下(printk.c): #define KERN_EMERG "" /* system is unusable */ #define KERN_ALERT "" /* action must be taken immediately */ #define KERN_CRIT "" /* critical conditions */ #define KERN_ERR "" /* error conditions */ #define KERN_WARNING "" /* warning conditions */ #define KERN_NOTICE "" /* normal but significant condition */ #define KERN_INFO "" /* informational */ #define KERN_DEBUG "" /* debug-level messages */ 當(dāng)指定的等級(jí)小于console_level(默認(rèn)4)時(shí)候,就可以直接在tty上面打印輸出語句 console_level設(shè)置方式: 通過對/proc/sys/kernel/printk的訪問來改變[color=#000102]console_loglevel的值:[/color] [Tekkaman2440@SBC2440V4]#echo 1 > /proc/sys/kernel/printk [Tekkaman2440@SBC2440V4]#cat /proc/sys/kernel/printk 1 4 1 7 [Tekkaman2440@SBC2440V4]#insmod hello.ko [Tekkaman2440@SBC2440V4]#rmmod hello [Tekkaman2440@SBC2440V4]#echo 7 > /proc/sys/kernel/printk [Tekkaman2440@SBC2440V4]#cat /proc/sys/kernel/printk7 4 1 7 [Tekkaman2440@SBC2440V4]#insmod hello.ko Hello, Tekkaman Ninja ! [Tekkaman2440@SBC2440V4]#rmmod hello Goodbye, Tekkaman Ninja ! Love Linux !Love ARM ! Love KeKe ! 四個(gè)數(shù)字的含義:當(dāng)前的loglevel、默認(rèn)loglevel、最小允許的loglevel、引導(dǎo)時(shí)的默認(rèn)loglevel。 4、模塊中使用的頭文件 #include linux/init.h> #include linux/module.h> 5、模塊中使用的描述性定義 MODULE_AUTHOR(""); MODULE_DESCRIPTION(""); MODULE_VERSION(""); MODULE_ALIAS(""); MODULE_DEVICE_TABLE(""); 描繪性定義一般放在最后面 6、模塊中的參數(shù) (內(nèi)核允許對驅(qū)動(dòng)程序指定參數(shù),而這些參數(shù)可在裝載驅(qū)動(dòng)程序模塊時(shí)改變[color=#000000]。 example : insmod hello-param.ko howmany=2 whom="KeKe" TNparam=4,3,2,1[/color]) [color=#000000]對于如何向模塊傳遞參數(shù),Linux kernel 提供了一個(gè)簡單的框架。其允許驅(qū)動(dòng)程序聲明參數(shù),并且用戶在系統(tǒng)啟動(dòng)或模塊裝載時(shí)為參數(shù)指定相應(yīng)值,在驅(qū)動(dòng)程序里,參數(shù)的用法如同全局變量。這些模塊參數(shù)也能夠在sysfs中顯示出來。結(jié)果,有許許多多的方法用來創(chuàng)建和管理模塊參數(shù)。 通過宏module_param()定義一個(gè)模塊參數(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。這些類型分別是:a byte, a short integer, an unsigned short integer, an integer, an unsigned integer, a long integer, an unsigned long integer, a pointer to a char, a Boolean, a Boolean whose value is inverted from what the user specifies. The byte type is stored in a single char and the Boolean types are stored in variables of type int. The rest are stored in the corresponding primitive C types. 最后,perm指定了在sysfs中相應(yīng)文件的訪問權(quán)限。訪問權(quán)限用通常的八進(jìn)制格式來表示,例如,用0644(表示ower具有讀寫權(quán)限,group和everyone只讀權(quán)限), 或者用通常的S_Ifoo定義,例如,S_IRUGO | S_IWUSR (表示everyone具有讀權(quán)限,用戶具有寫權(quán)限)。用0表示完全關(guān)閉在sysfs中相對應(yīng)的項(xiàng)。 其實(shí)宏不會(huì)聲明變量,因此在使用宏之前,必須聲明變量。所以,典型地用法如下: static unsigned int use_acm = 0; module_param(use_acm, uint, S_IRUGO); 這些必須寫在模塊源文件的開頭部分。即use_acm是全局的。 我們也可以使模塊源文件內(nèi)部的變量名與外部的參數(shù)名有不同的名字。這通過宏module_param_named()定義。 module_param_named(name, variable, type, perm); 這里name是外部可見的參數(shù)名,variable是源文件內(nèi)部的全局變量名。例如: static unsigned int max_test = 9; module_param_name(maximum_line_test, max_test, int, 0); 如果模塊參數(shù)是一個(gè)字符串時(shí),通常使用charp類型定義這個(gè)模塊參數(shù)。內(nèi)核復(fù)制用戶提供的字符串到內(nèi)存,并且相對應(yīng)的變量指向這個(gè)字符串。例如: static char *name; module_param(name, charp, 0); 另一種方法是通過宏module_param_string()讓內(nèi)核把字符串直接復(fù)制到程序中的字符數(shù)組內(nèi)。 module_param_string(name, string, len, perm); 這里,name是外部的參數(shù)名,string是內(nèi)部的變量名,len是以string命名的buffer大?。梢孕∮赽uffer的大小,但是沒有意義),perm表示sysfs的訪問權(quán)限(或者perm是零,表示完全關(guān)閉相對應(yīng)的sysfs項(xiàng))。例如: static char species[BUF_LEN]; module_param_string(specifies, species, BUF_LEN, 0); 上面說得只是給模塊傳入一個(gè)參數(shù)的情況,如果給模塊傳入多個(gè)參數(shù),那該怎么辦呢?可以通過宏module_param_array()給模塊傳入多個(gè)參數(shù)。 用法如下: module_param_array(name, type, nump, perm); 這里,name既是外部模塊的參數(shù)名又是程序內(nèi)部的變量名,type是數(shù)據(jù)類型,perm是sysfs的訪問權(quán)限。指針nump指向一個(gè)整數(shù),其值表示有多少個(gè)參數(shù)存放在數(shù)組name中。值得注意是name數(shù)組必須靜態(tài)分配。例如: static int finsh[MAX_FISH]; static int nr_fish; module_param_array(fish, int, &nr_fish, 0444); 通過宏module_param_array_named()使得內(nèi)部的數(shù)組名與外部的參數(shù)名有不同的名字。例如: module_param_array_named(name, array, type, nump, perm); 這里的參數(shù)意義與其它宏一樣。 最后,通過宏MODULE_PARM_DESC()對參數(shù)進(jìn)行說明: static unsigned short size = 1; module_param(size, ushort, 0644); MODULE_PARM_DESC(size, “The size in inches of the fishing pole” \ “connected to this computer.” ); 使用這些宏時(shí)需要包含頭文件。[/color] [color=#ff0000]注意:[/color] 由于模塊參數(shù)指的是外部向模塊傳遞參數(shù),模塊聲明的變量能否被內(nèi)核其他代碼和模塊使用呢?(是不是只要是非靜態(tài)的且導(dǎo)出符號(hào),內(nèi)核和其他模塊就能共享這個(gè)變量呢?) example code : #include linux/init.h> #include linux/module.h> #include linux/moduleparam.h> MODULE_LICENSE("Dual BSD/GPL"); static char *whom = "Tekkaman Ninja"; static int howmany = 1; static int TNparam[] = {1,2,3,4}; static int TNparam_nr = 4; module_param(howmany, int, S_IRUGO); module_param(whom, charp, S_IRUGO); module_param_array(TNparam , int , &TNparam_nr , S_IRUGO); static int hello_init(void) { int i; for (i = 0; i howmany; i++) printk(KERN_ALERT "(%d) Hello, %s !\n", i, whom); for (i = 0; i ; i++) printk(KERN_ALERT "TNparam[%d] : %d \n", i, TNparam[i]); return 0; } static void hello_exit(void) { printk(KERN_ALERT "Goodbye, Tekkaman Ninja !\n Love Linux !Love ARM ! Love KeKe !\n"); } module_init(hello_init); module_exit(hello_exit); 編譯完成后 測試1 insmod hello-param.ko howmany=2 whom="KeKe" TNparam=4,3,2,1 測試2 [Tekkaman2440@SBC2440V4]#insmod hello-param.ko howmany=2 whom="KeKe" TNparam=4,3,2,1,5,6,7,8 TNparam: can only take 4 arguments hello_param: `4' invalid for parameter `TNparam' insmod: cannot insert 'hello-param.ko': Invalid parameters (-1): Invalid argument 注意: 這個(gè)測試說明module_param_array(TNparam , int , &TNparam_nr , S_IRUGO),參數(shù)[color=#ff0102]TNparam_nr不能自己限制參數(shù)的個(gè)數(shù),而需要程序員自己確定參數(shù)的個(gè)數(shù),否則數(shù)組[/color][color=#0000cc]TNparam可能會(huì)越界。(個(gè)人認(rèn)為如果傳送數(shù)組形式的參數(shù)最好再定義一個(gè)宏來確定數(shù)組的大小)[/color] 7、導(dǎo)出符號(hào) [color=#000000] 當(dāng)裝載模塊的時(shí)候,模塊動(dòng)態(tài)地鏈接入內(nèi)核之中。動(dòng)態(tài)鏈接的二進(jìn)制代碼只能調(diào)用外部函數(shù),然而,外部函數(shù)必須明確地輸出,在內(nèi)核中,通過EXPORT_SYMBOL()和EXPORT_SYMBOL_GPL來達(dá)到這個(gè)目的。 輸出的函數(shù)可以被其它模塊調(diào)用。沒有輸出過的函數(shù)不能被其它模塊調(diào)用。模塊比核心內(nèi)核映像代碼具有更嚴(yán)格的鏈接和調(diào)用規(guī)則。因?yàn)樗泻诵脑次募溄映梢粋€(gè)單一的作為基礎(chǔ)的映像,因此在內(nèi)核中核心代碼可以調(diào)用任何非靜態(tài)的接口。當(dāng)然,輸出符號(hào)也必須是非靜態(tài)屬性。 一套輸出的內(nèi)核符號(hào)稱之為輸出的內(nèi)核接口,甚至稱之為kernel API。 輸出一個(gè)內(nèi)核符號(hào)是舉手之勞之事。當(dāng)函數(shù)聲明之時(shí),在其后用EXPORT_SYMBOL()把函數(shù)輸出。 例如: /* it will receive control requests including set_configuration(), which enables non-control requests. */ int usb_gadget_register_driver(struct usb_gadget_driver *driver) { … } EXPORT_SYMBOL(usb_gadget_register_driver) ; 從此以后,任何模塊都可以調(diào)用函數(shù)usb_gadget_register_driver(),只要在源文件中包含聲明這個(gè)函數(shù)的頭文件,或者extern這個(gè)函數(shù)的聲明。 有些開發(fā)者希望他們的接口只讓遵從GPL的模塊調(diào)用。通過MODULE_LICENSE()的使用,內(nèi)核鏈接器能夠強(qiáng)制保證做到這點(diǎn)。如果你希望前面的函數(shù)僅被標(biāo)有GPL許可證的模塊訪問,那么你可以用如下方式輸出符號(hào): EXPORT_SYMBOL_GPL(usb_gadget_register_driver); 如果你的代碼配置為模塊方式,那么必須確保:源文件中使用的所有接口必須是已經(jīng)輸出的符號(hào),否則導(dǎo)致在裝載時(shí)鏈接錯(cuò)誤。[/color] [b]本文來自ChinaUnix博客,如果查看原文請點(diǎn):[/b][url]http://blog./u2/83200/showart_1679685.html[/url] |
|