使用Qemu模拟Arm处理器,开发第一个Linux驱动程序

Linux驱动的开发,是程序设计人员的重要工作方向。想掌握驱动的技巧,需要学习Linux的shell知识、内核架构、c语言编程等。本文使用Qemu模拟一个Arm处理器,在其上面运行Linux,然后在上面学习加载驱动。提供一个教程示范这个驱动的C源程序和编译方法。

使用Qemu模拟Arm处理器,运行Linux系统 讲解了如何基于Linux 5.10.0 生成一个可以在Qemu模拟的Arm处理器上运行的Linux系统。有了这个系统,咱们就可以在其上面加载驱动了。驱动分为内置驱动和可加载驱动:内置驱动与 Linux 内核编译在一起,可加载驱动则被编译为单独的 ko 文件。

咱们演示的驱动可执行文件在被称为主机(Host)的电脑上编译,这种编译被称为交叉编译。交叉编译就是这样一种编译:其编译出来的程序在与编译系统不相同的处理器上运行,比如我们在基于x86_64的处理器运行的Linux系统上,编译一个驱动ko,然后这个ko下载到arm处理器的开发版上运行,那么在x86_64上的编译称为交叉编译。交叉编译需要安装交叉编译器,并且将其目录加到运行目录PATH上。下面是具体步骤,也有相应的解释。

# 下载最新的 Linaro GCC 工具链(含 sysroot)
wget https://releases.linaro.org/components/toolchain/binaries/latest-7/arm-linux-gnueabihf/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz
# 解压到指定目录
sudo mkdir -p /opt/arm/
sudo tar -xf gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf.tar.xz -C ~/opt/arm
sudo mv gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf gcc-linaro-7.5.0

现在我们在内核源码目录linux-work子目录drivers目录下增加一个hellodriver目录,这个目录增加一个hellodriver.c和一个Makefile。

//hellodriver.c
#include <linux/module.h>

static int __init hellodriver_init(void)
{
	printk("HanJ %s %s\n" , __FILE__ , __func__ ) ;
	return 0 ;
}
module_init( hellodriver_init ) ;

static void __exit hellodriver_exit(void)
{
	printk("HanJ %s %s\n" , __FILE__ , __func__ ) ;
}
module_exit( hellodriver_exit ) ;

MODULE_AUTHOR("HanJ");
MODULE_DESCRIPTION("This is hellodriver");
MODULE_LICENSE("GPL");

module_ini是module.h文件中定义的带参数宏,用来标记初始化函数,当模块被编译为 .ko 文件时,这个标记会被记录在模块的特殊数据段中。当通过 insmod 加载模块时,内核会解析模块中的标记信息,将参数指定的函数指针(代码中的hellodriver_init)注册到内核的临时初始化队列,紧接着执行这个初始化函数。相对应的,module_exit将注册的函数指针从临时初始化列表中去除。

#Makefile
obj-m += hellodriver.o

这个Makefile只有一行,obj-m表示这个驱动编译成一个单独的ko,m换成y的话,直接编译成内核的一部分,换成n的话则不编译。这个目录的代码不参与内核。

要让Linux内核编译时,能够遍历到这个目录,需要在其上层目录的Makefile最后将本目录加进去。

obj-$(CONFIG_MOST)		+= most/
#HanJ adding 25/7/17
obj-m += hellodriver/

准备了源码和Makefile之后我们在内核根目录,运行buildmodule.sh。可以参考Gitee上的原始脚本文件(linux-work/buildmodule.sh · tdPeak/LinuxSty - Gitee.com)。

export CROSS_COMPILE=arm-linux-gnueabihf-
export ARCH=arm 
export PATH=/opt/arm/gcc-linaro-7.5/bin/:$PATH
git checkout -- drivers/Makefile
make modules

这个脚本,首先指定内核交叉编译头和内核架构(arm),然后将交叉编译器所在目录加入执行目录PATH,接着从git仓库里获取最新版本的Makefile,因为复制内核源码时,会用Linux内核的原始版本覆盖这个Makefile,所以需要更新一下。make modules表示不编译内核镜像,只编译可加载模块。

运行了这个脚本之后,会在hellodriver目录下生成hellodriver.ko。怎么下载到Qemu模拟器上,请从下一讲学习。