内核驱动的本质——模块
在Linux中,驱动的本质就是一个模块。模块可以被选择“静态编译”或“模块化编译”
1. 静态编译:链接入内核镜像,默认永远被加载
2. 模块化编译:需要在内核运行时动态加载
这两种处理步骤详见驱动模块的编译与安装
当模块被加载时,可以认为模块即是内核的一部分
1.一个最简单的模块
#include <linux/module.h>
#include <linux/init.h>
static int var = 666;
module_param(var, int, S_IRUGO);
static int __init chrdev_init(void)
{
printk(KERN_INFO "chrdev init\n");
return 0;
}
static void __exit chrdev_exit(void)
{
printk(KERN_INFO "chrdev exit\n");
}
module_init(chrdev_init);
module_exit(chrdev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("taurenking");
MODULE_DESCRIPTION("test");
MODULE_ALIAS("xxx");
2. 用户定义加载、卸载函数
static int __init chrdev_init(void)
{
printk(KERN_INFO "chrdev init\n");
return 0;
}
static void __exit chrdev_exit(void)
{
printk(KERN_INFO "chrdev exit\n");
}
module_init(chrdev_init);
module_exit(chrdev_exit);
- 当模块被加载、卸载时,会分别执行用户绑定过的加载、卸载函数。加载函数需要完成模块的初始化工作;而卸载函数则完成相反的功能,加载函数申请了什么,卸载函数就要释放什么
- 关于__init和__exit标识,__init实质是一个宏:
#define _ _init _ _attribute_ _ ((_ _section_ _ (".init.text")))
其作用就是将被他修饰的函数链接入.init.text段(本来默认是代码段)。内核中所有这类函数都会被链接入.init.text段中,故所有模块的加载函数其实是被统一存放的。内核启动时统一会加载.init.text段中的这些模块加载函数,加载完后就会把这个段给释放掉以节省内存。__exit标识同理 - 加载和卸载函数需要使用module_init()和module_exit()来与加载、卸载行为绑定;module_init()和module_exit()的实质是带参宏,它们会把加载、卸载函数指针传给内核来进行绑定
3.模块描述信息
MODULE_LICENSE("GPL");
MODULE_AUTHOR("taurenking");
MODULE_DESCRIPTION("test");
MODULE_ALIAS("xxx");
- 模块描述信息没什么特别的,唯一值得注意的是许可证,一般都为“GPL”,否则可能会出错
4.模块传参
- 我们可以在模块内以全局变量的形式定义参数,使用module_param()来告诉kernel这个全局变量是参数。其中第一个参数是变量名,第二个参数是类型,第三个参数是读写权限
static int var = 666;
module_param(var, int, S_IRUGO);
- 以本文的模块为例,用户层可以使用
insmod test.ko var=123
来传参,当用户选择不传参时,则以模块内部参数的默认值作为参数值 - 那么对于静态链接进内核的模块,如何传参呢?可以在U-boot中的bootargs里设置
test.ko.var=123
来传参,这种情况说实话很少见。。。。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)