共计 3037 个字符,预计需要花费 8 分钟才能阅读完成。
1 为什么要编写内核模块
有很多事情在Ring3层是无法去做的,很多系统相关的数据只能在内核去获取,这样就可以通过编写内核模块来获取数据。众所周知 KVM也是Linux 内核模块。
2 编写代码
mkdir -p /data/ko_example
cd /data/ko_example
创建文件,内容如下:vim ko_example.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("lyy");
MODULE_DESCRIPTION("A simple example Linux module.");
MODULE_VERSION("1.0");
static int __init ko_example_init(void) {
printk(KERN_INFO "ko_example: Hello, World!\n");
return 0;
}
static void __exit ko_example_exit(void) {
printk(KERN_INFO "ko_example: Goodbye, World!\n");
}
module_init(ko_example_init);
module_exit(ko_example_exit);
这是一个最简单的内核模块,只是在加载内核模块的时候输出语句,卸载内核模块时同样输出语句。
-
MODULE_LICENSE,代表模块的许可证,可用列表查看(以下命令在CentOS7.8上运行,其余版本可能有所区别)
-
grep "MODULE_LICENSE" -B 27 /usr/src/kernels/
uname -r
/include/linux/module.h
-
-
printk:不是printf,
KERN_INFO
是一个标志,用于声明应为该行设置的日志记录优先级,并且不带逗号。 -
module_init(ko_example_init); 内核模块编写的特定方法,将自定义方法注册上去,加载内核时模块执行。
-
module_exit(ko_example_exit);内核模块编写的特定方法,将自定义方法注册上去,卸载内核时模块执行。
3 编译内核模块
现在已经写好了一个简单的内核模块ko.example.c,编译我们需要另外构建一个Makefile
文件
vim Makefile
文件内容如下:
# 去掉签名否则会提示错误
CONFIG_MODULE_SIG=n
CONFIG_MODULE_SIG_ALL=n
obj-m += ko_example.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
其中前两行需要,如果没有前两行,加载内核模块时,一般会校验签名,如果内核模块本身没有经过签名,或者签名值与预期值不符,这两种情况都会被认为是签名认证失败,根据策略的不同,签名认证失败可能会导致模块被拒绝加载,也可能是继续正常加载但内核会显示一条警告信息。
如果未加上这两语句,加载模块后会提示如下:
[ 2671.174942] ko_example: loading out-of-tree module taints kernel. [ 2671.175059] ko_example: module verification failed: signature and/or required key missing - tainting kernel
这里我们就不细讲了内核签名有时间再关注。
继续编译。
make
这回会在目录下生成一些文件:
[root@ss ko_example]# ls
ko_example.c ko_example.ko ko_example.mod.c ko_example.mod.o ko_example.o Makefile modules.order Module.symvers
其中ko_example.ko
就是最终的内核模块。
最后,我们清空dmesg输出,方便查看后面的结果。
dmesg -C
4 加载内核模块
加载内核模块可以用两个命令:在这里推荐第一个,第二个操作比较麻烦。
insmod
:不会检查依赖项,本例中可以直接再/data/ko_example目录下运行,modprobe
:会检查并加载依赖项,并且需要放到特定目录,否则会提示找不到模块
insmod
insmod比较简单
insmod ko_example.ko
modprobe
在Linux中,modprobe命令会在以下几个路径中搜索可加载的内核模块:
- /lib/modules/
uname -r
/kernel(内核模块所在的默认目录) - /lib/modules/
uname -r
/extra/(用户自行编译的内核模块所在的目录,一般不需要担心) - /lib/modules/(常见的内核模块目录,一般也不需要担心)
其中,uname -r
为当前系统所使用的内核版本。
当我们使用modprobe命令加载一个内核模块时,系统首先会在以上三个路径中搜索是否存在该模块,若存在,系统会将其加载到内存中。若以上三个路径都没有找到该模块,则系统会返回“找不到该模块”的错误。
此外就算复制过去后还需要更新依赖文件,否则依旧无法找到对应的模块
- 将编译好的模块放入/lib/module/
uname -r
下, - 执行命令
depmod
更新modules.dep文件(任何目录) - modprobe ko_example.ko
5 卸载模块
同样可以两种卸载方式
rmmod ko_example
(在这里推荐使用这个)modprobe -r ko_example
6 查看dmesg输出
[root@ss ko_example]# dmesg
[ 3897.130103] ko_example:Hello, World!
[ 3977.961346] ko_example: Goodbye, World!
[ 4898.483268] perf: interrupt took too long (8862 > 8847), lowering kernel.perf_event_max_sample_rate to 22000
[ 5622.473621] ko_example: Hello, World!
[ 5627.830269] ko_example: Goodbye, World!
7 查看模块信息
可以看到作者、内核版本等
[root@ss ko_example]# modinfo ko_example
filename: /lib/modules/3.10.0-1160.el7.x86_64/kernel/ko_example.ko
version: 1.0
description: A simple example Linux module.
author: lyy
license: GPL
retpoline: Y
rhelversion: 7.9
srcversion: F710CBEBAE01EEEF51C4087
depends:
vermagic: 3.10.0-1160.el7.x86_64 SMP mod_unload modversions