大多數(shù)的Linux驅(qū)動(dòng)程序救欧,都以內(nèi)核模塊的形式,運(yùn)行在Linux內(nèi)核中。
內(nèi)核模塊可以通過insmod/rmmod命令加載/卸載难衰。此過程中不需要重啟動(dòng)任何東西,這使得調(diào)試內(nèi)核模塊非常方便逗栽。
寫一個(gè)簡單的內(nèi)核模塊
Hello.c:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
static int __init init_hello(void)
{
pr_info("Hello world\n");
return 0;
}
static void __exit cleanup_hello(void)
{
pr_info("Goodbye world\n");
}
module_init(init_hello);
module_exit(cleanup_hello);
保存hello.c到任意目錄盖袭,然后再同一目錄中創(chuàng)建Makefile文件。
Makefile:
obj-m += hello.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
注意彼宠,第4和7行中make前的縮進(jìn)是<tab>鳄虱。
確認(rèn)編譯內(nèi)核模塊的頭文件都已經(jīng)在當(dāng)前系統(tǒng)中安裝了。
運(yùn)行:
# ls /usr/src/kernels/$(shell uname -r)
如果此目錄不存在兵志,則需要安裝或下載相應(yīng)的內(nèi)核頭文件醇蝴。
例如,在我的Linux (Fedora Core 26)中想罕,我是通過下面命令安裝:
# dnf install kernel-devel
編譯:
# make
make -C /lib/modules/4.11.6-201.fc25.x86_64/build M=/opt4/foo/hello modules
make[1]: Entering directory '/usr/src/kernels/4.11.6-201.fc25.x86_64'
Building modules, stage 2.
MODPOST 1 modules
make[1]: Leaving directory '/usr/src/kernels/4.11.6-201.fc25.x86_64'
[root@euca-10-254-112-100 hello]# ls
Makefile hello.c hello.mod.c hello.o
Module.symvers hello.ko hello.mod.o modules.order
運(yùn)行:
[root@euca-10-254-112-100 hello]# insmod hello.ko
[root@euca-10-254-112-100 hello]# dmesg | tail -1
[ 2467.713120] Hello world
[root@euca-10-254-112-100 hello]# rmmod hello.ko
[root@euca-10-254-112-100 hello]# dmesg | tail -1
[ 2516.974827] Goodbye world
pr_info(...) 等同于printk(KERN_INFO...)悠栓。
內(nèi)核日志也可以通過 journalctl -k 命令查看。
內(nèi)核模塊的額外信息
可以給內(nèi)核模塊加上描述和許可:
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xxx");
MODULE_DESCRIPTION("xxxx");
在hello.c的尾部加上以上3行按价,執(zhí)行 make 命令惭适。
使用modinfo命令查看內(nèi)核模塊的額外信息:
[root@euca-10-254-112-100 hello]# modinfo hello.ko
filename: /opt4/foo/hello/hello.ko
description: xxxx
author: xxx
license: GPL
depends:
vermagic: 4.11.6-201.fc25.x86_64 SMP mod_unload
內(nèi)核模塊的啟動(dòng)參數(shù)
Hello.c:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
static char * str = "world";
module_param(str, charp, 0);
MODULE_PARM_DESC(str, "the name to say hello");
static int __init init_hello(void)
{
pr_info("Hello %s\n", str);
return 0;
}
static void __exit cleanup_hello(void)
{
pr_info("Goodbye %s\n", str);
}
module_init(init_hello);
module_exit(cleanup_hello);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xxx");
MODULE_DESCRIPTION("xxxx");
上述代碼給內(nèi)核模塊啟動(dòng)時(shí),增加了一個(gè)字符串類型的參數(shù)÷ジ洌現(xiàn)在癞志,可以跟任何人說 hello 了。
運(yùn)行:
[root@euca-10-254-112-100 hello]# insmod hello.ko str="mountain"
[root@euca-10-254-112-100 hello]# dmesg | tail -1
[ 5487.679867] Hello mountain
[root@euca-10-254-112-100 hello]# rmmod hello.ko
[root@euca-10-254-112-100 hello]# dmesg | tail -1
[ 5495.265233] Goodbye mountain