歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Unix知識 >> 關於Unix

編寫簡單的Linux2.6內核模塊(1)

您的內核必須已經啟用這些選項進行了編譯: Loadable module support --- [*] Enable loadable module support [*] Module unloading [ ] Module versioning support (E XP ERIMENTAL) [*] Automatic kernel module loading 如果按照第一篇教程中的說明編譯  

您的內核必須已經啟用這些選項進行了編譯:

Loadable module support  --->  [*] Enable loadable module support  [*]   Module unloading  [ ]   Module versioning support (EXPERIMENTAL)  [*]   Automatic kernel module loading

如果按照第一篇教程中的說明編譯內核,那麼就已經正確地設置了這些選項。否則,修改這些選項,重新編譯內核,並引導到新內核。

一個簡單的模塊骨架

首先,找到編譯當前 Linux 內核的源代碼。將目錄切換到 Linux 源代碼目錄中的 drivers/misc/。現在,拷貝下面的代碼並將其粘貼到一個名為 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 文件。添加這一行:

obj-m += mymodule.o
編譯模塊:

# make -C <top directory of your kernel source> SUBDIRS=$PWD modules
使用 insmod ./mymodule.ko 加載這個模塊,並查看是否打印了您的消息: dmesg | tail。應該會在輸出的結束處看到:

My module worked!
現在刪除內核模塊:rmmod mymodule。再次查看 dmesg;應該會看到:

Unloading my module.
這樣您就已經編寫並運行了一個新的內核模塊!恭喜!

模塊/內核接口

現在,我們來做一些與您的模塊有關的更有趣的事情。要了解的一個關鍵內容是,模塊只能“看到”內核故意讓它訪問的函數和變量。首先,我們以錯誤的方式來進行嘗試。

編輯文件 kernel/printk.c,在所有包含文件之後其他全局變量聲明附近(但要在所有函數之外)添加下面一行:

int my_variable = 0;
現在重新編譯內核並引導到新內核。然後,將下面的內容添加到模塊的 mymodule_init 函數起始處,置於其他代碼之前。

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
加載模塊(這將失敗):insmod ./mymodule.ko。模塊的加載會失敗,並給出消息:

insmod: error inserting './mymodule.ko': -1 Unknown symbol in module
這說明內核不允許模塊訪問那個變量。當模塊加載時,它必須解析所有外部引用,比如函數名或者變量名。如果它不能找到內核導出的符號列表中所有未解析的名稱,那麼模塊就不能寫入那個變量或者調用那個函數。在內核中某個地方有為變量 my_variable 分配的空間,但模塊不知道是哪裡。

為解決此問題,我們將把 my_variable 添加到內核導出的符號列表中。在很多內核目錄中,都有一個特定的文件,用於導出在那個目錄中定義的符號。再次打開 kernel/printk.c 文件,在變量聲明之後添加下面一行:

EXPORT_SYMBOL(my_variable);
重新編譯並重新引導到新內核。現在再一次嘗試加載模塊:insmod ./mymodule.ko。這一次,當查看 dmesg 時,應該看到:

my_variable is 0My module worked!
重新加載模塊:

# rmmod mymodule && insmod ./mymodule.ko
現在應該看到:

Unloading my module.my_variable is 1My module worked!
每次重新加載那個模塊,my_variable 都會增 1。您正在讀寫一個在主內核中定義的變量。只要被 EXPORT_SYMBOL() 顯式地聲明,模塊就可以訪問主內核中的任何變量。例如,函數 printk() 是在內核中定義的,並且在文件 kernel/printk.c 中被導出。

簡單的可引導內核模塊是用來研究內核的一個有趣的途徑。例如,可以使用一個模塊來打開或關閉 printk,方法是在內核中定義一個變量 do_print(它初始化為 0)。然後,讓所有 printk 都依賴於“do_print”:

if (do_print) {    printk ("Big long obnoxious message\n");}
然後,只有當您的模塊被加載時才打開它。

模塊參數

引導模塊時,可以向它傳遞參數。要使用模塊參數加載模塊,這樣寫:

insmod module.ko [param1=value param2=value ...]
為了使用這些參數的值,要在模塊中聲明變量來保存它們,並在所有函數之外的某個地方使用宏 MODULE_PARM(variable, type)MODULE_PARM_DESC(variable, description) 來接收它們。type 參數應該是一個格式為 [min[-max]]{b,h,i,l,s} 字符串,其中 min 和 max 是數組的長度限度。如果兩者都忽略了,則默認為 1。最後一個字符是類型說明符:

b       byteh       shorti       intl       longs       string

可以在 MODULE_PARM_DESCdescription 域中添加任何需要的說明符。

編寫使用中斷的模塊

現在我們將編寫一個模塊,其中有一個函數,當內核接收到某個 IRQ 上的一個中斷時會調用它。首先,將文件 mymodule.c 拷貝到 myirqtest.c,然後刪除函數的內容,只保留返回語句。在編輯器中打開 myirqtest.c,並使用“myirqtest”替換所出現的“mymodule”來修改函數名。另外刪除 printk。為了能夠使用中斷,將下面一行:

#include <linux/interrupt.h>


加入到文件的頂部。

使用 cat /proc/interrupts 找出正在使用的中斷。第一列顯示出正在使用的中斷號,第二列是機器自最後一次引導後在那個 IRQ 上發行了多少次中斷,第三列是使用這個 IRQ 的設備。在這個示例中,我們將研究來自網絡接口的中斷,並使用兩個模塊參數 interfaceirq 來指明我們要使用的接口和 IRQ 行。

為了使用模塊參數,要聲明兩個變量來存放它們,並

Copyright © Linux教程網 All Rights Reserved