Linux 核心/模組
核心模組是可以在需要時載入到核心映像中的程式碼,而無需使用者重新構建核心或重啟計算機。模組化設計確保您不必建立包含所有硬體和情況所需程式碼的單片核心。
常見的核心模組是裝置驅動程式,它們直接訪問計算機和外設硬體。
從核心 3.0 開始,核心模組的副檔名為 .ko。
要確定已載入的模組,請執行 lsmod 命令,該命令讀取檔案 /proc/modules。以下是 lsmod 的示例輸出
Module Size Used by ctr 13049 1 ccm 17773 1 snd_hda_codec_hdmi 46368 1 snd_hda_codec_cirrus 18855 1 btusb 32412 0 hid_generic 12548 0 arc4 12608 2 b43 387371 0 mac80211 630669 1 b43 hid_apple 13386 0 bcm5974 17589 0 joydev 17381 0 cfg80211 484040 2 b43,mac80211 ssb 62379 1 b43 hid_holtek_mouse 12625 0
請注意,lsmod 列印的輸出通常很長,並且包含列出的多個核心模組。出於顯而易見的原因,此示例已縮短。
為了將模組新增到核心,您可以使用 modprobe 命令。核心模組守護程序 kmod 通常執行 modprobe 以載入模組。Modprobe 傳遞一個字串引數,該引數可以是實際的模組名稱或模組的別名。Modprode 搜尋 /proc/modprobe.conf 以將模組別名與實際模組關聯。為了確定模組依賴關係,modprobe 搜尋 /lib/module/version/modules.dep 並確定在載入請求的模組之前是否必須載入其他模組。modules.dep 檔案使用 depmod -a 命令建立,該命令確定特定模組是否呼叫其他模組定義的函式或變數(符號)。
如果有依賴項,insmod 會在載入請求的模組之前載入它們。雖然 insmod 需要有關請求模組的路徑和檔名的詳細說明,但 modprobe 不需要。以下是如何使用 "spidev.ko" 的示例
sudo modprobe spidev
Modprobe、insmod 和 depmod 在 module-init-tools 或 kmod 包中找到。
已載入的模組列在 /proc/modules 中。使用 modinfo modulename(針對檔案 modulename.ko)來確定模組資訊。以下是一個示例
modinfo spidev
請注意,您不能使用 X(例如 xterm),因為核心只會將目錄列印到控制檯或日誌檔案。
最簡單的核心模組
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");
static int hello_init(void)
{
printk(KERN_ALERT "Hello, world\n");
return 0;
}
static void hello_exit(void)
{
printk(KERN_ALERT "Goodbye, cruel world\n");
}
module_init(hello_init);
module_exit(hello_exit);
核心模組必須始終包含這兩個函式:init_module 和 cleanup_module。Insmod 在將模組載入到核心時呼叫 init_module,而 rmsmod 在刪除模組時呼叫 cleanup_module。printk() 是核心使用的日誌宏,並分配了 <1> 的優先順序。核心優先順序共有八個,在 kernel.h 中定義。每個核心優先順序都有其特定的預期含義和相關定義
43 #define KERN_EMERG "<0>" /* system is unusable */
44 #define KERN_ALERT "<1>" /* action must be taken immediately */
45 #define KERN_CRIT "<2>" /* critical conditions */
46 #define KERN_ERR "<3>" /* error conditions */
47 #define KERN_WARNING "<4>" /* warning conditions */
48 #define KERN_NOTICE "<5>" /* normal but significant condition */
49 #define KERN_INFO "<6>" /* informational */
50 #define KERN_DEBUG "<7>" /* debug-level messages */
在使用 printk() 時,如果 syslogd 和 klogd 正在執行,printk() 的輸出將追加到 /var/log/messages。如果以低優先順序呼叫 printk(),它只會追加到 /var/log/messages。為了確保它列印到控制檯,請使用 KERN_ALERT 優先順序。
核心模組的編譯方式與使用者空間程式碼不同。以下是一個示例 makefile
obj-m += hello.o all: modules .DEFAULT: $(MAKE) -C $(KDIR) M=$$PWD $@
執行 make 命令來編譯你的簡單模組。
使用 module_init() 和 module_exit() 宏,您可以更改 init_module() 和 cleanup_module(void) 的名稱。
__init 宏在內建驅動程式(例如)載入後釋放模組中初始化函式的記憶體。但是,這在使用 modprobe 時不會發生。
__exit 宏可以防止在初始化期間載入 cleanup_module,因為它在核心使用期間不需要在核心中。這些宏可以在 linux/init.h 中找到,對管理記憶體非常有用。__initdata 與 __init 相似,但用於變數。
MODULE_LICENSE() 宏允許您確定程式碼的許可證,併為您的模組定義它。
MODULE_PARM() 允許您在 insmod 初始化模組時傳遞命令列引數。
為了跨多個檔案擴充套件模組,必須更改 make 檔案。
obj-m += hello.o obj-m += startstop.o startstop-objs := start.o stop.o default: modules .DEFAULT: $(MAKE) -C $(KDIR) M=$$PWD $@
% make make[1]: Entering directory `/usr/src/linux-2.6.10' CC [M] /home/ldd3/src/misc-modules/hello.o Building modules, stage 2. MODPOST CC /home/ldd3/src/misc-modules/hello.mod.o LD [M] /home/ldd3/src/misc-modules/hello.ko make[1]: Leaving directory `/usr/src/linux-2.6.10' % su root# insmod ./hello.ko Hello, world root# rmmod hello Goodbye cruel world root#
可用於模組的符號可以在 /proc/kallsyms 中找到。
可以編寫模組來替換核心的系統呼叫。
要檢視使用者空間程序執行了哪些系統呼叫,請使用 **strace** 執行該程序。
CPU 有不同的模式,80386 有四個。有關環的描述,請參閱維基百科環條。
參考資料