注意:这篇文章上次更新于591天前,文章内容可能已经过时。
This article was last updated591 days ago, the content may be outdated.
本文编写一个简单的 Linux 内核模块,然后在 QEMU 中使用 insmod 加载模块, lsmod 查看模块信息, rmmod 卸载模块来进行测试工作。
实验环境在前文Linux Kernel 从编译到运行 和 为 Linux Kernel 添加系统调用 中已经准备好了,这里不再赘述。
编译模块的工作流程如下:
-
环境准备
-
编写模块代码
-
编写 Makefile
-
编译模块
-
加载、卸载测试
环境准备
在前文 为 Linux Kernel 添加系统调用 中我们使用 make bzImage 编译内核,这个命令不会编译模块,也不会生成模块导出的符号。
所谓模块导出的符号,就是模块中的函数或者变量,如果其他模块或者内核代码需要调用模块中的函数或者变量,就需要这些符号。
在 Linux Kernel 中,模块导出的符号是通过 EXPORT_SYMBOL 和 EXPORT_SYMBOL_GPL 来导出的。
编译模块需要使用 make modules 命令,这个命令会编译模块,并且生成模块导出的符号。对应的文件是 Module.symvers。
在编写我们自己的模块之前,我们需要先编译内核,然后生成 Module.symvers 文件。
编译内核在前文中已经完成,这里不再重复编译,我们需要编译模块,并生成 Module.symvers 文件。
1 | # 切换到内核源码目录 |
这个过程比 make bzImage 要慢一些。
编译完成后,如果我们在 ~/Desktop/kernel/linux 还看不到 Module.symvers 文件,需要执行 make modules_prepare 命令。
1 | make modules_prepare |
执行完成后,我们就可以在 ~/Desktop/kernel/linux 目录下看到 Module.symvers 文件了。
1 | wgxls@server:~/Desktop/kernel/linux$ file Module.symvers |
Module.symvers 文件是一个文本文件,里面记录了模块导出的符号。
它的基本格式如下:
1 | <符号地址> <符号名称> <所在模块> <导出类型> |
这是我的 Module.symvers 文件
编写模块代码
来到上文为 Linux Kernel 添加系统调用中的 workspace 目录,创建一个新的目录。
1 | mkdir my_module_demo |
先写一个最简单的模块,只包含加载和退出函数。
创建 my_module.c touch my_module.c
写入以下内容。
1 |
|
编写 Makefile
为 my_module 编写 Makefile 文件,其中 -C 后面跟 linux 内核代码的根路径。
1 | obj-m += my_module.o |
编译模块
截至目前,workspace 目录下的内容如下:
1 | wgxls@server:~/Desktop/kernel/workspace$ tree -L 2 |
进入到 my_module_demo 目录下,执行 make 命令。
1 | wgxls@server:~/Desktop/kernel/workspace/my_module_demo$ make |
编译完成后,在当前目录下会生成 my_module.ko
1 | wgxls@server:~/Desktop/kernel/workspace/my_module_demo$ file my_module.ko |
测试运行
最后,我们在 QEMU 的环境下测试这个模块的安装和卸载。
首先把 my_module.ko 拷贝到 initramfs 目录下。
1 | cp my_module.ko ../initramfs/ |
然后重新制作 initramfs
1 | wgxls@server:~/Desktop/kernel/workspace$ make clean |
启动 QEMU
1 | wgxls@server:~/Desktop/kernel/workspace$ make run |

安装模块

这里除了打印了 Hello, world! 之外,还报了一个模块签名验证失败的信息,同时将内核标记为污染。
暂时忽略这些信息。
使用 lsmod 来查看模块。
1 | ~ # lsmod | grep -ie "my" |
使用 rmmod 来卸载模块。

当我们第二次安装模块时,就不会再次打印模块验证失败的消息了。

The End…


