设备驱动之模块理解
在同一台计算机上,安装不同的操作系统,其驱动程序区别很大
但是记住一点,驱动程序的功能大概是相同的,有以下三点
1,初始化硬件,这是驱动最基本的功能:通过总线识别设备,访问设备寄存器,按需求配置设备端口,同时
设置中断
2,向操作系统提供统一的编程,以便应用程序可以使用系统的api来访问设备
3,提供辅助功能,例如虚拟打印,回环网卡
模块:
内核模块是一种可以被内核动态加载和卸载的可执行程序
相对于普通应用程序,内核模块的特殊点
1,运行环境不同,内核模块运行在内核态,可以访问系统几乎所有的硬件资源,可以获得与操作系统内核
相同的权限,危险性较大,一点问题就会导致系统崩溃;而普通应用程序只能在用户态运行,相对安全
2,功能定位不同,普通应用程序完成特定的目标,内核模块提供通用的功能
3,函数调用方式不同,内核模块只能调用内核提供的特定函数,普通应用程序可以调用自身以外的函数
只要能正确连接就能运行.
另外在编写模块时就时刻注意,考虑并发的问题
因为在内核空间同一时间会有多个进程在运行,当然在同一时刻会有不同有应用程序访问驱动程序模块
而大部分的设备能够向cpu发送中断,此处有一个概念就是代码的重入,意为当一个内核模块程序在没有
调用完毕之前又被多次调用.支持重入的代码叫可重入代码.
注意:内核和驱动代码必须是可重入的
既然要求可重入,内核模块的数据结构要采取必要的措施来保护在多线程情况下不被其他进程破坏,应该
采取加锁机制,用于保护共享数据.
另外,内核模块使用物理内存,而应用程序使用虚拟内存.
内核模块可使用的内存非常小,使用时应该特别注意
一个内核模块至少应该包括加载和卸载两种操作
如:
static int __init init_func(void);
static void __exit exit_func(void);
说明:这两个函数名字可以自己定义,但必须使用规定的返回值和参数格式
static修饰符的作用是函数仅在当前的文件有效,外部不可见.__init告诉编译器该函数在初始化完毕后被忽略
__exit告诉编译器该代码仅在卸载模块时被调用
内核提供了一个kmod的模块来管理内核模块,kmod模块与用户态的kmodule模块通信,获取模块信息
内核模块加载:
insmod:加载模块时不检查内核模块的符号是否已经在内核中定义
modprobe:不仅检查内核模块符号表,还会检查模块的依赖关系
另外,由于kmod的机制,内核可以在需要加载某个内核模块的时候,通知用户态的modprobe加载模块
使用insmod加载内核模块时,特权级系统调用会查找内核符号表(一般存在于内核模块列表的和一个模块结构中)
insmod把内核模块加载到虚拟内存中,利用内核输出的符号表来修改被加载模块中没有解析的内核函数和资源地址
修改完后,使用特权系统调用申请模块空间,因为内核模块工作在内核态.访问用户态空间要做地址转换
申请好空间后,insmod把内核模块复制到新的空间,把内核模块放在模块列表的尾部,并设置标志为UNINITIALIZED
表示内核模块未被引用.同时insmod告诉内核初始和清除函数的地址.
内核模块的卸载:
rmmod
一个内核模块被其他模块引用的时候自身引用计数器会自动加1
当要卸载时,要判断引用计数器是否为0,为0才卸载.否则只能减1
当然kmod会定期检查每个模块的引用计数器,当为0是会卸载该模块.
当使用rmmod时候,rmmod会从内核模块列表中查找指定模块,判断是否为0,如果是0就卸载,同时释放占用的内存
编写一个基本的内核模块
/*包含的头文件*/
#include
#include
#include
MODULE_LICENSE("GPL");/*声明*/
MODULE_AUTHOR("Huntley");/*作者*/
/*模块初始化函数*/
static int __init hello_init(void)
{
printk(KERN_ALERT "(init)Hello,World!\n");
return 0;
}
/*模块退出清除函数*/
static void __exit hello_exit(void)
{
printk(KERN_ALERT "(exit)Bye,World!\");
}
module_init(hello_init);/*模块初始化*/
module_exit(hello_exit);/*模块退出*/
同时要会编写相应的makefile
为模块添加参数
/*包含的头文件*/
#include
#include
#include
MODULE_LICENSE("GPL");/*声明*/
MODULE_AUTHOR("Huntley");/*作者*/
static int initvalue = 0;
static char *initname = NULL;
module_param(initvalue,int, S_IRUGO);
module_param(initname,charp,S_IRUGO);
/*模块初始化函数*/
static int __init hello_init(void)
{
printk(KERN_ALERT"initvalue = %d initname = %s\n",initvalue,initname);
printk(KERN_ALERT "(init)Hello,World!\n");
return 0;
}
/*模块退出清除函数*/
static void __exit hello_exit(void)
{
printk(KERN_ALERT "(exit)Bye,World!\");
}
module_init(hello_init);/*模块初始化*/
module_exit(hello_exit);/*模块退出*/
使用如下命令输入参数,当然也可以在程序中事先定义好
insmod xxx.ko initvalue=123 initname="test"
阅读(1143) | 评论(0) | 转发(0) |