本文以 demo 例子的形式演示 Linux kernel 内核模块编译、装载和卸载
常用命令
- 加载kernel模块命令
- insmod
- modprobe # insmod 安装时不处理依赖,仅安装当前模块;modprobe 会安装模块的依赖依次安装
- modinfo # 查看模块信息
- rmmod # 卸载模块
示例
demo 文件
假设编译目录为 /root/hello-module/
,示例编译共涉及两个文件:
$ cat hello.c
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");
/*
* hello module init method
*/
static int hello_init(void)
{
printk(KERN_INFO "hello kernel world!\n");
return 0;
}
/*
* hello module exit method
*/
static void hello_exit(void)
{
printk(KERN_ALERT "bye kernel world!\n");
}
module_init(hello_init);
module_exit(hello_exit);
obj-m := hello.o
KERNEL_DIR := /lib/modules/`uname -r`/build
PWD := $(shell pwd)
default:
make -C ${KERNEL_DIR} M=${PWD} modules
clean:
rm -f *.o *.ko *.mod.c *.mod.o modules.* Module.*
说明:
- obj-m := hello.o # 要生成
hello
模块即 hello.ko
- 若有多个文件生成,则须添加
hello-objs :=file1.o file2.o…..
KERNEL_DIR
# 内核源码目录
PWD
# 当前目录,即:/root/hello-module/
- make
-C ${KERNEL_DIR}
# 指定内核源码目录
M=${PWD} modules
# 指定模块源码目录
环境准备
$ yum install kernel-devel-$(uname -r) kernel-headers-$(uname -r) # 版本需要与当前的 kernel 版本对应
$ cd /lib/modules/`uname -r`/ # 查看 build 链接是否正常,若不正常,需要安装对应版本的 kernel-devel
编译
$ make
make -C /lib/modules/`uname -r`/build M=/root/hello-module modules
make[1]: Entering directory `/usr/src/kernels/3.10.0-1160.6.1.el7.x86_64'
CC [M] /root/hello-module/hello.o
Building modules, stage 2.
MODPOST 1 modules
CC /root/hello-module/hello.mod.o
LD [M] /root/hello-module/hello.ko
make[1]: Leaving directory `/usr/src/kernels/3.10.0-1160.6.1.el7.x86_64'
$ ls -l
total 228
-rw-r--r-- 1 root root 410 Apr 18 07:35 hello.c
-rw-r--r-- 1 root root 104768 Apr 18 07:59 hello.ko
-rw-r--r-- 1 root root 907 Apr 18 07:59 hello.mod.c
-rw-r--r-- 1 root root 60568 Apr 18 07:59 hello.mod.o
-rw-r--r-- 1 root root 47304 Apr 18 07:59 hello.o
-rw-r--r-- 1 root root 188 Apr 18 07:38 Makefile
-rw-r--r-- 1 root root 34 Apr 18 07:59 modules.order
-rw-r--r-- 1 root root 0 Apr 18 07:59 Module.symvers
加载
- 将
hello.ko
模块加载到内核,要使用绝对路径:
$ insmod /root/hello-module/hello.ko
$ dmesg -c
[ 1834.003419] hello kernel world!
$ lsmod | grep hello
hello 12496 0
$ modinfo hello.ko
filename: /root/hello-module/hello.ko
license: Dual BSD/GPL
retpoline: Y
rhelversion: 7.9
srcversion: 039659EB2884A32F2B6B26F
depends:
vermagic: 3.10.0-1160.6.1.el7.x86_64 SMP mod_unload modversions
$ rmmod hello.ko
dmesg -c
或 tail /var/log/messages
查看加载日志:
$ dmesg -c
[ 1907.262481] bye kernel world!
配置
- 黑名单:Linux 禁用功能,可以通过禁用对应的驱动实现
echo "blacklist amd76x_edac" >> /etc/modprobe.d/blacklist.conf
cat <<EOF | sudo tee /etc/modules-load.d/containerd.conf
overlay
br_netfilter
EOF
systemd-modules-load.service
- 作用:系统启动早期的服务,用于从静态的配置文件加载内核模块
- 配置:加载如下路径的配置
$ systemctl cat systemd-modules-load.service
...
ConditionDirectoryNotEmpty=|/lib/modules-load.d
ConditionDirectoryNotEmpty=|/usr/lib/modules-load.d
ConditionDirectoryNotEmpty=|/usr/local/lib/modules-load.d
ConditionDirectoryNotEmpty=|/etc/modules-load.d
ConditionDirectoryNotEmpty=|/run/modules-load.d
...
FAQ
编译kernel版本问题
编译 hello.ko
时用的 kenerl
不是安装机器的 kenerl
版本会报如下错误:
$ insmod hello.ko
insmod: ERROR: could not insert module hello.ko: Invalid module format
$ modprobe hello.ko
modprobe: FATAL: Module hello.ko not found.