Linux内核开发01-编写Linux内核模块

95次阅读
没有评论

共计 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命令加载一个内核模块时,系统首先会在以上三个路径中搜索是否存在该模块,若存在,系统会将其加载到内存中。若以上三个路径都没有找到该模块,则系统会返回“找不到该模块”的错误。

此外就算复制过去后还需要更新依赖文件,否则依旧无法找到对应的模块

  1. 将编译好的模块放入/lib/module/uname -r下,
  2. 执行命令depmod更新modules.dep文件(任何目录)
  3. 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!

Linux内核开发01-编写Linux内核模块

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

8 参考文献

正文完
 
landery
版权声明:本站原创文章,由 landery 2023-06-26发表,共计3037字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)