內核模組是動態載入的內核代碼,運行在內核態(Ring 0),具有完全硬件訪問權限。本文從最小模組入手,涵蓋 Makefile、編譯載入、初始化/清理、用戶態交互、符號導出、版本依賴等核心知識點。
#include
#include
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Developer");
MODULE_DESCRIPTION("Minimal kernel module");
static int __init hello_init(void) {
printk(KERN_ALERT "HELLO WORLD\n");
return 0; // 非 0 表示初始化失敗,模組不會被載入
}
static void __exit hello_exit(void) {
printk(KERN_ALERT "GoodBye\n");
}
module_init(hello_init);
module_exit(hello_exit);
__init:標記初始化函數,載入後釋放其代碼段內存(.init.text 區)__exit:標記清理函數,僅在模組移除時調用,內建內核時被優化掉printk:內核打印,級別 KERN_ALERT 等,輸出至內核環形緩衝區(dmesg 查看)MODULE_LICENSE:聲明許可證,專有許可會污染內核(tainted),影響技術支援obj-m := hello.o
KDIR := /lib/modules/$(shell uname -r)/build
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
rm -f *.ko *.o *.mod.* *.symvers *.order
機制:KERNELRELEASE 變量區分內核樹建構 vs 外部建構。modules 目標觸發模組編譯並生成 .ko。
# 編譯(需內核頭文件包:kernel-devel / linux-headers-$(uname -r))
make
# 載入(需 root)
insmod hello.ko
dmesg -T | tail -2 # 查看輸出
# 列出已載入模組
lsmod | grep hello
# 卸載
rmmod hello
dmesg -T | tail -1
static char *whom = "world";
static int count = 1;
module_param(whom, charp, 0644);
module_param(count, int, 0644);
MODULE_PARM_DESC(whom, "Greeting target");
MODULE_PARM_DESC(count, "Repeat count");
static int __init hello_init(void) {
for (int i = 0; i < count; i++)
printk(KERN_ALERT "Hello, %s!\n", whom);
return 0;
}
載入時傳參:insmod hello.ko whom="kernel" count=3
// 導出函數/變量供其他模組使用
EXPORT_SYMBOL(my_function);
EXPORT_SYMBOL_GPL(my_gpl_function); // 僅 GPL 模組可用
依賴關係:modprobe 會自動解析 modules.dep 載入依賴模組;insmod 不會。
vermagic.o,嵌入內核版本、GCC 版本、配置選項(CONFIG_SMP、CONFIG_PREEMPT 等)modinfo hello.ko | grep vermagic 查看insmod --force 或修改 vermagic 字符串| 特性 | 用戶態應用 | 內核模組 |
|---|---|---|
| 地址空間 | 獨立虛擬地址空間 | 共享內核地址空間 |
| 庫函數 | glibc 等 | 僅內核導出符號(無 libc) |
| 錯誤處理 | 段錯誤僅殺死進程 | Oops/Panic 可致系統崩潰 |
| 併發 | 進程/線程 | 中斷、軟中斷、多 CPU 並行 |
| 調試 | GDB、strace | kgdb、ftrace、printk、crash |