Linux驱动开发 ---- 2_深入理解内核模块
目录
- Linux驱动开发 ---- 2_深入理解内核模块
- 学习目标
- 时间安排建议
- 理论学习
- 1. 什么是内核模块?
- 2. 模块加载与卸载
- 3. 内核模块开发基础
- 实践任务
- 任务1:准备开发环境
- 任务2:编写简单内核模块
- 任务3:编写Makefile
- 任务4:编译和测试模块
- 任务5:清理环境
- 代码详解
- `hello_module.c`详解
- `Makefile`详解
- 注意事项
- 总结
学习目标
- 理解Linux内核模块的基本概念和工作原理。
- 掌握模块的加载(
insmod
)、卸载(rmmod
)和参数传递。 - 编写并测试一个简单的内核模块。
时间安排建议
- 理论学习(1-2小时):学习内核模块的基础知识。
- 实践(1小时):编写、编译和测试内核模块。
理论学习
1. 什么是内核模块?
- 内核模块是Linux内核的可加载扩展,可以动态加载到内核中,无需重启系统。
- 优点:提高灵活性,减少内核体积。
- 文件后缀:
.ko
(Kernel Object)。 - 示例:USB驱动、网络驱动通常以模块形式存在。
2. 模块加载与卸载
insmod
:加载模块到内核。- 用法:
insmod module.ko [参数]
- 用法:
rmmod
:卸载模块。- 用法:
rmmod module_name
- 用法:
lsmod
:查看已加载的模块。- 模块参数:可以在加载时传递参数给模块。
- 示例:
insmod my_module.ko my_param=5
- 示例:
3. 内核模块开发基础
- 基本结构:
- 初始化函数(加载时调用)。
- 退出函数(卸载时调用)。
- 常用头文件:
<linux/module.h>
、<linux/kernel.h>
。 - 编译:需要内核源码和
Makefile
。
实践任务
任务1:准备开发环境
-
检查内核版本:
uname -r
- 示例输出:
5.15.0-73-generic
(Ubuntu 22.04默认版本)。
- 示例输出:
-
安装内核头文件和开发工具:
sudo apt install linux-headers-$(uname -r) build-essential
- 创建工作目录:
mkdir ~/kernel_modules && cd ~/kernel_modules
任务2:编写简单内核模块
- 创建文件
hello_module.c
:nano hello_module.c
- 输入以下代码:
#include <linux/module.h> // 内核模块必需头文件
#include <linux/kernel.h> // 提供printk等内核函数
#include <linux/init.h> // 定义模块初始化和退出宏// 定义一个模块参数
static int my_param = 0;
module_param(my_param, int, S_IRUGO); // S_IRUGO表示参数可读// 模块初始化函数,加载时调用
static int __init hello_init(void) {printk(KERN_INFO "Hello, World! Parameter: %d\n", my_param);return 0; // 返回0表示成功
}// 模块退出函数,卸载时调用
static void __exit hello_exit(void) {printk(KERN_INFO "Goodbye, World!\n");
}// 注册初始化和退出函数
module_init(hello_init);
module_exit(hello_exit);// 模块元信息
MODULE_LICENSE("GPL"); // 许可证
MODULE_AUTHOR("Your Name"); // 作者
MODULE_DESCRIPTION("A simple hello world module"); // 描述
任务3:编写Makefile
-
创建文件
Makefile
:nano Makefile
-
输入以下内容:
obj-m += hello_module.oall:make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modulesclean:make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
任务4:编译和测试模块
-
编译模块:
make
- 输出文件:
hello_module.ko
。
- 输出文件:
-
加载模块:
sudo insmod hello_module.ko my_param=42
-
查看日志:
dmesg | tail
- 预期输出:
Hello, World! Parameter: 42
。
- 预期输出:
-
检查已加载模块:
lsmod | grep hello
-
卸载模块:
sudo rmmod hello_module
-
再次查看日志:
dmesg | tail
- 预期输出:
Goodbye, World!
。
- 预期输出:
任务5:清理环境
make clean
代码详解
hello_module.c
详解
#include <linux/module.h>
:提供模块相关宏和函数。printk(KERN_INFO ...)
:内核日志函数,KERN_INFO
表示信息级别,输出到/var/log/kern.log
或dmesg
。module_param
:定义可传递的参数,my_param
是整数类型,S_IRUGO
表示全局可读。__init
和__exit
:标记初始化和退出函数,__init
函数在加载后释放内存。MODULE_LICENSE("GPL")
:声明模块遵循GPL许可证,避免内核污染警告。
Makefile
详解
obj-m += hello_module.o
:指定目标模块。-C /lib/modules/...
:指向内核源码目录。M=$(PWD)
:指定当前目录为模块构建路径。
注意事项
- 权限问题:加载模块需要
sudo
,否则会提示权限不足。 - 日志查看:
dmesg
可能包含大量信息,用tail
筛选最新日志。 - 内核版本匹配:确保
linux-headers
版本与当前内核一致(uname -r
)。 - 实践巩固:尝试修改
my_param
值或日志内容,重新编译测试。
总结
完成以上内容后,您将:
- 理解内核模块的作用和基本结构。
- 掌握模块的编写、编译、加载和卸载。
- 成功运行一个打印“Hello, World!”的模块,并传递参数。