一、前言
实验环境:
系统:Ubuntu 23.04
内核:Linux-6.2.0-39-generic
二、具体步骤
1、环境准备
//更新一下
sudo apt update
//安装构建工具:build-essential包,这个包包括了ggc/g++编译器和make工具等。
sudo apt install build-essential
//安装内核头文件:安装与当前运行内核版本匹配的头文件
//可以uname-r这个指令查看内核版本,以6.2.0-39为例
//这个指令很有可能无法定位软件包
//可以用sudo apt search linux-headers-6.2 搜索以下6.2版本的软件包都有哪些
sudo apt install linux-headers-6.2.0-39-generic
这个是我搜索结果中的一条。
2、编写模块代码
首先需要创建一个包含模块的源代码的C文件。
以下是一个简单的示例:
//这里可以选择你喜欢的编译器vim、gedit等都可以,这里我用的是nano
nano hello_world.c
#include <linux/module.h> // 必需的,包含对模块的支持
#include <linux/kernel.h> // 包含宏如KERN_INFO
#include <linux/init.h> // 包含__init和__exit宏
// 模块初始化函数
static int __init hello_world_init(void) {
printk(KERN_INFO "Hello, world!\n");
return 0; // 如果模块成功加载返回0,否则返回非0值
}
// 模块卸载函数
static void __exit hello_world_exit(void) {
printk(KERN_INFO "Goodbye\n");
}
module_init(hello_world_init); // 宏用于加载模块
module_exit(hello_world_exit); // 宏用于卸载模块
//模块的元数据,其中声明许可证是必须有的,其他的可写可不写
MODULE_LICENSE("GPL"); // 声明许可证
MODULE_AUTHOR("Your Name"); // 声明作者
MODULE_DESCRIPTION("A simple Hello World Module"); // 声明模块描述
MODULE_VERSION("1.0"); // 声明模块版本
3、编写Makefile
nano Makefile
//内核源代码路径
KERNEL_DIR=/home/z/linux-system/linux-6.2
//模块对象文件
obj-m += characterdevice.o
all://默认目标用于编译模块
make -C $(KERNEL_DIR) M=$(PWD) modules
clean://清理编译生成的文件
make -C $(KERNEL_DIR) M=$(PWD) clean
下面给出一个交叉编译的例子,需要添加交叉编译器的前缀和路径
4、编译模块
make
编译会生成一个.ko文件
5、加载模块
insmod hello_world.ko
成功输出Hello_world
6、验证
dmesg | tail
这个是查看日志的指令,上面也可以看到加载模块的信息
7、卸载模块
rmmod hello_world
输出Goodbye
三、可能出现的错误
1、缺失文件
解决:重新编译一下内核。
有时候编译器更新了,和编译内核时的编译器版本不一致也会报错
2、分隔符缺失
这种情况常见于复制过来的Makefile
这个时候make前面的是空格而不是TAB,改成TAB就好了