我们首先来看看内核模块的定义
模块是具有独立功能的程序,可以单独编译,但是不能单独运行。模块在运行时,会被链接到内核,作为内核的一部分在内核空间运行。模块通常由一组函数和数据结构组成,用来实现一种文件系统、驱动程序或其他内核上层的功能。
实验环境:
Ubuntu 18.04 + 4.15.0
编写模块
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/init.h>
static int __init lkp_init(void)
{
printk("<1>Hello, World! from the kernel space...\n");
return 0;
}
static void __exit lkp_cleanup(void)
{
printk("<1>Good Bye, World! leaving kernel space...\n");
}
module_init(lkp_init); // 注册模块
module_exit(lkp_cleanup); // 注销模块
MODULE_LICENSE("GPL"); //告诉内核该模块具有GNU公共许可证
- module.h头文件中包含了对模块的结构定义以及模块的版本控制
- kernel.h包含了常用的内核函数
- init.h包含宏__init和__exit,__init告诉编译器,这个函数或者变量仅用于初始化,编译程序将所有的__init代码存储到特殊的内存段中,初始化结束后,内存被释放
- module_init()/exit(),向内核注册/注销模块提供新功能
编写Makefile文件
什么是Makefile文件?
Makefile文件就是用一组约定好的语法规则,告诉make命令,怎么样链接和编译程序,带来的好处就是自动化编译。
在当前目录下,创建Makefile文件,比如可以用命令
touch Makefile
在文件中输入以下代码
# Makefile 4.0
obj-m := testKernel.o
CURRENT_PATH := $(shell pwd)
LINUX_KERNEL := $(shell uname -r)
LINUX_KERNEL_PATH := /usr/src/linux-headers-$(LINUX_KERNEL)
all:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
clean:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean
- obj-m := 这是一个赋值语句,说明要使用目标文件testKernel.o建立一个模块
- 后面几条语句都是常见的shell命令
编译文件
直接在当前目录下,在控制台中输入make。对于ubuntu自带的原始的内核文件,遇到如下错误
按照提示,缺少库文件,我们只需要安装一个即可。
sudo apt-get install libelf-dev
安装完成之后,再次编译
至此,编译完成,会在当前目录生成以下文件
插入模块,并且运行
使用insmod插入内核模块
insmod testKernel.ko
检测是否正确插入
lsmod | grep testKernel
模块使用的函数printk()会默认打印系统文件/var/log/syslog(之前ubuntu的版本本日志存放在/var/log/messages),输入以下命令,查看messages是否输出了我们想要的信息。
卸载模块
使用rmmod命令卸载模块
rmmod testKernel
从如下消息中,可以看出卸载成功
以上就是一个简单的内核模块从编译到卸载的过程