http://www.cnblogs.com/yuuyuu/p/5119891.htmlhtml
ZC: 疑問,最後的 模塊kernel_mod 調用 模塊kernel_fun的函數fun,是成功的OK的。可是 模塊kernel_mod 怎麼就知道 它調用的就是 模塊kernel_fun的fun函數?若是 又有一個 模塊kernel_fun01它也導出了fun函數,此時 模塊kernel_mod調用fun的話調用的是哪個模塊的fun函數?linux
(ZC: 測試了一下,兩個模塊 有相同的導出函數的話,在 加載模塊時[insmod] 會報錯:"insmod: error inserting 'kernel_fun01.ko': -1 Invalid module format",而後 用命令"dmesg |tail -n 10" 能夠看到更明確的信息:"kernel_fun01: exports duplicate symbol fun (owned by kernel_fun)"。可見 內核模塊不能導出相同名字的函數...)shell
ZC: 模塊kernel_mod 調用 函數fun,是經過 模塊所在的路徑來找 fun函數的嗎?模塊所在的路徑 找不到的話,就繼續找PATH裏面的路徑??仍是全部模塊都是在同一個特殊的路徑下??編程
ZC: 命令"insmod ???" 安裝的內核模塊,都是位於相同的地方?就是說 相同的路徑?內核模塊有沒有 相對路徑之說?函數
ZC: 不一樣路徑的模塊 能導出相同函數名的函數嗎?post
ZC: 答:這樣測試的:kernel_fun.ko 放置於"/home/DriverZ/" 而且已經安裝,將 kernel_fun01.ko 放於"/home/"下 安裝時 報錯"insmod: error inserting 'kernel_fun01.ko': -1 Invalid module format","dmesg |tail -n 10" 顯示"kernel_fun01: exports duplicate symbol fun (owned by kernel_fun)"測試
ZC: 這樣子的模塊,加載以後,在OS重啓以後 須要再次手動加載。∵上面加載 kernel_fun01.ko時 報錯,而我重啓OS以後 加載kernel_fun01.ko很順利,說明 kernel_fun.ko在OS重啓以後 沒有被自動加載。因而 有以下疑問:ui
(1)、如何使得內核模塊開機自動加載?spa
(2)、如何查看 當前已經加載了哪些內核模塊 以及它們的詳細信息? --> 貌似是 lsmod指針
ZC: 本文章的一些命令:
insmod ???.ko 參數名=參數值
rmmod ???.ko
modinfo ???.ko
lsmod
dmesg |tail -n 10
cat /proc/kallsyms | grep kernel_fun --> 顯示模塊的導出函數(kernel all symbols)
*** *** *** *** *** *** *** *** *** *** *** ***
一.前言
咱們一塊兒從3個小例子來體驗一下linux內核編程。以下:
1.內核編程之hello world
2.模塊參數傳遞
3.模塊間函數調用
二.準備工做
首先,在你的linux系統上面安裝linux頭文件,debian系列:
1 $:sudo apt-get install linux-headers-`uname -r`
安裝後,在你的/lib/modules/目錄下有你剛剛安裝的頭文件版本號對應的目錄。頭文件夾下面還有個build文件夾,裏面的Makefile文件是等會要編譯內核模塊用的。如圖,這是我機器上面的:
注意:安裝的頭文件版本必定要和你的系統版本同樣,否則你本身編寫的模塊不能插入到本機的內核。若是你apt-get安裝頭文件時,沒有對應的頭文件,或者你的源裏面放不穩定版本的源後,依然沒有對應的頭文件,你能夠到這裏搜索須要的deb包來安裝。再或者下載跟本機對應的內核源碼來構建環境。
三.內核編程之hello world
咱們先來了解下內核模塊文件的入口和出口。它由2個宏來註冊入口和出口,分別是:
1 module_init(x); 2 module_exit(x);
這2個宏在頭文件目錄的include/linux/module.h。宏裏面的x表明註冊到內核的入口和出口函數。通俗一點講就是模塊初始化和模塊卸載時調用的函數。
初始化函數的形式:int my_init(void);
退出函數的形式:void my_exit(void);
另外還有一些宏:
MODULE_LICENSE(_license):模塊的許可證。
MODULE_AUTHOR(_author):模塊的做者。
MODULE_VERSION(_version):模塊版本
MODULE_DESCRIPTION(_description):模塊的描述。
還有一些就不一一舉例了。
如今,我來看最簡單的hello world例子:
文件名:kernel_hello.c
1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/kernel.h> 4 5 /* 如下4個宏分別是許可證,做者,模塊描述,模塊版本 */ 6 MODULE_LICENSE("Dual BSD/GPL"); 7 MODULE_AUTHOR("yuuyuu"); 8 MODULE_DESCRIPTION("kernel module hello"); 9 MODULE_VERSION("1.0"); 10 11 /* 入口函數 */ 12 static int hello_init(void) 13 { 14 printk(KERN_ALERT "hello_init() start\n"); 15 16 return 0; 17 } 18 19 /* 退出函數 */ 20 static void hello_exit(void) 21 { 22 printk(KERN_ALERT "hello_exit() start\n"); 23 } 24 25 /* 註冊到內核 */ 26 module_init(hello_init); 27 module_exit(hello_exit);
上面的printk()函數時內核本身實現的輸出函數,KERN_ALERT時輸出信息的級別!
而後再寫一個很簡單的Makefile文件:
1 KERNAL_DIR ?= /lib/modules/$(shell uname -r)/build 2 PWD := $(shell pwd) 3 4 obj-m := kernel_hello.o 5 6 default: 7 $(MAKE) -C $(KERNAL_DIR) M=$(PWD) modules
KERNAL_DIR:你剛剛安裝頭文件的目錄
PWD:表明當前你編寫模塊文件的目錄。
obj-m:模塊的依賴目標文件。另外,內核的Makefile文件:obj-m表明將目標文件編譯成模塊,obj-y表明編譯到內核,ojb-n表明不編譯。
-C:執行make的時候,把工做目錄切換到-C後面指定的參數目錄,這裏即頭文件目錄下的build目錄。
M:這個M時內核Makefile裏面的一個變量。做用時回到當前目錄繼續讀取Makefile,這裏的就是讀完build目錄下的Makefile以後再回到咱們的這個目錄,讀取咱們剛剛編寫的那個Makefile。
最後的modules是表明編譯模塊。
如今咱們來編譯下,在咱們編寫Makefile的目錄下執行make,會看到生成了模塊文件kernel_hello.ko
查看模塊信息:sudo modinfo kernel_hello.ko
能夠看到剛剛那幾個宏插入的模塊信息。
如今咱們一口氣執行4個動做:插入模塊,查看內核已插入的模塊,卸載模塊,查看dmesg信息:
能夠看到,模塊在初始化和退出時都打印了函數裏面的信息。
四.模塊參數傳遞
模塊的參數傳遞也是一個宏,在頭文件目錄的include/linux/moduleparam.h:
1 module_param(name, type, perm)
name:模塊中的變量名,也是用戶可指定參數名。
type:byte,short,ushot,int,uint,long,ulong,charp,bool這些
perm:模塊的權限控制。跟linux文件權限控制同樣的。
文件名:kernel_hello_param.c
1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/kernel.h> 4 5 /* 如下4個宏分別是許可證,做者,模塊描述,模塊版本 */ 6 MODULE_LICENSE("Dual BSD/GPL"); 7 MODULE_AUTHOR("yuuyuu"); 8 MODULE_DESCRIPTION("kernel module hello"); 9 MODULE_VERSION("1.0"); 10 11 static char *msg; 12 module_param(msg, charp, 0644); 13 14 /* 入口函數 */ 15 static int hello_init(void) 16 { 17 printk(KERN_ALERT "hello_init() start\n"); 18 printk(KERN_ALERT "%s\n", msg); 19 20 return 0; 21 } 22 23 /* 退出函數 */ 24 static void hello_exit(void) 25 { 26 printk(KERN_ALERT "hello_exit() start\n"); 27 } 28 29 /* 註冊到內核 */ 30 module_init(hello_init); 31 module_exit(hello_exit);
比上一個文件,就增長了11,12,18行。注意第12行的charp,是內核的字符指針。
編譯後,傳參插入,dmesg查看信息:
插入的參數msg跟在模塊後面便可。
五.模塊間函數調用
模塊的函數導出到符號表才能夠供其餘函數使用,須要用到宏:
1 EXPORT_SYMBOL(sym)
該宏在include/linux/export.h裏面。
既然模塊間函數調用,咱們要編寫2個模塊。
文件一:kernel_fun.h
1 #ifndef KERNEL_FUN_H 2 #define KERNEL_FUN_H 3 4 void fun(void); 5 6 #endif
文件二,要導出的模塊文件:kernel_fun.c
1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/kernel.h> 4 #include <linux/export.h> 5 6 #include "kernel_fun.h" 7 8 /* 如下4個宏分別是許可證,做者,模塊描述,模塊版本 */ 9 MODULE_LICENSE("Dual BSD/GPL"); 10 MODULE_AUTHOR("yuuyuu"); 11 MODULE_DESCRIPTION("kernel module hello"); 12 MODULE_VERSION("1.0"); 13 14 /* 入口函數 */ 15 static int fun_init(void) 16 { 17 printk(KERN_ALERT "fun_init() start\n"); 18 19 return 0; 20 } 21 22 void fun() 23 { 24 printk(KERN_ALERT "fun() is called\n"); 25 } 26 27 /* 退出函數 */ 28 static void fun_exit(void) 29 { 30 printk(KERN_ALERT "fun_exit() start\n"); 31 } 32 33 /* 註冊到內核 */ 34 module_init(fun_init); 35 module_exit(fun_exit); 36 37 /* 導出符號表 */ 38 EXPORT_SYMBOL(fun);
最後一行就是導出到符號表。
文件三,要調用模塊文件二的函數:kernel_mod.c
1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/kernel.h> 4 #include <linux/export.h> 5 6 #include "kernel_fun.h" 7 8 /* 如下4個宏分別是許可證,做者,模塊描述,模塊版本 */ 9 MODULE_LICENSE("Dual BSD/GPL"); 10 MODULE_AUTHOR("yuuyuu"); 11 MODULE_DESCRIPTION("kernel module hello"); 12 MODULE_VERSION("1.0"); 13 14 /* 入口函數 */ 15 static int mod_init(void) 16 { 17 printk(KERN_ALERT "mod_init() start\n"); 18 19 /* 調用fun */ 20 fun(); 21 return 0; 22 } 23 24 /* 退出函數 */ 25 static void mod_exit(void) 26 { 27 printk(KERN_ALERT "mod_exit() start\n"); 28 } 29 30 /* 註冊到內核 */ 31 module_init(mod_init); 32 module_exit(mod_exit);
第20行便是調用其餘模塊的函數。
這裏要編譯2個模塊,對應的Makefile文件:
1 KERNAL_DIR ?= /lib/modules/$(shell uname -r)/build 2 PWD := $(shell pwd) 3 4 obj-m := kernel_mod.o kernel_fun.o 5 6 default: 7 $(MAKE) -C $(KERNAL_DIR) M=$(PWD) modules
編譯好這2個模塊後,咱們如今來驗證。注意,由於kernel_mod依賴kernel_fun,因此我要先插入kernel_fun模塊。
卸載模塊的時候,咱們要先卸載kernel_mod,緣由同上。
依次插入kernel_fun,查看它的符號表,而後插入kernel_mod,查看dmesg:
能夠看到kernel_fun的fun()被kernle_mod調用了。