《嵌入式Linux驱动开发教程》--内核模块

内核模块

绝大多数的驱动都是以内核模块的形式实现。

宏内核和微内核
  宏内核(Linux):所有的内核功能都被整体编译在一起,形成单独的内核镜像文件,内核中各功能模块的交互通过直接的函数调用进行。
  微内核(Windows):功能模块的交互需要微内核提供的通信机制
宏内核添加改变内核的某个功能,需要重新编译整个内核,然后重启整个系统,引入内核模块:
  内核模块:被单独编译的一段内核代码,根据需求动态的加载、卸载到内核。修改驱动的代码,仅编译驱动代码本身,加载的内核测试。
  内核模块有助于减小内核镜像文件的体积,减少内核占用的内存空间(整个内核镜像加载到内存中),不必把所有驱动都编译进内核,以模块形式单独编译驱动程序。
(内核模块不一定都是驱动程序,驱动程序也不全是模块的形式)



2.1内核模块程序的初始化和退出函数原型

在这里插入图片描述

2.2内核模块的相关工具

1.模块加载
1.1insmod ***.ko:加载指定目录下的.ko文件到内核,需要指定路径,默认是在当前目录找。
1.2depmod+modprobe ***
  depmod : 更新模块的依赖信息
  modprobe : 自动加载模块到内核,不指定路径和后缀
2.模块信息 modinfo ***:查看模块的信息。
3.模块卸载 rmmod ***:如果内核配置为允许卸载模块,将指定的模块从内核中卸载。

2.3内核模块的一般形式(\module\ex2)

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

// 使用static关键字,因为static关键字修饰的函数的链接属性为内部,解决了内核模块加载到内核之后函数名可能会重复定义的问题。
// 初始化只会调用一次,使用 _init 修饰,当函数调用成功后,模块的加载程序会释放掉这部分空间,节约内存
static int _init vser_init(void)    
{
    printk("模块初始化了");
    return 0;
}
// 使用 _exit 修饰,如果模块不允许卸载,那么这段代码完全不需要加载
static void _exit vser_exit(void)
{
    printk("模块卸载了");
}

//module_init和module_exit是两宏,之后模块的初始化和卸载函数可以通过别名定义
module_init(vser_init);//指定初始化模块别名
module_exit(vser_exit);//指定卸载模块别名

//全是宏:模块以及作者的相关信息
//modinfo命令输出信息
MODULE_LICENSE("GPL");                  //接受相关许可协议,必须加
MODULE_AUTHOR("NAPO <1234567@qq.com>");
MODULE_DESCRIPTION("A Module");
MODULE_ALIAS("test_module");		    //模块的别名,模块加载时可以使用别名

2.4多个源文件编译一个内核文件(\module\ex3)

与调用外部c文件有差别,只在需要调用的文件中使用extern声明下,不需要include .h文件

makefile的修改:
在这里插入图片描述

2.5内核模块参数(\module\ex4)

模块初始化函数在模块加载时被调用,但是函数是不接受参数的,所以需要想办法通过命令行向其传递参数,这就产生了模块参数。

在这里插入图片描述

module_param(name, type, perm); # 单个变量使用的宏
module_param_array(name, type, nump,perm); # 数组使用的宏
name:变量名
type:变量类型
nump:数组元素个数的指针,可选
perm:sysfs文件系统中的文件权限属性
参数的类型有:bool、invbool(反转值bool)、charp(字符指针)、short、int、long、ushort(u表示无符号)、uint、ulong。以及它们对应的数组类型
指定模块参数的值:modprobe vser baudrate=115200 port=1,2,3,4 name=“virtual-serial”

2.6内核模块的依赖

模块之间的链接(2.4是多个源文件生成一个内核模块)
在一个驱动程序使用另一个驱动程序
1.导出的条件:
  全局变量;
  在模块的全局部分导出
2.使用方法
  第一、在模块函数,全局变量定义之后使用EXPORT_SYMBOL(函数名)
  第二、在掉用该函数的模块中使用extern对之声明
  第三、首先加载定义该函数的模块,再加载调用该函数的模块
  第四、两个模块需要一起编译,要不然的话vser.c并不知道dep.c的存在,所以依然找不到符号。
  第五、卸载时需要先卸载vser模块再卸载dep模块,要不然可能会因为vser依赖dep模块,而导致dep模块不能被卸载。

2.7内核模块与普通应用程序的区别

1.内核模块是操作系统的一部分,运行在内核空间,应用程序运行在用户空间(主要)。
2.内核模块中的函数是被动的被调用的,比如初始化函数和清除函数分别在模块加载和模块卸载时被调用。模块注册一些服务性质的函数 供其他功能呢单元调用,应用程序是顺序执行,通常调用一些函数。
3.内核模块处于C函数库之下,不能调用C库函数,应用程序可以调用。
4.内核模块需要做一些清除性的工作,而应用程序有些不需要。
5.内核模块如果产生了非法访问一般导致系统崩溃,应用程序一般只影响自己。
6.内核模块并发多,如中断,多处理器;应用程序一般多进程或多线程。
7.内核空间只有4KB或8KB的栈,需要大的内存空间时,需要动态分配。
8.内核空间的printk,不支持浮点数。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值