The Operating System
07 Oct 2023
创建一个简单的内核模块 #
内核模块文件:
#include <linux/module.h>
#include <linux/init.h>
static int __init my_test_init(void)
{
printk(KERN_EMERG "my first kernel module init\n");
return 0;
}
static void __exit my_test_exit(void)
{
printk("goodbye\n");
}
module_init(my_test_init);
module_exit(my_test_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("rlk");
MODULE_DESCRIPTION("my test kernel module");
MODULE_ALIAS("mytest");
<linux/init.h>
对应的是include/linux/init.h
,包含了module_init()
和module_exit()
的函数声明。module_init
表示这是模块入口,module_exit
告诉内核该模块的退出函数。<linux/module.h>
对应的是include/linux/module.h
包含了MODULE_AUTHOR()
等一些宏的声明。
接着来看如何编译内核模块:
#BASEINCLUDE ?= /home/rlk/rlk/runninglinuxkernel_5.0
BASEINCLUDE ?= /lib/modules/`uname -r`/build
CONFIG_MODULE_SIG=n
mytest-objs := my_test.o
obj-m := mytest.o
all :
$(MAKE) -C $(BASEINCLUDE) M=$(PWD) modules;
clean:
$(MAKE) -C $(BASEINCLUDE) M=$(PWD) clean;
rm -f *.ko;
BASEINCLUDE
指向正在运行 Linux 的内核编译目录。<模块名>-objs := <目标文件>.o
表示该内核模块需要哪些目标文件obj-m := <模块名>.o
表示要生成的模块
插入模块:
03 Oct 2023
第一步先进行源码拉取,这里使用的是一个特别版本:https://github.com/runninglinuxkernel/runninglinuxkernel_5.0.git
我们还需要下载 ARM 编译工具链:https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads (我使用的是10.3的版本)
编译 Kernel #
run_rlk_arm64.sh
这段脚本中提供了下面这几行代码来编译 kernel:
make_kernel_image(){
echo "start build kernel image..."
make debian_defconfig
make -j $JOBCOUNT
}
运行:
./run_rlk_arm64.sh build_kernel
从上面的代码可以看出,编译内核只需要两步:
- 配置编译选项:
make menuconfig
或者make %_defconfig
。menuconfig
是 Linux 提供的一种文字式图形化配置方式,依赖libncurses5-dev
。make %_defconfig
会依赖下面的这几行,在固定的路径去读取相应的 config 文件
# scripts/kconfig/Makefile
%_defconfig: $(obj)/conf
$(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)
make -j$(nproc)
编译
如果是替换本机的 Linux 内核还需要做以下的几步:
3. make modules_install
4. make install
编译 rootfs #
kernel_build=$PWD/rootfs_debian_arm64/usr/src/linux/
prepare_rootfs(){
if [ ! -d $rootfs_path ]; then
echo "decompressing rootfs..."
# split -d -b 80m rootfs_debian_arm64.tar.xz -- rootfs_debian_arm64.part
cat rootfs_debian_arm64.part0* > rootfs_debian_arm64.tar.xz
tar -Jxf rootfs_debian_arm64.tar.xz
fi
}
build_kernel_devel(){
kernver="$(cat include/config/kernel.release)"
echo "kernel version: $kernver"
mkdir -p $kernel_build
rm rootfs_debian_arm64/lib/modules/$kernver/build
cp -a include $kernel_build
cp Makefile .config Module.symvers System.map vmlinux $kernel_build
mkdir -p $kernel_build/arch/arm64/
mkdir -p $kernel_build/arch/arm64/kernel/
cp -a arch/arm64/include $kernel_build/arch/arm64/
cp -a arch/arm64/Makefile $kernel_build/arch/arm64/
cp arch/arm64/kernel/module.lds $kernel_build/arch/arm64/kernel/
ln -s /usr/src/linux rootfs_debian_arm64/lib/modules/$kernver/build
# ln to debian linux-5.0/scripts
ln -s /usr/src/linux-kbuild/scripts rootfs_debian_arm64/usr/src/linux/scripts
#ln -s /usr/src/linux-kbuild/tools rootfs_debian_arm64/usr/src/linux/tools
}
build_rootfs(){
if [ ! -f $rootfs_image ]; then
make install
make modules_install -j $JOBCOUNT
# make headers_install
build_kernel_devel
echo "making image..."
dd if=/dev/zero of=$rootfs_image bs=1M count=$rootfs_size
mkfs.ext4 $rootfs_image
mkdir -p tmpfs
echo "copy data into rootfs..."
mount -t ext4 $rootfs_image tmpfs/ -o loop
cp -af $rootfs_path/* tmpfs/
umount tmpfs
chmod 777 $rootfs_image
rm -rf $rootfs_path
fi
}
使用 Qemu 运行 #
- 编译 QEMU,编译选项记得开启
--enable-kvm --enable-virtfs
- 使用下面的脚本运行
run_qemu_debian(){
cmd="$QEMU -m 1024 -cpu cortex-a57 -smp 4 -M virt,gic-version=3,its=on,iommu=smmuv3\
-nographic $SMP -kernel arch/arm64/boot/Image \
-append \"$kernel_arg $debug_arg $rootfs_arg $crash_arg $dyn_arg\"\
-drive if=none,file=$rootfs_image,format=raw,id=hd0\
-device virtio-blk-device,drive=hd0\
--fsdev local,id=kmod_dev,path=./kmodules,security_model=none\
-device virtio-9p-pci,fsdev=kmod_dev,mount_tag=kmod_mount\
$DBG"
echo "running:"
echo $cmd
eval $cmd
}
调试内核 #
我们使用 gdb 加 qemu 来调试内核