Linux 就是一般所說的單內核(monolithic kernel),即操做系統的大部分功能都被稱爲內核,並在特權模式下運行。經過 Linux 內核模塊(LKM)能夠在運行時動態地更改 Linux。Linux可加載內核模塊(從內核的 1.2 版本開始引入)是 Linux 內核的最重要創新之一。它們提供了可伸縮的、動態的內核。探索隱藏在可加載模塊後面的原理,並學習這些獨立的對象如何動態地轉換成 Linux 內核的一部分。linux
(1) linux 內核模塊的建立shell
LKM 包含 entry 和 exit 函數。當向內核插入模塊時,調用 entry 函數,從內核刪除模塊時則調用 exit 函數。由於 entry 和 exit 函數是用戶定義的,因此存在 module_init
和 module_exit
宏,用於定義這些函數屬於哪一種函數。LKM 還包含一組必要的宏和一組可選的宏,用於定義模塊的許可證、模塊的做者、模塊的描述等等,見下例。數組
#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> static int __init hello_3_init(void) { printk(KERN_ALERT "Hello, world n"); return 0; } static void __exit hello_3_exit(void) { printk(KERN_ALERT "Goodbye, world \n" ); } MODULE_LICENSE("GPL"); module_init(hello_3_init); module_exit(hello_3_exit);
(2)構建linux 內核模塊函數
編譯模塊的make file 必須是Makefile,不能是makefile,而且要生成模塊必須先編譯經過。 內核模塊的makefile以下學習
ifneq ($(KERNELRELEASE),) obj-m := mytest.o mytest-objs := file1.o else
KVER ?= $(shell uname -r)
KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: $(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions endif
1> KERNELRELEASE是在內核源碼的頂層Makefile中定義的一個變量,在第一次讀取執行此Makefile時,KERNELRELEASE沒有被定義。因此執行else以後內容。ui
2> 當執行make 時候 執行默認目標defalult。spa
$(KDIR) 指明跳轉到內核源碼目錄下讀取那裏的Makefile;M=$(PWD) 指示路徑;代表而後返回到當前目錄繼續讀入、執行當前的Makefile。操作系統
$(KVER) 要生成的模塊內核版本 當前系統版本code
3> 當從內核源碼目錄返回時,KERNELRELEASE已被被定義,kbuild也被啓動去解析kbuild語法的語句,make將繼續讀取else以前的內容。視頻
mytest-objs := file1.o 表示mytest.o 由file1.o 鏈接生成
obj-m := mytest.o 表示編譯鏈接後將生成mytest.o模塊 mytest.ko。
(3) 編譯 安裝 卸載
1> 編譯 執行make 生成mytest.ko內核模塊
2> 安裝內核模塊 sudo insmod mytest.ko
3> 查看內核模塊輸出 dmesg -c
4> 卸載內核模塊 sudo rmmod mytest.ko
注意:Linux命令dmesg用來顯示開機信息,kernel會將開機信息存儲在ring buffer中。您如果開機時來不及查看信息,可利用dmesg來查看。開機信息亦保存在/var/log目錄中,名稱爲dmesg的文件裏。
(4)內核模塊的啓動參數
像咱們普通的應用程序同樣,有main函數啓動能夠有參數,內核模塊啓動時候也可使用參數。這點很重要,如在視頻壓縮中經過最優參數達到不一樣壓縮效率。先看一個例子 咱們在解釋其中的代碼。
#include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> #define MAX_ARRAY 66 static int int_var = 0; static char *str_var = "default"; static int int_array[6]; int narr; module_param(int_var, int, 0644); MODULE_PARM_DESC(int_var, "A integer variable"); module_param(str_var,charp,0644); MODULE_PARM_DESC(str_var, "A string variable"); module_param_array(int_array, int, &narr, 0644); MODULE_PARM_DESC(int_array, "A integer array"); static int __init hello_init(void) { int i; printk(KERN_ALERT "Hello, my LKM.\n"); printk(KERN_ALERT "int_var %d.\n", int_var); printk(KERN_ALERT "str_var %s.\n", str_var); for(i = 0; i < narr; i ++) { printk("int_array[%d] = %d\n", i, int_array[i]); } return 0; } static void __exit hello_exit(void) { printk(KERN_ALERT "Bye, my LKM.\n"); } module_init(hello_init); module_exit(hello_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("This module is a example.");
方式一:在內核模塊裏,參數的用法如同全局變量。
module_param(name, type, perm);
name 用戶看到的參數名,又是模塊內接受參數的變量
type 表示參數的數據類型,下列之一:byte, short, ushort, int, uint, long, ulong, charp, bool, invbool
perm 指定了在sysfs中相應文件的訪問權限
eg:
static unsigned int int_var = 0;
module_param(int_var, uint, S_IRUGO);
方式二:模塊源文件內部的變量名與外部的參數名有不一樣的名
module_param_named(name, variable, type, perm);
name 外部可見的參數名
variable 源文件內部的全局變量名
方式三:字符串
static char *name;
module_param(name, charp, 0);
或者
static char species[BUF_LEN];
module_param_string(name, string, len, perm);
方式四:數組
static int finsh[MAX_FISH];
static int nr_fish;
module_param_array(fish, int, &nr_fish, 0444); //最終傳遞數組元素個數存在nr_fish中
編譯生成內核模塊
裝載內核模塊 sudo insmod parm.ko int_var=100 str_var=hello int_array=100,200