您的內(nèi)核必須已經(jīng)啟用這些選項進行了編譯:
Loadable module support --->
[*] Enable loadable module support
[*] Module unloading
[ ] Module versioning support (EXPERIMENTAL)
[*] Automatic kernel module loading
|
如果按照第一篇教程中的說明編譯內(nèi)核,那么就已經(jīng)正確地設(shè)置了這些選項。否則,修改這些選項,重新編譯內(nèi)核,并引導(dǎo)到新內(nèi)核。
一個簡單的模塊骨架
首先,找到編譯當前 Linux 內(nèi)核的源代碼。將目錄切換到 Linux 源代碼目錄中的 drivers/misc/
?,F(xiàn)在,拷貝下面的代碼并將其粘貼到一個名為 mymodule.c
的文件:
#include <linux/module.h>
#include <linux/config.h>
#include <linux/init.h>
static int __init mymodule_init(void)
{
printk ("My module worked!\n");
return 0;
}
static void __exit mymodule_exit(void)
{
printk ("Unloading my module.\n");
return;
}
module_init(mymodule_init);
module_exit(mymodule_exit);
MODULE_LICENSE("GPL");
|
保存這個文件,并在同一目錄下編輯 Makefile
文件。添加這一行:
編譯模塊:
# make -C <top directory of your kernel source> SUBDIRS=$PWD modules
|
使用 insmod ./mymodule.ko
加載這個模塊,并查看是否打印了您的消息: dmesg | tail
。應(yīng)該會在輸出的結(jié)束處看到:
現(xiàn)在刪除內(nèi)核模塊:rmmod mymodule
。再次查看 dmesg;應(yīng)該會看到:
這樣您就已經(jīng)編寫并運行了一個新的內(nèi)核模塊!恭喜!
模塊/內(nèi)核接口
現(xiàn)在,我們來做一些與您的模塊有關(guān)的更有趣的事情。要了解的一個關(guān)鍵內(nèi)容是,模塊只能“看到”內(nèi)核故意讓它訪問的函數(shù)和變量。首先,我們以錯誤的方式來進行嘗試。
編輯文件 kernel/printk.c
,在所有包含文件之后其他全局變量聲明附近(但要在所有函數(shù)之外)添加下面一行:
現(xiàn)在重新編譯內(nèi)核并引導(dǎo)到新內(nèi)核。然后,將下面的內(nèi)容添加到模塊的 mymodule_init
函數(shù)起始處,置于其他代碼之前。
extern int my_variable;
printk ("my_variable is %d\n", my_variable);
my_variable++;
|
保存修改并重新編譯模塊:
# make -C <top directory of your kernel source> SUBDIRS=$PWD modules
|
加載模塊(這將失?。?code>insmod ./mymodule.ko。模塊的加載會失敗,并給出消息:
insmod: error inserting './mymodule.ko': -1 Unknown symbol in module
|
這說明內(nèi)核不允許模塊訪問那個變量。當模塊加載時,它必須解析所有外部引用,比如函數(shù)名或者變量名。如果它不能找到內(nèi)核導(dǎo)出的符號列表中所有未解析的名稱,那么模塊就不能寫入那個變量或者調(diào)用那個函數(shù)。在內(nèi)核中某個地方有為變量 my_variable
分配的空間,但模塊不知道是哪里。
為解決此問題,我們將把 my_variable
添加到內(nèi)核導(dǎo)出的符號列表中。在很多內(nèi)核目錄中,都有一個特定的文件,用于導(dǎo)出在那個目錄中定義的符號。再次打開 kernel/printk.c
文件,在變量聲明之后添加下面一行:
EXPORT_SYMBOL(my_variable);
|
重新編譯并重新引導(dǎo)到新內(nèi)核?,F(xiàn)在再一次嘗試加載模塊:insmod ./mymodule.ko
。這一次,當查看 dmesg 時,應(yīng)該看到:
my_variable is 0
My module worked!
|
重新加載模塊:
# rmmod mymodule && insmod ./mymodule.ko
|
現(xiàn)在應(yīng)該看到:
Unloading my module.
my_variable is 1
My module worked!
|
每次重新加載那個模塊,my_variable
都會增 1。您正在讀寫一個在主內(nèi)核中定義的變量。只要被 EXPORT_SYMBOL()
顯式地聲明,模塊就可以訪問主內(nèi)核中的任何變量。例如,函數(shù) printk()
是在內(nèi)核中定義的,并且在文件 kernel/printk.c
中被導(dǎo)出。
簡單的可引導(dǎo)內(nèi)核模塊是用來研究內(nèi)核的一個有趣的途徑。例如,可以使用一個模塊來打開或關(guān)閉 printk
,方法是在內(nèi)核中定義一個變量 do_print
(它初始化為 0)。然后,讓所有 printk
都依賴于“do_print
”:
if (do_print) {
printk ("Big long obnoxious message\n");
}
|
然后,只有當您的模塊被加載時才打開它。
模塊參數(shù)
引導(dǎo)模塊時,可以向它傳遞參數(shù)。要使用模塊參數(shù)加載模塊,這樣寫:
insmod module.ko [param1=value param2=value ...]
|
為了使用這些參數(shù)的值,要在模塊中聲明變量來保存它們,并在所有函數(shù)之外的某個地方使用宏 MODULE_PARM(variable, type)
和 MODULE_PARM_DESC(variable, description)
來接收它們。type
參數(shù)應(yīng)該是一個格式為 [min[-max]]{b,h,i,l,s}
字符串,其中 min 和 max 是數(shù)組的長度限度。如果兩者都忽略了,則默認為 1。最后一個字符是類型說明符:
b byte
h short
i int
l long
s string
|
可以在 MODULE_PARM_DESC
的 description
域中添加任何需要的說明符。