搭建驱动开发环境
安装linux kernel的头文件
开发Linux驱动之前,需要安装linux的一系列头文件,在arch linux上可以使用sudo pacman -S linux-headers
安装所需的头文件
在安装之后,可以在/usr/src/linux
下面看到开发驱动必须的头文件目录
配置vscode
在vscode中下载C/C++插件,使用Ctrl+Shift+p
创建C配置文件
当配置文件创建成功以后,需要在配置文件中设置对应的头文件路径,需要设置如下三个头文件路径:
/usr/src/linux/include
/usr/src/linux/arch/x86/include
/usr/src/linux/arch/x86/include/generated
之后得到的配置文件为:
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/**",
"/usr/src/linux/include",
"/usr/src/linux/arch/x86/include",
"/usr/src/linux/arch/x86/include/generated"
],
"defines": [],
"compilerPath": "/usr/bin/gcc",
"cStandard": "gnu17",
"cppStandard": "gnu++17",
"intelliSenseMode": "linux-gcc-x64"
}
],
"version": 4
}
编写Hello World模块
linux的driver有两种方法,一种是init方法,它会在该模块装载进kernel的时候调用,另一种是exit方法,它会在模块被卸载的时候调用
在编写driver之前,需要导入两个头文件linux/module.h
和linux/init.h
,在kernel中无法调用普通的printf来打印,需要调用内核提供的输出函数printk来打印信息
最终的hello.c如下:
#include <linux/module.h>
#include <linux/init.h>
int __init test_init(void){
printk("Hello Kernel\n");
return 0;
}
void __exit test_exit(void){
printk("Goodbye Kernel\n");
}
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");
这里最关键的是需要调用module_init
来注册模块被加载进kernel的时候调用的init方法和module_exit
来注册模块被卸载的时候调用的exit方法
之后编写make file
DRIVER_NAME = hello # 该名称需要和driver的文件名一致
KERNEL_DIR = /lib/modules/$(shell uname -r)/build # 内核模块的地址,固定在/lib/modules/xxx/build下
MODULE_DIR:= $(shell pwd) # 编译出内核模块的输出地址
obj-m:=$(DRIVER_NAME).o # 设置.o的文件名
modules:
make -C $(KERNEL_DIR) M=$(MODULE_DIR) modules
rm -f *.o *.mod *.mod.c *.order *.symvers .*.cmd
clean:
rm -f *.o *.mod.c .*.*.cmd *.ko
之后调用make即可编译出驱动文件:
装载驱动和卸载驱动
当得到.ko文件之后,就可以装载驱动到kernel中了,可以调用sudo insmod hello.ko
将模块装载进kernel,之后使用dmesg
即可看到驱动init函数的输出:
通过lsmod
可以查询装载到内核的模块:
通过modinfo
可以查看内核模块的一些信息:
最后可以通过sudo rmmod hello
卸载内核驱动,并且通过dmesg可以看到exit函数被调用: