https://zhuanlan.zhihu.com/p/483160418
目录
一、模块指令
https://www.pianshen.com/article/2706300609/
modinfo -p mod名:查看mod的可用参数信息
参数传递
加载模块时传递参数
传递参数时如果参数名称写错了,会报错:unknown parameter 'var_out' ignored
二、模块化编程
1.编写模块
最简单的示例
必须用c语言编写
https://blog.csdn.net/nanfeibuyi/article/details/105034422
基本步骤:
- 引用相关的头文件
#include <linux/init.h> //module_init(),module_exit() #include <linux/module.h> //MODULE_LICENSE(), MODULE_AUTHOR()
- 需要编写一个初始化/加载函数--init()函数和一个退出/卸载函数--exit()函数。
其中使用module_init(初始化函数)来指定初始化函数,使用module_exit(退出函数)来指定退出函数。 模块加载时会调用初始化函数,模块卸载时会调用退出函数。
3.添加模块相关描述信息
MODULE_LICENSE("GPL"); 说明遵循GPL协议 MODULE_AUTHOR("Genven_Liang"); 说明作者信息
简单示例:
#include <linux/init.h> //__init, __exit
#include <linux/module.h> //module_init(), module_exit(),MODULE_LICENSE(),MODULE_AUTHOR(),MODULE_DESCRIPTION()
MODULE_LICENSE("GPL"); //声明协议
MODULE_AUTHOR("Genven_Liang"); //作者信息
MODULE_DESCRIPTION("test for linux driver."); //对本驱动程序的描述
//初始化函数 执行insmod时会调用该函数
static int __init hello_init(void)
{
printk("hello_init!\n");
return 0;
}
//退出函数 执行remod时会调用该函数
static void __exit hello_exit(void)
{
printk("hello_exit!\n");
}
module_init(hello_init);
module_exit(hello_exit);
模块语法
2.编译模块
https://zhuanlan.zhihu.com/p/409648724
编译方式
1)内部编译:将驱动程序源码放在内核源码目录中进行编译
2)外部编译:将驱动程序源码放在内核源码目录外进行编译
3)静态编译:编译进uImage中
4)动态编译 (.ko文件,动态加载驱模块)
外部编译+动态编译
编译模块命令`make -C M=`,https://blog.csdn.net/qq_40334837/article/details/89515751
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
这句是Makefile的规则:这里的$(MAKE)就相当于make,-C 选项的作用是指将当前工作目录转移到你所指定的位置。“M=”选项的作用是,当用户需要以某个内核为基础编译一个外部模块的话,需要在make modules 命令中加入“M=dir”,程序会自动到你所指定的dir目录中查找模块源码,将其编译,生成KO文件。
M是内核根目录下的Makefile中使用的变量
内部编译(将驱动源码集成到内核并参加编译)
https://blog.csdn.net/Naisu_kun/article/details/126362277
编写好的驱动模块的Kconfig和Makefile,然后集成到driver根目录下的Kconfig与Makefile中。假如我的驱动模块目录为daiq,则:
1.向父目录中的Makefile添加:
obj-y += daiq/
表示在编译过程中包含子目录daiq目录。
2.修改Kconfig文件,添加:
source "drivers/daiq/Kconfig"
表示在配置时引用子目录daiq中的配置文件Kconfig。
驱动编译错误整理
-
error: macro "access_ok" passed 3 arguments, but takes just 2
https://blog.csdn.net/weixin_39901404/article/details/110560048
去掉access_ok函数的第一个参数即可 -
error: ‘struct file’ has no member named ‘f_dentry’
使用file_inode(f)替换掉f->f_dentry->d_inode -
加载驱动模块时Device or resource busy的解决方法
https://www.shuzhiduo.com/A/Ae5R72XAdQ/
我的情况是字符设备已经被占用(没有动态注册),加载驱动模块时报错。 -
模块间符号导出
三、模块与驱动
https://blog.csdn.net/weixin_44818675/article/details/135386653
模块的诞生背景
linux内核整体结构非常庞大,其包含的组件也非常多。我们怎么把需要的部分都包含在内核中呢?
一种办法是把所有的需要的功能都编译到内核中。这会导致两个问题,一是生成的内核会很大,二是如果我们要在现有的内核中新增或删除功能,不得不重新编译内核,工作效率会非常的低,同时如果编译的模块不是很完善,很有可能会造成内核崩溃。
linux提供了另一种机制来解决这个问题,这种集中被称为模块,可以实现编译出的内核本身并不含有所有功能,而在这些功能需要被使用的时候,其对应的代码可以被动态的加载到内核中。
模块特点
1)模块本身并不被编译入内核,从而控制了内核的大小。
2)模块一旦被加载,他就和内核中的其他部分完全一样。
模块与驱动的关系
模块并不是驱动的必要形式:即:驱动不一定必须是模块,有些驱动是直接编译进内核的;同时模块也不全是驱动,例如我们写的一些很小的算法可以作为模块编译进内核,但它并不是驱动。就像烧饼不一定是圆的,圆的也不都是烧饼一样。
0 条评论