实验环境
- 完整的可以编译的内核源码,这里选取 RK3568_Android11 源码中的kernel部分
添加HELLO WORLD字符设备驱动
- 在RK3568_Android11/kernel/drivers/char/中新建文件夹lx_hello
- 文件夹中增加lx_hello.c Makefile Kconfig 文件(Makefile 和Kconfig 为编译规则文件,这里仿照其他文件内容填写)
编写驱动文件lx_hello.c
内核模块头文件
- #include <linux/module.h>:包含内核模块信息声明的相关函数
- #include <linux/init.h>:包含了 module_init()和 module_exit()函数的声明
- #include <linux/kernel.h>:包含内核提供的各种函数,如printk
驱动文件的入口和出口
- 模块的出口和入口函数是内核模块驱动所必须的
- module_init(XXXX_init); //固定格式模块入口,加载模块的时候调用
- module_exit(XXXX_exit); //固定格式模块出口,卸载模块的时候调用
- 入口函数实现
static int __init hello_init(void) { printk(KERN_INFO "module init success\n"); return 0; }
- 出口函数实现
static void __exit hello_exit(void) //模块的退出
{
printk(KERN_INFO "module exit success\n");
}
-
入口函数中__init 启修饰函数节省内存的作用,因为加载驱动模块的初始化程序只会调用一次,在调用完成后不会再次调用,所以该函数占用的内存空间应该被释放掉,函数名前边加__init 可以达到此目的。
- __init 标记的函数会在编译后放在ELF文件的特定代码段,在模块加载时单独分配内存,函数调用成功后,模块的加载程序会释放掉该部分内存空间。
- __exit 标记用于修饰清除函数,作用和__init 类似,但是用于模块的卸载,如果模块不用卸载,则这段代码不会被加载到内存中。
-
prink 区别于用户层c调用的printf函数,printf为glibc库函数,内核无法调用。内核自己实现了一套打印机制。
-
printk 与printf的使用区别是,prink需要增加打印等级,数字越小级别越高。
- #define KERN_EMERG "<0>" 通常是系统崩溃前的信息
- #define KERN_ALERT "<1>" 需要立即处理的消息
- #define KERN_CRIT "<2>" 严重情况
- #define KERN_ERR "<3>" 错误情况
- #define KERN_WARNING "<4>" 有问题的情况
- #define KERN_NOTICE "<5>" 注意信息
- #define KERN_INFO "<6>" 普通消息
- #define KERN_DEBUG "<7>" 调试信息
-
当printk 等级比控制台等级高时,会打印在终端上,可以通过
cat /proc/sys/kernel/printk
查看系统控制台等级,出来的四个参数依次为- 当前控制台日志级别
- 默认消息日志级别
- 最小的控制台级别
- 默认控制台日志级别
打印内核所有打印信息:dmesg
- 内核log缓冲区大小有限制,缓冲区数据可能被冲掉
声明文件
MODULE_AUTHOR("lx"); //声明作者
MODULE_DESCRIPTION("功能描述"); //功能描述
MODULE_VERSION("1.0"); //版本
MODULE_LICENSE("GPL v2");////告诉内核该模块具有GNU公共许可证
- MODULE_LICENSE():表示模块代码接受的软件许可协议,Linux内核遵循GPL V2开源协议,内核模块与linux内核保持一致即可。
- MODULE_AUTHOR():描述模块的作者信息
- MODULE_DESCRIPTION():对模块的简单介绍
- MODULE_ALIAS():给模块设置一个别名
- 声明文件为宏定义,最后编译器会将这些信息放在编译生成的ELF文件的一个特殊的段中。
编译生成库文件
- 修改上层(kernel/drivers/char/)Kconfig 添加
source "drivers/char/lx_hello/Kconfig"
- 修改上层(kernel/drivers/char/)Makefiel 添加
obj-$(CONFIG_LX_HELLO) += lx_hello/
- 修改内核配置文件 (kernel/arch/arm64/configs/rockchip_defconfig)添加
CONFIG_LX_HELLO=m
- m 代表编译成模块 y 代表直接编译进内核 n 表示内核不支持该模块
- 选择m编译后生成ko文件会放在当前目录下
内核模块加载/卸载
-
使用insmod命令加载
-
使用rmmod命令卸载