linux核心有一個很強大的功能就是可以動態載入模組兒,這裡說的模組兒其實就是一個核心支援的小程式。在真正進入linux核心世界之前,我們可以先過一把癮,自己載入運行一個核心模組兒。
既然模組兒就是程式,我們就先來寫一個小程式,這裡還是拿最簡單的“Hello World”程式來舉例子。我們先上代碼,然後再講解。
#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>MODULE_LICENSE("GPL");static int __init lkp_init(void){printk("<1>Hello World!...from the kernel!\n");}static int __exit lkp_exit(void){printk("<1>Goodbye World!...from the kernel!\n");}module_init(lkp_init);module_exit(lkp_exit);
首先是三個標頭檔,和以前看到的標頭檔不同的是它們都是linux目錄下的,那麼這個目錄在哪兒呢。在ubuntu的系統中,它被放在/usr/src/linux-headers-x.x.x.-xx/include/linux/這個目 錄下,有興趣的小夥伴可以去看一下,這裡只做個簡單的介紹:module.h裡放的是對模組兒的版本控制資訊,後面的宏定義MODULE_LICENSE就是在這裡面定義的;kernel.h裡放的是常用的核心功能,比如這裡的printk;init.h裡放的是宏__init(注意這裡是兩個底線)、__exit和module_init、module_exit。
下面的宏定義MODULE_LICENSE("GPL");是告訴核心,我們編寫的這這個模組兒是符合GNU通用公用許可證的,簡單來說就是合法的。沒有這句系統會給你你一個警告。
再下面是兩個函數,static和int屬於基本知識,就不多說了,後面的__init和__exit是兩個宏定義,告訴系統你寫的程式比較特殊,可以特殊處理一下。這主要是為了一些效能最佳化,其實不特殊處理也沒關係。這兩個函數都定義了一個printk函數,它來自核心,和printf用法類似,不過它會把內容輸出到一個特定的檔案內,而非標準輸出,這個我們一會兒會看到。
最後也是兩個宏定義,是這段代碼最關鍵的地方。第一個宏定義可以被看做我們的小模組兒的入口,其實就是告訴系統模組執行時要先執行lkp_init這個函數,而另一個類似,代表模組的出口。它們的內部其實是由回呼函數完成的,這裡不詳細講了。
程式寫好以後,我們可以給它起個名字儲存起來,這裡我們叫它hello.c。
程式有了,我們還需要一個指導編譯的規則,就是Makefile檔案,同樣的,我們還是先來看代碼。
obj_m:=hello.oCURRENT_PATH := $(shell pwd)LINUX_KERNEL := $(shell uname -r)LINUX_KERNEL_PAHT := /usr/src/linux-headers-(LINUX_KERNEL)all:make -C $(LINUX_KERNEL_PAHT) M=$(CURRENT_PATH) modulesclean:make -C $(LINUX_KERNEL_PAHT) M=$(CURRENT_PATH) clean
makefile的文法有很多教程,這裡也不多說,關鍵說一下-C與M=的作用,這兩點在編譯核心中非常常見:當make的目標為all時,-C $(KDIR) 指明跳轉到核心源碼目錄下讀取那裡的Makefile;M=$(PWD) 表明然後返回到目前的目錄繼續讀入、執行當前的Makefile。還有最後面的modules和clean為固定項,不能改變。
寫完Makefile儲存,然後執行make命令,可以產生很多個檔案,其中一個尾碼名為.ko的檔案是我們需要插入核心的,在將其插入之前,我們先來看一下當前核心中已有的模組:執行命令# lsmod可以看到當前系統中的核心模組。然後執行命令# insmod hello.ko就可以把我們的模組插進入了,再執行一下# lsmod可以看到我們的模組已經插入了(一般第一個就是剛插入的)。這時候我們可以用命令#dmesg 來查看剛才插入模組的輸出(一般是在最後面)。好了,這說明我們的模組兒已經可以運行了。當然,既然是隨時可插拔的,也可以把它卸載掉。執行命令# rmmod hello就可以把它卸載了,再# dmesg看一下,可以看到程式退出時輸出的那句話。
以上,我們已經完成了核心模組的編寫和載入。