搭建驱动开发环境

安装linux kernel的头文件

开发Linux驱动之前,需要安装linux的一系列头文件,在arch linux上可以使用sudo pacman -S linux-headers安装所需的头文件

在安装之后,可以在/usr/src/linux下面看到开发驱动必须的头文件目录
image.png

配置vscode

在vscode中下载C/C++插件,使用Ctrl+Shift+p创建C配置文件
image.png
当配置文件创建成功以后,需要在配置文件中设置对应的头文件路径,需要设置如下三个头文件路径:

/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.hlinux/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即可编译出驱动文件:
image.png

image.png

装载驱动和卸载驱动

当得到.ko文件之后,就可以装载驱动到kernel中了,可以调用sudo insmod hello.ko将模块装载进kernel,之后使用dmesg即可看到驱动init函数的输出:
image.png
通过lsmod可以查询装载到内核的模块:
image.png
通过modinfo可以查看内核模块的一些信息:
image.png

最后可以通过sudo rmmod hello卸载内核驱动,并且通过dmesg可以看到exit函数被调用:
image.png