编写一个内核模块
简介
在这篇文章中我介绍了如何编写一个内核模块,从需要安装哪些包,到makefile怎么写.最后是实验结果的展示和对过程中的一些细节的解释
代码地址
https://github.com/liodegwin/kernel/tree/main/module/helloworld
参考
https://linux.cn/article-3251-1.html
https://www.oschina.net/translate/writing-a-simple-linux-kernel-module?print
感谢
Photo by Alex Andrews: https://www.pexels.com/photo/photo-of-fox-sitting-on-ground-2295744/
准备
安装内核模块开发工具包和头文件
fedora安装命令如下
1 | dnf install kernel-devel |
其实只需要内核的头文件就能编译出内核模块,也就是第二个命令
安装的结果就是在lib/modules文件夹下多了很多文件
1 | [liode@liodePC:13:helloworld]$ ls /lib/modules |
每一个对应一个内核版本,我这个系统升级过3次,一共有4个内核版本
对于其中的一个版本,里面的内容如下
1 | [liode@liodePC:14:helloworld]$ ls /lib/modules/5.19.12* |
这里面的build目录里面就是真正的内核头文件了
1 | [liode@liodePC:37:build]$ ls |
build里面有这个makefile,编译的时候会用到,十分重要
编写模块
编写源码
在hello.c中
1 |
|
在Makefile中
1 | obj-m := hello.o |
实验
执行make命令编译出hello.ko
执行sudo insmod hello.ko之后,首先观察打印信息dmesg
1 | [ 5169.258034] hello: loading out-of-tree module taints kernel. |
可以看到在安装好了之后,有打印输出
同时,lsmod看到多了一个hello的模块
1 | [liode@liodePC:96:helloworld]$ lsmod | grep hello |
移除模块之后也有打印,执行sudo rmmod hello
1 | [22528.691648] Goodbye world |
细节
模块的名字是由什么决定的
是makefile中的obj-m参数决定的,和module_init(hello_init)修饰的入口函数名完全没有关系
makefile的写法
总体形式
编译一个模块的make命令是下面这种形式
1 | make -C 内核源码目录 M=模块源码目录 modules |
我的makefile是这样的
1 | obj-m := hello.o |
all这里展开之后是这样
1 | make -C /lib/modules/5.19.12-200.fc36.x86_64/build M=/home/liode/nfs_dir/kernel/helloworld modules |
参数介绍
-C
指的是要进入到后面的目录,找到里面的makefie,对于这里,就是内核源码目录里面的makefile
M
其实是makefile里面需要的一个变量,是内核makefile决定的,不是make工具的一个flag,这个变量指定了模块源文件所在的目录
modules
这个其实和M一样,是makefile中定义的变量,指明了要编译一个模块
总之,首先要指定两个目录,一个内核源码目录,一个模块源码目录。而光有目录还不行,还需要告诉make,要将那一个源文件,编译成哪一个ko文件。这就是obj-m参数的功能c
obj-m:=hello.o
hello.o是hello.c源文件默认生成的目标文件的名字,不能是其他的名字
而这句话的意思是说生成一个叫做hello.ko的ko文件,也就是说,模块的名字和源文件的名字是相同的,如果更加详细的指定不同的名字,我还不知道
KDIR:=/lib/modules/$(shell uname -r)/build
这是内核的头文件的目录,不过是我使用命令下载的,暂时还没有检测过,是否和自己手动下载的源码中头文件的部分完全一致
展开之后是这样
/lib/modules/5.19.12-200.fc36.x86_64/build
make命令详细的解释可以参考下