背景
参考《奔跑吧 Linux内核》4.1章的教程,动手完成第一个内核模块的编译。
Linux Kernel Workbook
内核模块代码
#include<linux/init.h>
#include<linux/module.h>
// __init是链接属性,表示把函数放到.init.text位置
static int __init demo_init(void)
{
printk("demo init!\n");
return 0;
}
//__exit是链接属性,表示把函数放到.exit.text位置
static void __exit demo_exit(void)
{
printk("demo exit!\n");
}
// 设置入口点,module_init是加载模块时将调用的第一个函数
module_init(demo_init);
// module_exit() 是从系统中卸载模块时调用的函数
module_exit(demo_exit);
//执行modinfo时会显示模块相关的信息
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ben Shushu");
MODULE_DESCRIPTION("This my first kernel module");
MODULE_ALIAS("demo");
__init和__exit的定义
#define __init __attribute__ ((__section__ (".init.text"))) __cold
#define __exit __attribute__ (( __section__ (".exit.text"))) __cold
模块编译
# 你的模块的名字
NAME = hello
# 内核的源码头文件目录
KDIR := /usr/lib/modules/$(shell uname -r)/build/
# 最后生成的*.ko的名字,与前面的NAME对应
obj-m := $(NAME).o
# 源文件的名字.o
$(NAME)-objs := hello_world.o
# 当前目录,给之后的M赋值
PWD := $(shell pwd)
# 进入内核源码头文件目录,调用该目录下的Makefile
# 设置M=PWD,M是内核源码头文件目录下的Makefile的一个变量
# 用来指示生成的模块存放的目录
build:
$(MAKE) -C $(KDIR) M=$(PWD) modules
# 这样clean比较方便
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
- make -C 指定Makefile的路径,KDIR即内核模块Makefile的路径
- M指定了当前模块的路径
模块的名字不能和.o的名字重名
执行make编译模块

image.png
安装和卸载模块
#安装模块
sudo insmod hello.ko
#卸载模块
sudo rmmod hello
#查询已经安装的模块
sudo lsmod
#查询模块日志
sudo dmesg
#查询模块信息
modinfo hello.ko

image.png
问题记录
- Ubuntu20 内核版本依赖gcc-12
sudo apt install gcc-12
#查询gcc12安装位置
whereis gcc-12
#查询结果gcc: /usr/bin/gcc /usr/lib/gcc /usr/share/gcc /usr/share/man/man1/gcc.1.gz
2.makefile miss separator
makefile只支持TAB,vscode默认将tab转成了4个空格