文章目录
- 模块实现
- 编译模块的 makefile
- 编译报错解决
- 模块编译日志
- 自动化
- 模块安装
- 模块卸载
模块实现
新建 my_module.c
文件
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
static int __init module_base_init(void)
{
printk("base module init!\r\n");
return 0;
}
static void __exit module_base_exit(void)
{
printk("base module exit!\r\n");
}
module_init(module_base_init);
module_exit(module_base_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("tyustli");
编译模块的 makefile
新建 Makefile
文件
# 指定内核路径
KERNELDIR := /home/tyustli/code/open_source/kernel/linux-6.5.7
# 指定当前路径
CURRENT_PATH := $(shell pwd)
# 指定编译的模块名
obj-m := my_module.o
build: kernel_modules
kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
编译报错解决
此时如果直接在模块路径执行 make
会有 如下警告和报错,cc1: error: cannot load plugin ./scripts/gcc-plugins/arm_ssp_per_task_plugin.so: ./scripts/gcc-plugins/arm_ssp_per_task_plugin.so: undefined symbol: _Z16gen_load_tp_hardP7rtx_def
完整日志如下
make -C /home/tyustli/code/open_source/kernel/linux-6.5.7 M=/home/tyustli/code/qemu_code/linux_driver/0001_module_init modules
make[1]: Entering directory '/home/tyustli/code/open_source/kernel/linux-6.5.7'
warning: the compiler differs from the one used to build the kernel
The kernel was built by: arm-none-linux-gnueabihf-gcc (GNU Toolchain for the A-profile Architecture 10.3-2021.07 (arm-10.29)) 10.3.1 20210621
You are using: gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
CC [M] /home/tyustli/code/qemu_code/linux_driver/0001_module_init/my_module.o
cc1: error: cannot load plugin ./scripts/gcc-plugins/arm_ssp_per_task_plugin.so: ./scripts/gcc-plugins/arm_ssp_per_task_plugin.so: undefined symbol: _Z16gen_load_tp_hardP7rtx_def
make[3]: *** [scripts/Makefile.build:243: /home/tyustli/code/qemu_code/linux_driver/0001_module_init/my_module.o] Error 1
make[2]: *** [/home/tyustli/code/open_source/kernel/linux-6.5.7/Makefile:2037: /home/tyustli/code/qemu_code/linux_driver/0001_module_init] Error 2
make[1]: *** [Makefile:237: __sub-make] Error 2
make[1]: Leaving directory '/home/tyustli/code/open_source/kernel/linux-6.5.7'
make: *** [Makefile:11: kernel_modules] Error 2
这是因为前面编译内核的时候 ARCH
和 CROSS_COMPILE
是通过 shell
脚本传进去的,编译模块的时候又没有指定这些。
解决方法就是在内核顶层的 Makefile
直接定义这两个变量(有点粗暴)
ARCH = arm
CROSS_COMPILE = arm-none-linux-gnueabihf-
模块编译日志
make -C /home/tyustli/code/open_source/kernel/linux-6.5.7 M=/home/tyustli/code/qemu_code/linux_driver/0001_module_init modules
make[1]: Entering directory '/home/tyustli/code/open_source/kernel/linux-6.5.7'
CC [M] /home/tyustli/code/qemu_code/linux_driver/0001_module_init/my_module.o
MODPOST /home/tyustli/code/qemu_code/linux_driver/0001_module_init/Module.symvers
CC [M] /home/tyustli/code/qemu_code/linux_driver/0001_module_init/my_module.mod.o
LD [M] /home/tyustli/code/qemu_code/linux_driver/0001_module_init/my_module.ko
make[1]: Leaving directory '/home/tyustli/code/open_source/kernel/linux-6.5.7'
自动化
模块编译好之后,最好的方法就是将 rootfs 设置为 nfs
,这样直接将编译的 ko 放到网络文件系统中,直接启动内核即可。
由于目前使用的是 ubuntu + wifi
的形式,如果想让 qemu 联网,需要建立网桥,但是 wifi
网卡没有处于 AP 模式,处于 Managed 模式的无线网卡没有足够多的信息做网桥,只能转换成 master
模式
iwconfig wlp2s0 mode master
结果网卡不支持
Error for wireless request "Set Mode" (8B06) :
SET failed on device wlp2s0 ; Operation not permitted.
只能通过软件 hostapd
来实现。。。。。。
这个有点复杂,超出了研究 linux driver 的目的。
解决方法就是,每次生成 xxx.ko
之后,将生成的 ko 文件拷贝到根文件目录下,然后重新打包 rootfs
根文件系统。这样 linux 启动之后模块就在 rootfs 根文件系统中
# 将生成的 .ko 文件拷贝到根文件系统的 roorfs 中
cp ./my_module.ko /home/tyustli/code/open_source/busybox/rootfs/dev
# 切换到根文件系统目录
cd /home/tyustli/code/open_source/busybox
# 生成虚拟 SD 卡系统镜像
sudo dd if=/dev/zero of=rootfs.ext3 bs=1M count=32
# 格式化镜像
sudo mkfs.ext3 rootfs.ext3
#将文件复制到镜像中
sudo mkdir tmpfs_rootfs
sudo mount -t ext3 rootfs.ext3 tmpfs_rootfs/ -o loop
sudo cp -r rootfs/* tmpfs_rootfs/
sudo umount tmpfs_rootfs
rmdir tmpfs_rootfs
这里 rootfs 就制作好了,重新启动 linux
sudo qemu-system-arm -M vexpress-a9 -m 512M \
-kernel /home/tyustli/code/open_source/kernel/linux-6.5.7/arch/arm/boot/zImage \
-dtb /home/tyustli/code/open_source/kernel/linux-6.5.7/arch/arm/boot/dts/arm/vexpress-v2p-ca9.dtb -nographic \
-append "root=/dev/mmcblk0 rw console=ttyAMA0" \
-sd /home/tyustli/code/open_source/busybox/rootfs.ext3
脚本源码
# 参数解析
# ./my_module_build.sh para1 para2(可选)
# 脚本名称 指定模块路径 是否执行 make clean 命令
# 判断 shell 脚本有几个参数,如果没有指定 module 目录, shell 脚本就报错退出
if [ $# -eq 0 ]; then
echo "Incorrect number of arguments for command
Usage: my_module_build.sh <module_dir> build your own module"
exit
fi
# 切换到指定的目录
cd $1
# 如果是清除工程,就执行 make clean 命令
if [ "$2" == "clean" ]; then
make clean
exit
fi
# 编译指定目录的模块
make
# 将生成的 .ko 文件拷贝到根文件系统的 roorfs 中
cp ./my_module.ko /home/tyustli/code/open_source/busybox/rootfs/dev
# 切换到根文件系统目录
cd /home/tyustli/code/open_source/busybox
# 生成虚拟 SD 卡系统镜像
sudo dd if=/dev/zero of=rootfs.ext3 bs=1M count=32
# 格式化镜像
sudo mkfs.ext3 rootfs.ext3
#将文件复制到镜像中
sudo mkdir tmpfs_rootfs
sudo mount -t ext3 rootfs.ext3 tmpfs_rootfs/ -o loop
sudo cp -r rootfs/* tmpfs_rootfs/
sudo umount tmpfs_rootfs
rmdir tmpfs_rootfs
# 切换回指定的目录
cd $1
# 启动 kernel
sudo qemu-system-arm -M vexpress-a9 -m 512M \
-kernel /home/tyustli/code/open_source/kernel/linux-6.5.7/arch/arm/boot/zImage \
-dtb /home/tyustli/code/open_source/kernel/linux-6.5.7/arch/arm/boot/dts/arm/vexpress-v2p-ca9.dtb -nographic \
-append "root=/dev/mmcblk0 rw console=ttyAMA0" \
-sd /home/tyustli/code/open_source/busybox/rootfs.ext3
用法
./my_module_build.sh 0001_module_init/
模块安装
查看模块文件是否存在
ls /dev/my_module.ko
模块安装
/dev/my_module.ko
base module init
和 module_base_init
打印的信息一致
模块卸载
rmmod my_module
base module exit
和 module_base_exit
打印的信息一致