- Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)詳解(第2版)
- 華清遠(yuǎn)見(jiàn)嵌入式培訓(xùn)中心 宋寶華編著
- 160字
- 2018-12-27 10:06:12
第2篇 Linux設(shè)備驅(qū)動(dòng)核心理論
Linux內(nèi)核模塊
Linux文件系統(tǒng)與設(shè)備文件系統(tǒng)
字符設(shè)備驅(qū)動(dòng)
Linux設(shè)備驅(qū)動(dòng)中的并發(fā)控制
Linux設(shè)備驅(qū)動(dòng)中的阻塞與非阻塞I/O
Linux設(shè)備驅(qū)動(dòng)中的異步通知與異步I/O
中斷與時(shí)鐘
內(nèi)存與I/O訪問(wèn)
工程中的Linux設(shè)備驅(qū)動(dòng)
第4章 Linux內(nèi)核模塊
本章導(dǎo)讀
Linux設(shè)備驅(qū)動(dòng)會(huì)以內(nèi)核模塊的形式出現(xiàn),因此,學(xué)會(huì)編寫Linux內(nèi)核模塊編程是學(xué)習(xí)Linux設(shè)備驅(qū)動(dòng)的先決條件。
4.1~4.2節(jié)講解了Linux內(nèi)核模塊的概念和結(jié)構(gòu),4.3~4.8節(jié)對(duì)Linux內(nèi)核模塊的各個(gè)組成部分進(jìn)行了展現(xiàn),4.1~4.2節(jié)與4.3~4.8節(jié)是整體與部分的關(guān)系。
4.9節(jié)說(shuō)明了獨(dú)立存在的Linux內(nèi)核模塊的Makefile文件編寫方法和模塊的編譯方法。
4.1 Linux內(nèi)核模塊簡(jiǎn)介
Linux 內(nèi)核的整體結(jié)構(gòu)已經(jīng)非常龐大,而其包含的組件也非常多。我們?cè)鯓影研枰牟糠侄及趦?nèi)核中呢?
一種方法是把所有需要的功能都編譯到Linux內(nèi)核。這會(huì)導(dǎo)致兩個(gè)問(wèn)題,一是生成的內(nèi)核會(huì)很大,二是如果我們要在現(xiàn)有的內(nèi)核中新增或刪除功能,將不得不重新編譯內(nèi)核。
有沒(méi)有一種機(jī)制使得編譯出的內(nèi)核本身并不需要包含所有功能,而在這些功能需要被使用的時(shí)候,其對(duì)應(yīng)的代碼被動(dòng)態(tài)地加載到內(nèi)核中呢?
Linux提供了這樣的一種機(jī)制,這種機(jī)制被稱為模塊(Module)。模塊具有這樣的特點(diǎn)。
● 模塊本身不被編譯入內(nèi)核映像,從而控制了內(nèi)核的大小。
● 模塊一旦被加載,它就和內(nèi)核中的其他部分完全一樣。
為了使讀者建立對(duì)模塊的初步感性認(rèn)識(shí),我們先來(lái)看一個(gè)最簡(jiǎn)單的內(nèi)核模塊“Hello World”,如代碼清單4.1所示。
代碼清單4.1一個(gè)最簡(jiǎn)單的Linux內(nèi)核模塊
1 #include <linux/init.h> 2 #include <linux/module.h> 3 4 static int hello_init(void) 5 { 6 printk(KERN_INFO " Hello World enter\n"); 7 return 0; 8 } 9 10 static void hello_exit(void) 11 { 12 printk(KERN_INFO " Hello World exit\n "); 13 } 14 15 module_init(hello_init); 16 module_exit(hello_exit); 17 18 MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); 19 MODULE_LICENSE("Dual BSD/GPL"); 20 MODULE_DESCRIPTION("A simple Hello World Module"); 21 MODULE_ALIAS("a simplest module");
這個(gè)最簡(jiǎn)單的內(nèi)核模塊只包含內(nèi)核模塊加載函數(shù)、卸載函數(shù)和對(duì)Dual BSD/GPL許可權(quán)限的聲明以及一些描述信息,位于本書配套光盤 VirtualBox 虛擬機(jī)映像的/home/lihacker/develop/svn/ldd6410-read-only/training/kernel/drivers/hello 目錄。編譯它會(huì)產(chǎn)生 hello.ko 目標(biāo)文件,通過(guò)“insmod ./hello.ko”命令可以加載它,通過(guò)“rmmod hello”命令可以卸載它,加載時(shí)輸出“Hello World enter”,卸載時(shí)輸出“Hello World exit”。
內(nèi)核模塊中用于輸出的函數(shù)是內(nèi)核空間的printk()而非用戶空間的printf(),printk()的用法和printf()基本相似,但前者可定義輸出級(jí)別。printk()可作為一種最基本的內(nèi)核調(diào)試手段,在 Linux驅(qū)動(dòng)的調(diào)試章節(jié)中將詳細(xì)講解這個(gè)函數(shù)。
在Linux中,使用lsmod命令可以獲得系統(tǒng)中加載了的所有模塊以及模塊間的依賴關(guān)系,例如:
Module Size Used by hello 9 472 0 nls_iso8859_1 12 032 1 nls_cp437 13 696 1 vfat 18 816 1 fat 57 376 1 vfat ...
lsmod命令實(shí)際上讀取并分析“/proc/modules”文件,與上述lsmod命令結(jié)果對(duì)應(yīng)的“/proc/modules”文件如下:
lihacker@lihacker-laptop:~/$ cat /proc/modules hello 9472 0 - Live 0xf953b000 nls_iso8859_1 12032 1 - Live 0xf950c000 nls_cp437 13696 1 - Live 0xf9561000 vfat 18816 1 - Live 0xf94f3000 ...
內(nèi)核中已加載模塊的信息也存在于/sys/module 目錄下,加載 hello.ko 后,內(nèi)核中將包含/sys/module/hello目錄,該目錄下又包含一個(gè)refcnt文件和一個(gè)sections目錄,在/sys/module/hello目錄下運(yùn)行“tree -a”得到如下目錄樹(shù):
lihacker@lihacker-laptop:/sys/module/hello$ tree -a . |-- holders |-- initstate |-- notes | '-- .note.gnu.build-id |-- refcnt |-- sections | |-- .bss | |-- .data | |-- .gnu.linkonce.this_module | |-- .note.gnu.build-id | |-- .rodata.str1.1 | |-- .strtab | |-- .symtab | '-- .text '-- srcversion 3 directories, 12 files
modprobe命令比insmod命令要強(qiáng)大,它在加載某模塊時(shí),會(huì)同時(shí)加載該模塊所依賴的其他模塊。使用modprobe命令加載的模塊若以“modprobe -r filename”的方式卸載將同時(shí)卸載其依賴的模塊。
使用modinfo <模塊名>命令可以獲得模塊的信息,包括模塊作者、模塊的說(shuō)明、模塊所支持的參數(shù)以及vermagic:
lihacker@lihacker-laptop: ~ /develop/svn/ldd6410-read-only/training/kernel/drivers/ hello$ modinfo hello.ko filename: hello.ko alias: a simplest module description: A simple Hello World Module license: Dual BSD/GPL author: Barry Song <21cnbao@gmail.com> srcversion: 3FE9B0FBAFDD565399B9C05 depends: vermagic: 2.6.28-11-generic SMP mod_unload modversions 586
4.2 Linux內(nèi)核模塊程序結(jié)構(gòu)
一個(gè)Linux內(nèi)核模塊主要由如下幾個(gè)部分組成。
(1)模塊加載函數(shù)(一般需要)。
當(dāng)通過(guò)insmod或modprobe命令加載內(nèi)核模塊時(shí),模塊的加載函數(shù)會(huì)自動(dòng)被內(nèi)核執(zhí)行,完成本模塊的相關(guān)初始化工作。
(2)模塊卸載函數(shù)(一般需要)。
當(dāng)通過(guò)rmmod命令卸載某模塊時(shí),模塊的卸載函數(shù)會(huì)自動(dòng)被內(nèi)核執(zhí)行,完成與模塊卸載函數(shù)相反的功能。
(3)模塊許可證聲明(必須)。
許可證(LICENSE)聲明描述內(nèi)核模塊的許可權(quán)限,如果不聲明LICENSE,模塊被加載時(shí),將收到內(nèi)核被污染 (kernel tainted)的警告。
在Linux2.6內(nèi)核中,可接受的LICENSE包括“GPL”、“GPL v2”、“GPL and additional rights”、“Dual BSD/GPL”、“Dual MPL/GPL”和“Proprietary”。
大多數(shù)情況下,內(nèi)核模塊應(yīng)遵循 GPL 兼容許可權(quán)。Linux2.6 內(nèi)核模塊最常見(jiàn)的是以MODULE_LICENSE( "Dual BSD/GPL" )語(yǔ)句聲明模塊采用BSD/GPL雙LICENSE。
(4)模塊參數(shù)(可選)。
模塊參數(shù)是模塊被加載的時(shí)候可以被傳遞給它的值,它本身對(duì)應(yīng)模塊內(nèi)部的全局變量。
(5)模塊導(dǎo)出符號(hào)(可選)。
內(nèi)核模塊可以導(dǎo)出符號(hào)(symbol,對(duì)應(yīng)于函數(shù)或變量),這樣其他模塊可以使用本模塊中的變量或函數(shù)。
(6)模塊作者等信息聲明(可選)。
4.3 模塊加載函數(shù)
Linux內(nèi)核模塊加載函數(shù)一般以_ _init標(biāo)識(shí)聲明,典型的模塊加載函數(shù)的形式如代碼清單4.2所示。
代碼清單4.2內(nèi)核模塊加載函數(shù)
1 static int __init initialization_function(void) 2 { 3 /* 初始化代碼 */ 4 } 5 module_init(initialization_function);
模塊加載函數(shù)必須以“module_init(函數(shù)名)”的形式被指定。它返回整型值,若初始化成功,應(yīng)返回 0。而在初始化失敗時(shí),應(yīng)該返回錯(cuò)誤編碼。在 Linux 內(nèi)核里,錯(cuò)誤編碼是一個(gè)負(fù)值,在<linux/errno.h>中定義,包含-ENODEV、-ENOMEM 之類的符號(hào)值。總是返回相應(yīng)的錯(cuò)誤編碼是種非常好的習(xí)慣,因?yàn)橹挥羞@樣,用戶程序才可以利用perror等方法把它們轉(zhuǎn)換成有意義的錯(cuò)誤信息字符串。
在Linux2.6內(nèi)核中,可以使用request_module(const char *fmt, …)函數(shù)加載內(nèi)核模塊,驅(qū)動(dòng)開(kāi)發(fā)人員可以通過(guò)調(diào)用
request_module(module_name);
或
request_module("char-major-%d-%d", MAJOR(dev), MINOR(dev));
這種靈活的方式加載其他內(nèi)核模塊。
在Linux中,所有標(biāo)識(shí)為_(kāi) _init的函數(shù)在連接的時(shí)候都放在.init.text這個(gè)區(qū)段內(nèi),此外,所有的_ _init 函數(shù)在區(qū)段.initcall.init 中還保存了一份函數(shù)指針,在初始化時(shí)內(nèi)核會(huì)通過(guò)這些函數(shù)指針調(diào)用這些_ _init函數(shù),并在初始化完成后,釋放init區(qū)段(包括.init.text、.initcall.init等)。
4.8 模塊卸載函數(shù)
Linux內(nèi)核模塊加載函數(shù)一般以_ _exit標(biāo)識(shí)聲明,典型的模塊卸載函數(shù)的形式如代碼清單4.3所示。
代碼清單4.3 內(nèi)核模塊卸載函數(shù)
1 static void __exit cleanup_function(void) 2 { 3 /* 釋放代碼 */ 4 } 5 module_exit(cleanup_function);
模塊卸載函數(shù)在模塊卸載的時(shí)候執(zhí)行,不返回任何值,必須以“module_exit(函數(shù)名)”的形式來(lái)指定。
通常來(lái)說(shuō),模塊卸載函數(shù)要完成與模塊加載函數(shù)相反的功能,如下所示。
● 若模塊加載函數(shù)注冊(cè)了XXX,則模塊卸載函數(shù)應(yīng)該注銷XXX。
● 若模塊加載函數(shù)動(dòng)態(tài)申請(qǐng)了內(nèi)存,則模塊卸載函數(shù)應(yīng)釋放該內(nèi)存。
● 若模塊加載函數(shù)申請(qǐng)了硬件資源(中斷、DMA通道、I/O端口和I/O內(nèi)存等)的占用,則模塊卸載函數(shù)應(yīng)釋放這些硬件資源。
● 若模塊加載函數(shù)開(kāi)啟了硬件,則卸載函數(shù)中一般要關(guān)閉之。
和_ _init一樣,_ _exit也可以使對(duì)應(yīng)函數(shù)在運(yùn)行完成后自動(dòng)回收內(nèi)存。實(shí)際上,_ _init和_ _exit都是宏,其定義分別為:
#define __init __attribute__ ((__section__ (".init.text")))
和
#ifdef MODULE #define __exit __attribute__ ((__section__(".exit.text"))) #else #define __exit __attribute_used__attribute__ ((__section__(".exit.text"))) #endif
數(shù)據(jù)也可以被定義為_(kāi) _initdata和_ _exitdata,這兩個(gè)宏分別為:
#define __initdata __attribute__ ((__section__ (".init.data")))
和
#define __exitdata __attribute__ ((__section__(".exit.data")))
4.5 模塊參數(shù)
我們可以用“module_param(參數(shù)名,參數(shù)類型,參數(shù)讀/寫權(quán)限)”為模塊定義一個(gè)參數(shù),例如下列代碼定義了1個(gè)整型參數(shù)和1個(gè)字符指針參數(shù):
static char *book_name = " dissecting Linux Device Driver "; static int num = 4 000; module_param(num, int, S_IRUGO); module_param(book_name, charp, S_IRUGO);
在裝載內(nèi)核模塊時(shí),用戶可以向模塊傳遞參數(shù),形式為“insmode(或modprobe)模塊名 參數(shù)名=參數(shù)值”,如果不傳遞,參數(shù)將使用模塊內(nèi)定義的缺省值。
參數(shù)類型可以是byte、short、ushort、int、uint、long、ulong、charp(字符指針)、bool或invbool (布爾的反),在模塊被編譯時(shí)會(huì)將module_param中聲明的類型與變量定義的類型進(jìn)行比較,判斷是否一致。
模塊被加載后,在/sys/module/目錄下將出現(xiàn)以此模塊名命名的目錄。當(dāng)“參數(shù)讀/寫權(quán)限”為 0時(shí),表示此參數(shù)不存在sysfs文件系統(tǒng)下對(duì)應(yīng)的文件節(jié)點(diǎn),如果此模塊存在“參數(shù)讀/寫權(quán)限”不為0的命令行參數(shù),在此模塊的目錄下還將出現(xiàn)parameters目錄,包含一系列以參數(shù)名命名的文件節(jié)點(diǎn),這些文件的權(quán)限值就是傳入module_param()的“參數(shù)讀/寫權(quán)限”,而文件的內(nèi)容為參數(shù)的值。
除此之外,模塊也可以擁有參數(shù)數(shù)組,形式為“module_param_array(數(shù)組名,數(shù)組類型,數(shù)組長(zhǎng),參數(shù)讀/寫權(quán)限)”。從2.6.0~2.6.10版本,需將數(shù)組長(zhǎng)變量名賦給“數(shù)組長(zhǎng)”,從2.6.10 版本開(kāi)始,需將數(shù)組長(zhǎng)變量的指針賦給“數(shù)組長(zhǎng)”,當(dāng)不需要保存實(shí)際輸入的數(shù)組元素個(gè)數(shù)時(shí),可以設(shè)置“數(shù)組長(zhǎng)”為NULL。
運(yùn)行insmod或modprobe命令時(shí),應(yīng)使用逗號(hào)分隔輸入的數(shù)組元素。
現(xiàn)在我們定義一個(gè)包含兩個(gè)參數(shù)的模塊(如代碼清單4.4,位于虛擬機(jī)/home/lihacker/develop/svn/ldd6410-read-only/training/kernel/drivers/param目錄),并觀察模塊加載時(shí)被傳遞參數(shù)和不傳遞參數(shù)時(shí)的輸出。
代碼清單4.4帶參數(shù)的內(nèi)核模塊
1 #include <linux/init.h> 2 #include <linux/module.h> 3 MODULE_LICENSE("Dual BSD/GPL"); 4 5 static char *book_name = "dissecting Linux Device Driver"; 6 static int num = 4 000; 7 8 static int book_init(void) 9 { 10 printk(KERN_INFO " book name:%s\n",book_name); 11 printk(KERN_INFO " book num:%d\n",num); 12 return 0; 13 } 14 static void book_exit(void) 15 { 16 printk(KERN_INFO " Book module exit\n "); 17 } 18 module_init(book_init); 19 module_exit(book_exit); 20 module_param(num, int, S_IRUGO); 21 module_param(book_name, charp, S_IRUGO); 22 23 MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); 24 MODULE_DESCRIPTION("A simple Module for testing module params"); 25 MODULE_VERSION("V1.0");
對(duì)上述模塊運(yùn)行“insmod book.ko”命令加載,相應(yīng)輸出都為模塊內(nèi)的默認(rèn)值,通過(guò)查看“/var/log/messages”日志文件可以看到內(nèi)核的輸出:
[root@localhost driver_study]# tail -n 2 /var/log/messages Jul 2 01:03:10 localhost kernel: <6> book name:dissecting Linux Device Driver Jul 2 01:03:10 localhost kernel: book num:4 000
當(dāng)用戶運(yùn)行“insmod book.ko book_name='GoodBook' num=5000”命令時(shí),輸出的是用戶傳遞的參數(shù):
[root@localhost driver_study]# tail -n 2 /var/log/messages Jul 2 01:06:21 localhost kernel: <6> book name:GoodBook Jul 2 01:06:21 localhost kernel: book num:5 000
2.4 導(dǎo)出符號(hào)
Linux2.6的“/proc/kallsyms”文件對(duì)應(yīng)著內(nèi)核符號(hào)表,它記錄了符號(hào)以及符號(hào)所在的內(nèi)存地址。模塊可以使用如下宏導(dǎo)出符號(hào)到內(nèi)核符號(hào)表:
EXPORT_SYMBOL(符號(hào)名); EXPORT_SYMBOL_GPL(符號(hào)名);
導(dǎo)出的符號(hào)將可以被其他模塊使用,使用前聲明一下即可。EXPORT_SYMBOL_GPL()只適用于包含GPL許可權(quán)的模塊。代碼清單4.5給出了一個(gè)導(dǎo)出整數(shù)加、減運(yùn)算函數(shù)符號(hào)的內(nèi)核模塊的例子(這些導(dǎo)出符號(hào)毫無(wú)實(shí)際意義,僅僅是為了演示)。
代碼清單4.5 內(nèi)核模塊中的符號(hào)導(dǎo)出
1 #include <linux/init.h> 2 #include <linux/module.h> 3 MODULE_LICENSE("Dual BSD/GPL"); 4 5 int add_integar(int a,int b) 6 { 7 return a+b; 8 } 9 10 int sub_integar(int a,int b) 11 { 12 return a-b; 13 } 14 15 EXPORT_SYMBOL(add_integar); 16 EXPORT_SYMBOL(sub_integar);
從“/proc/kallsyms”文件中找出add_integar、sub_integar相關(guān)信息:
[root@localhost driver_study]# cat /proc/kallsyms | grep integar c886f050 r __kcrctab_add_integar [export] c886f058 r __kstrtab_add_integar [export] c886f070 r __ksymtab_add_integar [export] c886f054 r __kcrctab_sub_integar [export] c886f064 r __kstrtab_sub_integar [export] c886f078 r __ksymtab_sub_integar [export] c886f000 T add_integar [export] c886f00b T sub_integar [export] 13db98c9 a __crc_sub_integar [export] e1626dee a __crc_add_integar [export]
4.7 模塊聲明與描述
在 Linux 內(nèi)核模塊中,我們可以用 MODULE_AUTHOR、MODULE_DESCRIPTION、MODULE_VERSION、MODULE_DEVICE_TABLE、MODULE_ALIAS分別聲明模塊的作者、描述、版本、設(shè)備表和別名,例如:
MODULE_AUTHOR(author); MODULE_DESCRIPTION(description); MODULE_VERSION(version_string); MODULE_DEVICE_TABLE(table_info); MODULE_ALIAS(alternate_name);
對(duì)于USB、PCI等設(shè)備驅(qū)動(dòng),通常會(huì)創(chuàng)建一個(gè)MODULE_DEVICE_TABLE,表明該驅(qū)動(dòng)模塊所支持的設(shè)備,如代碼清單4.6所示。
代碼清單4.6 驅(qū)動(dòng)所支持的設(shè)備列表
1 /* 對(duì)應(yīng)此驅(qū)動(dòng)的設(shè)備表 */ 2 static struct usb_device_id skel_table [] = { 3 { USB_DEVICE(USB_SKEL_VENDOR_ID, 4 USB_SKEL_PRODUCT_ID) }, 5 { } /* 表結(jié)束 */ 6 }; 7 8 MODULE_DEVICE_TABLE (usb, skel_table);
此時(shí),并不需要讀者理解MODULE_DEVICE_TABLE的作用,后續(xù)相關(guān)章節(jié)會(huì)有詳細(xì)介紹。
4.8 模塊的使用計(jì)數(shù)
Linux2.4內(nèi)核中,模塊自身通過(guò)MOD_INC_USE_COUNT、MOD_DEC_USE_COUNT宏來(lái)管理自己被使用的計(jì)數(shù)。
Linux2.6內(nèi)核提供了模塊計(jì)數(shù)管理接口try_module_get(&module)和module_put (&module),從而取代Linux2.4內(nèi)核中的模塊使用計(jì)數(shù)管理宏。模塊的使用計(jì)數(shù)一般不必由模塊自身管理,而且模塊計(jì)數(shù)管理還考慮了SMP與PREEMPT機(jī)制的影響。
int try_module_get(struct module *module);
該函數(shù)用于增加模塊使用計(jì)數(shù);若返回為 0,表示調(diào)用失敗,希望使用的模塊沒(méi)有被加載或正在被卸載中。
void module_put(struct module *module);
該函數(shù)用于減少模塊使用計(jì)數(shù)。
try_module_get ()與module_put()的引入與使用與Linux2.6內(nèi)核下的設(shè)備模型密切相關(guān)。Linux2.6內(nèi)核為不同類型的設(shè)備定義了struct module *owner域,用來(lái)指向管理此設(shè)備的模塊。當(dāng)開(kāi)始使用某個(gè)設(shè)備時(shí),內(nèi)核使用try_module_get(dev->owner)去增加管理此設(shè)備的owner模塊的使用計(jì)數(shù);當(dāng)不再使用此設(shè)備時(shí),內(nèi)核使用 module_put(dev->owner)減少對(duì)管理此設(shè)備的owner 模塊的使用計(jì)數(shù)。這樣,當(dāng)設(shè)備在使用時(shí),管理此設(shè)備的模塊將不能被卸載。只有當(dāng)設(shè)備不再被使用時(shí),模塊才允許被卸載。
在Linux2.6內(nèi)核下,對(duì)于設(shè)備驅(qū)動(dòng)工程師而言,很少需要親自調(diào)用 try_module_get()與module_put(),因?yàn)榇藭r(shí)開(kāi)發(fā)人員所寫的驅(qū)動(dòng)通常為支持某具體設(shè)備的owner模塊,對(duì)此設(shè)備owner模塊的計(jì)數(shù)管理由內(nèi)核里更底層的代碼如總線驅(qū)動(dòng)或是此類設(shè)備共用的核心模塊來(lái)實(shí)現(xiàn),從而簡(jiǎn)化了設(shè)備驅(qū)動(dòng)開(kāi)發(fā)。
4.9 模塊的編譯
我們可以為代碼清單4.1的模板編寫一個(gè)簡(jiǎn)單的Makefile:
KVERS = $(shell uname -r) # Kernel modules obj-m += hello.o # Specify flags for the module compilation. #EXTRA_CFLAGS=-g -O0 build: kernel_modules kernel_modules: make -C /lib/modules/$(KVERS)/build M=$(CURDIR) modules clean: make -C /lib/modules/$(KVERS)/build M=$(CURDIR) clean
該Makefile文件應(yīng)該與源代碼hello.c位于同一目錄,開(kāi)啟其中的EXTRA_CFLAGS=-g -O0可以得到包含調(diào)試信息的hello.ko模塊。運(yùn)行make命令得到的模塊可直接在PC上運(yùn)行。
如果一個(gè)模塊包括多個(gè).c文件(如file1.c、file2.c),則應(yīng)該以如下方式編寫Makefile:
obj-m := modulename.o modulename-objs := file1.o file2.o
4.10 使用模塊繞開(kāi)GPL
對(duì)于企業(yè)自己編寫的驅(qū)動(dòng)等內(nèi)核代碼,如果不編譯為模塊則無(wú)法繞開(kāi)GPL,編譯為模塊后企業(yè)在產(chǎn)品中使用模塊,則公司對(duì)外不再需要提供對(duì)應(yīng)的源代碼,為了使公司產(chǎn)品所使用的Linux操作系統(tǒng)支持模塊,需要完成如下工作。
● 在內(nèi)核編譯時(shí)應(yīng)該選上“可以加載模塊”,嵌入式產(chǎn)品一般不需要?jiǎng)討B(tài)卸載模塊,所以“可以卸載模塊”不用選。
● 將我們編譯的內(nèi)核模塊.ko文件應(yīng)該放置在目標(biāo)文件系統(tǒng)的相關(guān)目錄中。
● 產(chǎn)品的文件系統(tǒng)中應(yīng)該包含了支持新內(nèi)核的insmod、lsmod、rmmod 等工具,由于嵌入式產(chǎn)品中一般不需要建立模塊間依賴關(guān)系,所以 modprobe 可以不要,一般也不需要卸載模塊,所以rmmod也可以不要。
● 在使用中用戶可使用insmod命令手動(dòng)加載模塊,如insmod xxx.ko。
● 但是一般而言,產(chǎn)品在啟動(dòng)過(guò)程中應(yīng)該加載模塊,在嵌入式產(chǎn)品Linux的啟動(dòng)過(guò)程中,加載企業(yè)自己的模塊的最簡(jiǎn)單的方法是修改啟動(dòng)過(guò)程的rc腳本,增加insmod /.../xxx.ko這樣的命令。用busybox做出的文件系統(tǒng),通常修改etc/init.d/rcS文件。
4.11 總結(jié)
本章主要講解了Linux內(nèi)核模塊的概念和基本的編程方法。內(nèi)核模塊由加載/卸載函數(shù)、功能函數(shù)以及一系列聲明組成,它可以被傳入?yún)?shù),也可以導(dǎo)出符號(hào)供其他模塊使用。
由于Linux設(shè)備驅(qū)動(dòng)以內(nèi)核模塊的形式而存在,因此,掌握這一章的內(nèi)容是編寫任何類型設(shè)備驅(qū)動(dòng)的必須。在具體的設(shè)備驅(qū)動(dòng)開(kāi)發(fā)中,將驅(qū)動(dòng)編譯為模塊也有很強(qiáng)的工程意義,因?yàn)槿绻麑⒄陂_(kāi)發(fā)中的驅(qū)動(dòng)直接編譯入內(nèi)核,而開(kāi)發(fā)過(guò)程中會(huì)不斷修改驅(qū)動(dòng)的代碼,則需要不斷地編譯內(nèi)核并重啟Linux,但是如果編譯為模塊,則只需要rmmod并insmod即可,開(kāi)發(fā)效率大為提高。
- 嵌入式Linux開(kāi)發(fā)技術(shù)
- Containerization with LXC
- 每天5分鐘玩轉(zhuǎn)Kubernetes
- Windows Vista基礎(chǔ)與應(yīng)用精品教程
- Ansible權(quán)威指南
- WindowsServer2012Hyper-V虛擬化部署與管理指南
- 精解Windows 8
- 新手易學(xué):系統(tǒng)安裝與重裝
- 奔跑吧 Linux內(nèi)核(入門篇)
- 8051軟核處理器設(shè)計(jì)實(shí)戰(zhàn)
- Linux命令行大全(第2版)
- μC/OS-III內(nèi)核實(shí)現(xiàn)與應(yīng)用開(kāi)發(fā)實(shí)戰(zhàn)指南:基于STM32
- Linux 從入門到項(xiàng)目實(shí)踐(超值版)
- Drupal 7 Cookbook
- OpenVZ Essentials