根据上一章的配置,我们的VS编辑器已经可以开发驱动了。那么按照程序员的国际惯例,第一个应用程序Hello world,而开始我们内核驱动的第一个程序Hello kernel。
驱动代码
首先明确一点,驱动函数里没有main函数。驱动代码的功能基本都由各种功能的回调函数注册实现。
比如USB设备的插入和拔出就分别是两个不同的,可以注册回调函数的事件。再比如设备文件接口被应用程序open、close、read、write等事件,也都是通过注册回调来实现具体功能的。
在VS code中新建C文件,如module_test.c,输入如下代码:
#include <linux/init.h>
#include <linux/module.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"); //声明遵循协议
Makefile
在VS code中新建Makefile文件,输入如下:
这是驱动的编译命令,它指定了编译依赖的内核源码路径,编译后并清除了临时文件。如果不懂也没有关系,只需要确保DRIVER_NAME的值和C源文件的名字一致,其他照抄即可,以后再看兴趣和需要去了解Makefile相关知识。
DRIVER_NAME = module_test
KERNEL_DIR = /lib/modules/$(shell uname -r)/build
MODULE_DIR:= $(shell pwd)
obj-m:=$(DRIVER_NAME).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文件,这就是驱动程序的执行文件
用modinfo命令查看一下我们编译出来的ko文件
modinfo module_test.ko
可以看见我们代码中声明GPL许可,和我们编译的5.4的内核版本
用insmod命令将ko文件加载到内核驱动当中,
然后用lsmod命令查看一下已经加载的驱动中有没有kernel_test模块,
并且用dmesg命令查看printk打印到内核日志里的字符串
sudo insmod module_test.ko
lsmod | grep module_test
dmesg
此时可以看到成功加载的module_test模块
dmesg可以看到最后一行的Hello Kernel,这说明当驱动文件被insmod命令加载到内核中的时候,回调函数test_init被调用。
再检查一下卸载驱动时,是不是也会调用对应的test_exit函数。
sudo rmmod module_test
dmesg
如下:
总结
modinfo用来查看.ko文件的详细属性
insmod用来加载驱动.ko文件
lsmod用来查看列出已经加载的驱动,太多不好找就用管道加grep定位
rmmod用来卸载已经加载的驱动
dmesg用来查看内核调试的打印信息,加 -C命令可以清空缓存