歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux綜合 >> Linux資訊 >> 更多Linux >> 編寫簡單的Linux2.6內核模塊(1)

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

日期:2017/2/27 9:41:19   编辑:更多Linux

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

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 0 My module worked! 重新加載模塊:

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

Unloading my module. my_variable is 1 My 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 byte h short i int l long s string

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

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

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 0 My module worked! 重新加載模塊:

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

Unloading my module. my_variable is 1 My 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 byte h short i int l long s string

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




Copyright © Linux教程網 All Rights Reserved