2.1 驅動模塊框架
Linux內核的整體架構非常龐大,其包含組件也非常多,怎樣把需要的部分包含在內核中?
一種方法是把所需要的功能編譯到linux內核中,這會導致兩個文件,一是生成的內核會很大,二是如果我們要在現有的內核中新增或刪除功能,將不得不重新編譯內核。
Linux提供了這樣一種機制,稱爲模塊(Moudle)。可使編譯出的內核本身並不包含所有的功能,而在這些被使用的時候,其對應的代碼可被動態的加載到內核。
模塊的特點
模塊本身不被編譯到內核映像,從而控制了內核的大小。
模塊一旦被加載,它就和內核中的其他部分完全一樣。
2.1.1對驅動模塊進行操作的shell命令
insmod demo.ko 插入驅動模塊
lsmod 查看插入正在運行驅動模塊
rmmod demo 移除驅動模塊
modinfo demo.ko 查看驅動模塊信息
dmesg 查看內核緩存信息
dmesg -c 清除內核緩存信息
有依賴關係的
modprobe (智能安裝) xx(不加.ko)
modprobe -r 移除(注意:移除需要依賴的模塊,同時會移除被依賴的模塊)
2.1.2一個最簡單的linux內核模塊代碼如下:
A.hello world驅動模塊 demo.c
#include <linux/init.h>
#include <linux/module.h>
static int __init demo_init(void)
{
printk("hello,word! driver module is inserted!\n");
return 0;
}
module_init(demo_init);/*指定驅動模塊入口點*/
static void __exit demo_exit(void)
{
printk("goodbye, word! driver is removed!\n");
}
module_exit(demo_exit);/*指定驅動模塊出口*/
MODULE_LICENSE("GPL");/*公共許可聲明*/
MODULE_AUTHOR("Songze Lee");/*作者*/
MODULE_VERSION("Verson 1.0");/*版本號*/
MODULE_DESCRIPTION("It is a simple demo for driver module");/*描述*/
內核模塊中用於輸出的函數的內核空間的printk(),用法和printf基本相似,但可以定義輸出級別。
Makefile編寫如下:
obj-m := demo.o
OUR_KERNEL := /ARM/linux-3.5-songze/
all:
make -C $(OUR_KERNEL) M=$(shell pwd) modules
# 進入到變量目錄(內核源碼目錄)下編譯 M 編譯pwd路徑下的模塊
clean:
make -C $(OUR_KERNEL) M=`pwd` clean
調試結果如下:
[projct /]# insmod demo.ko
[ 1670.390000]hello,word! driver module is inserted!
[projct /]#rmmod demo
[ 1681.215000]goodbye, word! driver is removed!
rmmod: module'demo' not found
B.驅動模塊調用子函數編寫
demo.c
/**
* 驅動模塊調用子函數
* 注意Makefile的編寫
*/
#include <linux/init.h>
#include <linux/module.h>
extern void call_func0(void);
static int __init demo_init(void)
{
call_func0();
printk("hello,word! driver module is inserted!\n");
return 0;
}
module_init(demo_init);/*指定驅動模塊入口點*/
static void __exit demo_exit(void)
{
printk("goodbye, word! driver is removed!\n");
}
module_exit(demo_exit);/*指定驅動模塊出口*/
MODULE_LICENSE("GPL");/*公共許可聲明*/
MODULE_AUTHOR("Songze Lee");/*作者*/
MODULE_VERSION("Verson 1.0");/*版本號*/
MODULE_DESCRIPTION("It is a simple demo for driver module");/*描述*/
fun0.c
#include <linux/init.h>
#include <linux/module.h>
extern void call_func1(void);
void call_func0(void)
{
call_func1();
}
fun1.c
#include <linux/init.h>
#include <linux/module.h>
void call_func1(void)
{
printk("you are a good boy.\n");
}
Makefile(重點注意)
obj-m := team_hehe.o
team_hehe-objs := demo.o fun0.o fun1.o
OUR_KERNEL := /ARM/linux-3.5-songze/
all:
make -C $(OUR_KERNEL) M=$(shell pwd) modules
clean:
make -C $(OUR_KERNEL) M=`pwd` clean
調試結果如下:
[projct /]# insmod team_hehe.ko
[ 1920.880000]you are a good boy.
[ 1920.880000]hello,word! driver module is inserted!
[projct /]#rmmod team_hehe
[ 1932.020000]goodbye, word! driver is removed!
rmmod: module'team_hehe' not found
2.1.3導出符號
Linux的“/proc/kallsyms”文件對應着內核符號表,它記錄了符號以及符號所在的內存地址。
模塊可以使用如下宏導出符號到內核符號表中:
EXPORT_SYMBOL(符號表);
EXPORT_SYMBOL_GPL(符號表);
導出的符號(變量或函數)可以被其他模塊使用,只需要使用前聲明一下即可。EXPORT_SYMBOL_GPL()只適用於包含GPL許可權的模塊。
示例代碼如下:
demo1.c
#include <linux/init.h>
#include <linux/module.h>
extern int sure;
extern void pri_value(int);
extern void call_func0(void);
static int __init demo_init(void)
{
sure = 3856;
pri_value(sure);
printk("hello,word! driver module is inserted!\n");
return 0;
}
module_init(demo_init);
static void __exit demo_exit(void)
{
printk("goodbye, word! driver is removed!\n");
}
module_exit(demo_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Songze Lee");
MODULE_VERSION("verson 1.0");
MODULE_DESCRIPTION("It is a simple demo for driver module");
demo2.c
#include <linux/init.h>
#include <linux/module.h>
static int sure = 9527;
/*符號導出 static定義的變量或函數其他模塊可以調用*/
EXPORT_SYMBOL_GPL(sure);
static void pri_value(int val)
{
printk("In %s: sure = %d\n", __FILE__, sure);
}
EXPORT_SYMBOL_GPL(pri_value);
static int __init demo_init(void)
{
pri_value(sure);
printk("hello,word! driver module is inserted!\n");
return 0;
}
module_init(demo_init);
static void __exit demo_exit(void)
{
printk("goodbye, word! driver is removed!\n");
}
module_exit(demo_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Songze Lee");
MODULE_VERSION("verson 1.0");
MODULE_DESCRIPTION("It is a simple demo for driver module");
調試結果如下:
[projct /]# insmod demo02.ko
[ 2484.680000] In/ARM/linux-3.5-songze/drivers/songze_drivers/module/
demo05/demo02.c: sure = 9527
[ 2484.680000] hello,word! driver module is inserted!
[projct /]# insmod msb.ko
[ 2491.690000] In /ARM/linux-3.5-songze/drivers/songze_drivers/module/
demo05/demo02.c: sure = 3856
[ 2491.690000] hello,word! driver module is inserted!
[projct /]# rmmod demo02
rmmod: remove 'demo02': Resource temporarily unavailabl兩者有依賴關係
[projct /]# rmmod msb
[ 2531.345000] goodbye, word! driver is removed!
rmmod: module 'msb' not found
[projct /]# rmmod demo02
[ 2542.910000] goodbye, word! driver is removed!
rmmod: module 'demo02' not found
2.1.4模塊參數
在用戶態下編程可以通過main()來傳遞命令行參數,而編寫一個內核模塊則可通過module_param()來傳遞命令行參數。
module_param宏是Linux 2.6內核中新增的,該宏被定義在include/linux/
moduleparam.h文件中,具體定義如下:
#define module_param(name, type, perm)
module_param_named(name, name, type,perm)
由此可知module_param的實現是通過module_param_named(name, name, type,perm)的。
module_param(name,type, perm)
name:參數名 type:參數類型perm:參數讀寫權限
參數類型可以是byte、short、ushort、int、uint、long、ulong、charp(字符指針)、bool或invbool(布爾的反),在模塊被編譯時會將module_param中聲明的類型與變量定義的類型進行比較、判斷是否一致。
module_param_array(name,type,num,perm)
name:數組名 type:數組類型num:數組長 perm:參數讀寫權限
perm參數的作用是什麼?
最後的 module_param字段是一個權限值,表示此參數在sysfs文件系統中所對應的文件節點的屬性。你應當使用 <linux/stat.h> 中定義的值. 這個值控制誰可以存取這些模塊參數在 sysfs 中的表示.
當perm爲0時,表示此參數不存在 sysfs文件系統下對應的文件節點。 否則, 模塊被加載後,在/sys/module/ 目錄下將出現以此模塊名命名的目錄, 帶有給定的權限.。
權限在include/linux/stat.h中有定義
比如:
#define S_IRWXU 00700
#define S_IRUSR 00400
#define S_IWUSR 00200
#define S_IXUSR 00100
#define S_IRWXG 00070
#define S_IRGRP 00040
#define S_IWGRP 00020
#define S_IXGRP 00010
#define S_IRWXO 00007
#define S_IROTH 00004
#define S_IWOTH 00002
#define S_IXOTH 00001
使用 S_IRUGO 參數可以被所有人讀取, 但是不能改變; S_IRUGO|S_IWUSR 允許 root 來改變參數.注意, 如果一個參數被 sysfs 修改, 你的模塊看到的參數值也改變了, 但是你的模塊沒有任何其他的通知. 你應當不要使模塊參數可寫, 除非你準備好檢測這個改變並且因而作出反應.
示例A:module_param(name, type, perm)
/**
*module_param(name, type, perm) 內核模塊傳參
*向當前模塊傳參
*/
#include <linux/init.h>
#include <linux/module.h>
static int x_rel = 480, y_rel = 272;
module_param(x_rel, int, 0);
module_param(y_rel, int, 0);
static char *info = "mdg: lol?";
static int num=10;
module_param(num,int,S_IRUGO);
module_param(info, charp, 0);
static int __init demo_init(void)
{
printk("hello,word! driver module is inserted!\n");
printk("x: %d, y: %d\n %s\n", x_rel, y_rel, info);
printk("num=%d\n",num);
return 0;
}
module_init(demo_init);
static void __exit demo_exit(void)
{
printk("goodbye, word! driver is removed!\n");
}
module_exit(demo_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Songze Lee");
MODULE_VERSION("version 1.0");
MODULE_DESCRIPTION("It is a simple demo for driver module");
調試結果:
[projct /]# insmod demo.ko num=110 x_rel=123 y_rel=345info="hello world"
[ 4140.560000] hello,word! driver module is inserted!
[ 4140.560000] x: 123, y: 345
[ 4140.560000] hello world
[ 4140.560000] num=110
[projct /]# cat /sys/module/demo/parameters/num
110
[projct /]# ls -l /sys/module/demo/parameters/num
-r--r--r-- 10 0 4096 Jan 1 14:06 /sys/module/demo/parameters/num
可發現num爲只讀文件
示例B:module_param_array(name,type,num,perm)
#include <linux/init.h>
#include <linux/module.h>
#define CNT 16
static int num = CNT;
static int array[CNT] = {1,2,3,4,5,6,7};
module_param_array(array, int, &num, 0);
static int __init demo_init(void)
{
int i;
printk("Insert module ok!\n");
for(i = 0; i < num; i++){
printk("array[%d] = %d\n", i, array[i]);
}
return 0;
}
module_init(demo_init);
static void __exit demo_exit(void)
{
printk("goodbye, word! driver is removed!\n");
}
module_exit(demo_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("millet9527");
MODULE_VERSION("uplooking plus 7");
MODULE_DESCRIPTION("It is a simple demo for driver module");
調試結果:
[projct /]# insmod demo.koarray=11,22,33,44,55,66,77,88,99
[ 4965.605000] Insert module ok!
[ 4965.605000] array[0] = 11
[ 4965.605000] array[1] = 22
[ 4965.605000] array[2] = 33
[ 4965.605000] array[3] = 44
[ 4965.605000] array[4] = 55
[ 4965.605000] array[5] = 66
[ 4965.605000] array[6] = 77
[ 4965.605000] array[7] = 88
[ 4965.605000] array[8] = 99
獲取源碼: git clone https://www.github.com/lisongze2016/Tiny4412_kernel.git