【Linux 驱动】第二章 构造和运行模块
一,核心模块与应用程序的对比
应用程序:小规模及中规模程序,从头到尾执行单个任务。
核心模块:预先注册自己,以便服务于将来的某个请求。然后他的初始化函数就立即结束。
退出时候,应用程序可以不释放自己申请的资源,而模块在退出之前必须仔细撤销初始化函数所做的一切。
二,用户空间和内核空间
模块运行在内核空间,应用程序运行在内核空间。
每当应用程序执行系统调用或者被硬件中断挂起时,Unix将执行模式从用户空间切换到内核空间。
应用程序在虚拟内存中布局,并具有一块很大的栈空间(保存函数调用历史以及当前活动函数中的自动变量)。内核具有非常小的栈,所以我们自己的函数必须和整个内核空间调用链一同共享这个栈。
【注意】在内核API中看到有两个下划线_ _的函数名:接口的底层组件
三,初始化和关闭
static int _ _init initialization_function(void)
{
/*初始化代码*/
return <int>;
}
module_init(initialization_function); //说明内核初始化位置,没有这个函数,则初始化函数无法调用
【注意】_ _init _ _initdata表明函数只在初始化期间使用,模块装载完成后不再使用。
static void _ _init cleanup_function(void)//没有返回值
清除函数 类似 module_exit(cleanup_function)
四,初始化过程中的错误处理
1)时刻铭记,注册可能会失败,因此模块代码要始终检查返回值。
2)当注册时,有些模块注册失败,则需要自行撤销已注册的设施。否则内核处于一种不稳定状态。唯一有效的解决办法:重新引导系统
3)使用goto
int _ _my_init_function(void)
{
int err;
err=regeister_this(ptr1,"skull");
if(err) goto fail_this;
err=regeister_that(ptr2,"skull");
if(err) goto fail_that;
err=regeister_those(ptr3,"skull");
if(err) goto fail_those;
return 0;//成功
fail_this:return err;
fail_those:unregeister_those(ptr3,"skull");
fail_that:unregeister_that(ptr3,"skull");
}
4)初始化函数还在运行时,内核就完全可能会调用我们的模块。所以我们应在用来支持某个设施的所有内部初始化完成之前,不要注册任何设施。
五,模块参数支持很多类型;
1)基本类型:
bool :布尔类型
invbool:颠倒了值的bool类型;
charp :字符指针类型,内存为用户提供的字符串分配;
int :整型
long :长整型
short :短整型
uint :无符号整型
ulong :无符号长整型
ushort :无符号短整型
定义模块参数的方法:
module_param(name, type, perm);
其中,name:表示参数的名字;
type:表示参数的类型;
perm:表示参数的访问权限;
2)数组类型:用逗号间隔的列表提供的值;
声明一个数组参数:
module_param_array(name, type, num, perm);
其中,name:表示数组的名字;
type:表示参数的类型;
num :表示数组中元素数量;
perm:表示参数的访问权限;
3)参数的访问权限
modlue_param和module_param_array中的perm用于设定该参数的访问权限;
perm表示该参数在sysfs文件系统中所对应的文件节点的属性;你用该使用<linux/stat.h>中定义的权限值;这个值控制谁可以存取这些模块参数在sysfs文件系统中的表示;当perm为0时,表示此参数不存在sysfs文件系统下对应的文件节点;否则,模块被加载后,在/sys/module/目录下将会出现以此模块名命名的目录,带有给定的权限;
比如:
#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
注意:如果一个参数被sysfs修改了,那么你的模块看到的参数值也被修改了,但是你的模块不会收到任何通知;你应当不要使模块参数可写,除非你准备好检测这个改变并因而作出反应;
六,在用户空间编写驱动程序
用户空间驱动程序的优点:
1)可以和整个C库链接
2)驱动程序不用借助外部程序(对于复杂的外设,常常需要和驱动一起发行用户提供策略的应用程序)就可以完成许多非常规的任务。
3)在驱动中可以使用浮点数,在某些特殊的硬件中,可能需要使用浮点数,而linux内核并不提供浮点数的支持。如果能在用户态实现驱动,就可以轻松解决这一问题
4) 驱动的问题不会导致整个系统挂起,有过驱动开发经验的人一定会对调试深有感触,一些错误常常导致整个系统挂起。而用户态的驱动在调试上就要方便很多。
5) 用户内存可以换出
6)设计良好的驱动仍然可以支持对设备的并发访问
7)可以给出封闭源码的驱动程序,不必采用GPL,更为灵活
用户空间驱动的最常见例子是X-server,很多USB设备的驱动也可以放到用户空间。目前,很多人尝试在用户态为PCI设备提供驱动
用户空间驱动的缺点:
1)中断在用户空间不可用,最新的UIO接口已经解决了这一问题
2)响应时间较慢
3)只能支持字符设备,无法支持块设备和网络设备
4)可靠性较低,很多驱动都是闭源的,我们没法通过阅读代码解决问题
5)有些硬件厂商只提供和某些linux开发版(常常早就过时了)相匹配的用户空间驱动
相关阅读: