有关字符设备驱动的基本概念
编写驱动程序的第一步:定义驱动程序为用户程序提供的能力(机制)!
a、模块运行于内核空间
应用程序运行于用户空间
b、模块运行时,分配的栈很小,注意栈溢出;
c、具有两个下划线前缀(__)的函数,通常是底层组件的接口,需要谨慎使用;
d、内核符号表
公共内核符号表用于解释模块内未定义的符号,它包含了所有的全局内核项(函数和变量)
的地址;导出内核符号表加以使用,可以建立模块间的依赖关系(模块层叠技术);
如果一个模块需要向其他模块导出符号:
1 EXPORT_SYMBOL(name); //(name必须是全局变量)符号可导出
2 EXPORT_SYMBOL_GPL(name); //符号只供GPL许可证下的模块使用
e、所有模块都包含的头文件
1 #include <linux/module.h> //包含大量函数和符号的定义
2 #include <linux/init.h> //指定出入口函数
3 #include <linux/moduleparam.h> //可选:用于定义向模块传递参数的功能
f、指定代码许可证
1 MODULE_LICENSE("GPL");
g、可使用 #include <linux/errno.h>中定义的错误编码,做函数返回值使用。
h、只有系统调用的名字前带有"sys_"前缀
Makefile
a、一个源文件
obj-m += src.o
b、多个源文件
obj-m += src.o
module-objs := src1.o src1.o
c、示例
1 KERN_DIR = /work/system/linux-2.6.22.6
2
3 all:
4 make -C $(KERN_DIR) M=`pwd` modules
5
6 clean:
7 make -C $(KERN_DIR) M=`pwd`
modules clean
8 rm -rf modules.order
9
10 obj-m += src.o
模块的装载与卸载
装载模块:insmod
modprobe
二者区别:modprobe会考虑要装载的模块是否引用了一些当前内核不存在的符号;如果有这类引用,modprobe会在当前模块搜索路径中查找定义了这些符号的其他模块,同时装 载这些模块到内核。
卸载模块:rmmod
列出当前已装载模块:lsmod
insmod *.ko
module_init(fun_init)
1 static int __init fun_init(void)
2 {
3 ......
4 }
5 module_init(fun_init);
rmmod *.ko
module_exit(fun_exit);
1 static int __exit fun_exit(void)
2 {
3 ......
4 }
5 module_exit(fun_exit);
初始化过程中的错误处理:
①检查函数返回值,确保函数顺利执行;
②出错后,及时撤销已顺利执行的操作(释放已占用内存);
③可以使用goto进行出错处理;
向模块传递参数
一个设计良好的模块,可以在装载时配置。
insmod hellop howmany=10 whom="Mom"
定义模块可传入参数的方法:
driver.c
1 ......
2 static char *whom = "world";
3 static int howmany = 1;
4 ......
5 module_param(howmany, int, S_IRUGO);
6 module_param(whom, charp, S_IRUGO);
7 ......
支持的参数类型:
(bool, invbool, charp, int, long, short, uint, ulong, ushort)
以数组的方式传递参数
1 module_param_array(name, type, num, perm);