注意:这篇文章上次更新于626天前,文章内容可能已经过时。
This article was last updated626 days ago, the content may be outdated.
本文编写一个简单的 Linux 内核模块,然后在 QEMU 中使用 insmod 加载模块, lsmod 查看模块信息, rmmod 卸载模块来进行测试工作。
实验环境在前文Linux Kernel 从编译到运行 和 为 Linux Kernel 添加系统调用 中已经准备好了,这里不再赘述。
编译模块的工作流程如下:
-
环境准备
-
编写模块代码
-
编写 Makefile
-
编译模块
-
加载、卸载测试
This article writes a simple Linux kernel module, then tests it in QEMU by loading it with insmod, checking its info with lsmod, and unloading it with rmmod.
The experimental environment was already prepared in Linux Kernel: From Compilation to Running and Adding a System Call to the Linux Kernel, so I won’t repeat it here.
The module compilation workflow is as follows:
-
Environment preparation
-
Writing the module code
-
Writing the Makefile
-
Compiling the module
-
Load/unload testing
环境准备
在前文 为 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…
Environment Preparation
In Adding a System Call to the Linux Kernel, we used make bzImage to compile the kernel. This command does not compile modules, nor does it generate the symbols exported by modules.
The so-called exported symbols are functions or variables in modules. If other modules or kernel code need to call a module’s functions or variables, they need these symbols.
In the Linux kernel, exported module symbols are exported via EXPORT_SYMBOL and EXPORT_SYMBOL_GPL.
To compile modules, you need the make modules command, which compiles modules and generates the exported module symbols. The corresponding file is Module.symvers.
Before writing our own module, we need to compile the kernel first, then generate the Module.symvers file.
The kernel compilation was already done in the previous article, so we won’t repeat it. We need to compile the modules and generate the Module.symvers file.
1 | # Switch to the kernel source directory |
This process is a bit slower than make bzImage.
After compilation, if you still can’t see the Module.symvers file in ~/Desktop/kernel/linux, run the make modules_prepare command.
1 | make modules_prepare |
After that, you can see the Module.symvers file in the ~/Desktop/kernel/linux directory.
1 | wgxls@server:~/Desktop/kernel/linux$ file Module.symvers |
The Module.symvers file is a text file that records the symbols exported by modules.
Its basic format is as follows:
1 | <symbol address> <symbol name> <module> <export type> |
This is my Module.symvers file
Writing the Module Code
Go to the workspace directory from Adding a System Call to the Linux Kernel, and create a new directory.
1 | mkdir my_module_demo |
First write the simplest module, containing only init and exit functions.
Create my_module.c touch my_module.c
Write the following content.
1 |
|
Writing the Makefile
Write a Makefile for my_module, where the path after -C is the root path of the linux kernel source code.
1 | obj-m += my_module.o |
Compiling the Module
So far, the contents of the workspace directory are as follows:
1 | wgxls@server:~/Desktop/kernel/workspace$ tree -L 2 |
Enter the my_module_demo directory and run the make command.
1 | wgxls@server:~/Desktop/kernel/workspace/my_module_demo$ make |
After compilation, my_module.ko will be generated in the current directory.
1 | wgxls@server:~/Desktop/kernel/workspace/my_module_demo$ file my_module.ko |
Test Run
Finally, we test the installation and removal of this module in the QEMU environment.
First, copy my_module.ko to the initramfs directory.
1 | cp my_module.ko ../initramfs/ |
Then rebuild the initramfs
1 | wgxls@server:~/Desktop/kernel/workspace$ make clean |
Boot QEMU
1 | wgxls@server:~/Desktop/kernel/workspace$ make run |

Install the module

Besides printing Hello, world!, there’s also a module signature verification failure message, and the kernel is marked as tainted.
Ignore these messages for now.
Use lsmod to view the module.
1 | ~ # lsmod | grep -ie "my" |
Use rmmod to unload the module.

When we install the module a second time, the module verification failure message won’t be printed again.

The End…


