本文共 8097 字,大约阅读时间需要 26 分钟。
Linux内核的整体架构非常庞大,其包含组件也非常多,怎样把需要的部分包含在内核中?
一种方法是把所需要的功能编译到linux内核中,这会导致两个文件,一是生成的内核会很大,二是如果我们要在现有的内核中新增或删除功能,将不得不重新编译内核。
Linux提供了这样一种机制,称为模块(Moudle)。可使编译出的内核本身并不包含所有的功能,而在这些被使用的时候,其对应的代码可被动态的加载到内核。
模块的特点
模块本身不被编译到内核映像,从而控制了内核的大小。
模块一旦被加载,它就和内核中的其他部分完全一样。
insmod demo.ko 插入驱动模块
lsmod 查看插入正在运行驱动模块
rmmod demo 移除驱动模块
modinfo demo.ko 查看驱动模块信息
dmesg 查看内核缓存信息
dmesg -c 清除内核缓存信息
有依赖关系的
modprobe (智能安装) xx(不加.ko)
modprobe -r 移除(注意:移除需要依赖的模块,同时会移除被依赖的模块)
A.hello world驱动模块 demo.c
#include#include 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.oOUR_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的编写 */#includefun0.c#include 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");/*描述*/
#include#include extern void call_func1(void);void call_func0(void){ call_func1();}
fun1.c
#include#include void call_func1(void){ printk("you are a good boy.\n");}
Makefile(重点注意)
obj-m := team_hehe.oteam_hehe-objs := demo.o fun0.o fun1.oOUR_KERNEL := /ARM/linux-3.5-songze/all: make -C $(OUR_KERNEL) M=$(shell pwd) modulesclean: 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
Linux的“/proc/kallsyms”文件对应着内核符号表,它记录了符号以及符号所在的内存地址。
模块可以使用如下宏导出符号到内核符号表中:
EXPORT_SYMBOL(符号表);
EXPORT_SYMBOL_GPL(符号表);
导出的符号(变量或函数)可以被其他模块使用,只需要使用前声明一下即可。EXPORT_SYMBOL_GPL()只适用于包含GPL许可权的模块。
示例代码如下:
demo1.c
#include#include 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#include 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
在用户态下编程可以通过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#include 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#include #define CNT 16static 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
转载地址:http://zglpi.baihongyu.com/