利用qemu编译并运行Linux内核
文章目录
前言
本文将介绍如何利用 qemu 编译并运行 Linux 内核,旨在搭建一个基于 qemu 的 Linux 内核运行环境,实现内核的编译、运行及调试等功能。
具体来说,包括以下内容:
1、成功编译 ARM Linux 内核,生成可在 qemu 中运行的内核镜像;
2、通过 qemu 模拟不同的开发平台(基于 Cortex - A9 的 Versatile Express 开发平台和 ARMv8 架构的平台),并确保内核在模拟环境中稳定运行;
3、实现多种调试方式,通过 QEMU 调试 ARM Linux 内核、调试 ARMv8 的 Linux 内核以及借助 Eclipse+QEMU 进行单步调试内核;
4、为 QEMU 添加文件系统支持,实现文件系统的挂载和使用。
一、如何编译和运行一个ARM Linux内核
1.准备工具
下载linux-4.15内核和busybox工具包,并解压。
由于wget下载太慢,我使用aria2c命令。aria2c是一个支持多协议、多源、断点续传的下载工具,通常比wget更快更稳定。首先安装aria2c:

然后使用aria2c下载:

解压源码


下面安装QEMU,QEMU是一种通用的开源计算机仿真器和虚拟器。

直接编译会出错,需要更改GCC版本。
往系统添加一个arm-linux-guneabi-gcc-7的链接配置并设置优先级。并查看gcc版本:

2.编译最小文件系统
下面利用busybox手工编译一个最小文件系统。 首先安装make-guile包:make-guile 是一个用于构建 Guile 扩展的工具,是为了编译 BusyBox 而安装的依赖。

切换到BusyBox源码目录,并设置环境变量以指定使用ARM架构的交叉编译工具链来编译BusyBox。


空格键选中:

连按两次ESC保存并退出。

命令行输入 make install 后解压的文件夹busybox-1.30.0下会出现这个。



出现的这个文件夹是 _install,它是在编译 BusyBox 时由 make install 命令创建的。这个目录用于存放编译后的可执行文件、库文件、头文件等,模拟了最终的安装环境。
在 BusyBox 的上下文中,make install 命令会将编译生成的文件放置到这个 _install 目录下,而不是直接安装到系统的根目录。这样做的目的是为了方便测试和验证编译结果,同时避免对系统文件造成影响。
将 _install 目录递归复制到 linux-4.15目录下。这样做是为了将解压后的文件和目录结构复制到目标目录,以便后续操作。

进入 _install 目录,创建 etc、dev、mnt 目录以及 etc/init.d/ 目录。

在_install /etc/init.d/目录下新创建一个rcS文件,并写入如下内容。
#!/bin/sh:指定使用 /bin/sh 作为脚本解释器。
mkdir -p /proc:创建 /proc 目录,该目录是虚拟文件系统,包含系统信息。
mkdir -p /tmp:创建 /tmp 目录,用于存放临时文件。
mkdir -p /sys:创建 /sys 目录,该目录是虚拟文件系统,包含设备信息。
mkdir -p /mnt:创建 /mnt 目录,用于挂载文件系统。
/bin/mount -a:挂载所有在 /etc/fstab 文件中定义的文件系统。
mkdir -p /dev/pts:创建 /dev/pts 目录,用于终端设备。
mount -t devpts devpts /dev/pts:挂载 devpts 文件系统到 /dev/pts。
echo /sbin/mdev > /proc/sys/kernel/hotplug:设置 mdev 为热插拔设备管理器。
mdev -s:启动 mdev 服务,自动创建设备文件。

这些步骤是为了在系统启动时进行必要的初始化设置,确保系统能够正常运行。
需要修改_install/etc/init.d/rcS文件需要可执行权限 chmod +x rcS

在_install /etc目录新创建一个fstab文件,并写入如下内容。fstab 文件用于定义系统启动时自动挂载的文件系统。

在_install /etc目录新创建一个inittab文件,并写入如下内容。

写入 inittab 文件的具体内容,定义了系统启动时的行为:
::sysinit:/etc/init.d/rcS:在系统初始化阶段执行 /etc/init.d/rcS 脚本。这个脚本通常包含系统启动时需要执行的初始化命令。
::respawn:-/bin/sh:如果 /bin/sh(shell)进程终止,系统会自动重新启动一个新的 /bin/sh 进程。这确保了 shell 进程始终运行。
::ctrlaltdel:/bin/umount -a -r:当用户按下 Ctrl+Alt+Del 组合键时,执行 /bin/umount -a -r 命令。这个命令会卸载所有挂载的文件系统并重启系统。
在_install/dev目录下创建如下设备节点,需要root权限。

编译内核:

配置initramfs和内核命令行 # 在 “General setup” 中:
- 选中 “Initial RAM filesystem and RAM disk (initramfs/initrd) support” # - 在 “Initramfs source file(s)” 中按下空格填入 “_install” # - 然后exit到"General setup"页面,选择Boot option,清空 “Default kernel command string” # 在 “Kernel Features” 中: # - 选中 "Memory split (2G/2G user/kernel split)"改成3G/1G # - 选中 “High Memory Support”








然后退出保存。
下面用指定的 ARM 架构交叉编译工具链,并行编译生成压缩内核镜像(bzImage),以及构建设备树二进制文件(dtbs)。


使用 qemu-system-arm 工具模拟 Versatile Express 开发平台(基于 Cortex - A9,4 核 ),设置内存为 200M ,指定内核镜像、设备树文件,配置启动参数,并以无图形界面模式运行。


按住ctrl+a 放下后按x可退出qemu。

二、通过QEMU调试ARM Linux内核
1.安装gdb-multiarch
首先安装gdb-multiarch工具,它是支持多种硬件体系架构的GDB版本。
Kernel hacking —>
Compile-time checks and compiler options —>
[*] Compile the kernel with debug info




2.内核调试
首先使用 qemu-system-arm 模拟基于 Cortex - A9 的 Versatile Express 开发平台(4 核 ),分配 200M 内存,指定内核镜像和设备树文件,设置启动参数,以无图形界面模式运行,并开启调试功能(暂停启动等待调试连接、监听默认端口等待调试 )。

新开一个终端,在窗口中输入:


如图所示,GDB开始接管ARM-Linux内核运行,并且到断点处暂停,这时即可使用GDB命令来调试内核。
退出命令: quit

三、通过QEMU调试ARMv8的Linux内核
1.运行ARMv8系统
Linux系统的QEMU工具包里包含了qemu-system-aarch64工具。安装如下工具包。


下面利用 update-alternatives 工具,用于在系统中配置和管理gcc -7和gcc -5版本的 aarch64-linux-gnu-gcc 编译器,包括安装指定版本以及选择使用的版本,接着查看所选 gcc 版本信息 。

下面切换到linux源码目录,设置好架构和交叉编译工具,打开内核配置界面,进行内核详细配置。
并把之前实验1的 _install 文件复制并命名 _install_arm64 文件。







执行make -j4,自动编译和链接程序,-j 是 --jobs 的缩写,-j4 表示让 make 最多允许 4 个编译命令同时执行,即同时启动 4 个任务并行构建,可更有效地利用 CPU 资源,加快编译速度 。

接下来使用 qemu-system-aarch64 启动一个 AArch64 架构的虚拟环境,指定虚拟机类型为 virt 、CPU 为 cortex - a57 ,分配 2048MB 内存、2 个虚拟 CPU 核心,加载指定路径的内核镜像,设置启动参数并将串口输出定向到标准输入输出 。

基于 QEMU 的虚拟化环境已经启动,虚拟机中的操作系统正在进行初始化和硬件驱动的加载等一系列启动流程。输入quit即可退出。
2.调试ARMv8处理器
由于ubuntu 18.04里面的aarch64工具链已经没有了gdb,因为都使用gdb-multiarch这个工具,但是gdb-multiarch在单步调试aarch64的时候有问题,所以我们需要单独下载一个aarch64-linux-gnu-gdb的工具。



配置用户 shell 环境的脚本文件;在 .bashrc 文件中添加指定路径到系统环境变量 PATH,使系统能找到对应工具链的可执行文件;source ~/.bashrc 用于让刚修改的 .bashrc 配置立即生效;aarch64-linux-gnu-gdb -v 查看 aarch64-linux-gnu 架构下 gdb 调试器的版本信息。

在.bashrc文件的最后一行加上:

3.单步调试aarch64的内核
首先保证aarch64-linux-gnu-gcc的版本是5.x的。 cd 切换到 linux - 4.15 目录,使用 qemu - system - aarch64 启动一个无图形界面的 ARM64 虚拟机,设置相关硬件参数并指定内核等;然后通过 ctrl+alt+t 新开窗口,再次进入 linux - 4.15 目录,用 aarch64 - linux - gnu - gdb 以文本用户界面模式调试 vmlinux ,建立与本地 1234 端口的远程连接,在 do_fork 函数处设置断点并继续执行。

四、通过Eclipse+QEMU单步调试内核
1.安装Eclipse-CDT软件
Eclipse-CDT是Eclipse的一个插件,可以提供强大的C/C++编译和编辑功能。
sudo apt install eclipse-cdt
由于优麒麟ubuntu 18.04默认安装的eclipse-cdt不能运行,所以我们到eclipse官网上下载一个。(下载最新版会闪退)
https://www.eclipse.org/downloads/packages/release/oxygen/3/eclipse-ide-cc-developers

下载解压到此处:


下面安装JDK以便Eclipse能够运行


2.创建工程
打开Eclipse菜单,选择“Window”→“Open Perspective”→“C/C++”。
新建一个C/C++的Makefile工程,在“File”→“New”→“Project”中选择“Makefile Project with Exiting Code”,创建一个新的工程.





接下来配置调试选项。选择Eclipse菜单中的“Run”→“Debug Configurations”选项,创建一个“C/C++ Attach to Application”调试选项。双击“C/C++ Attach to Application”可以创建一个新的连接。


右键点击添加,修改信息
Project:选择刚才创建的工程。
C/C++ Appliction:选择编译Linux内核带符号表信息的vmlinux。
Build befor launching:选择“Disable auto build”,如图所示。r
Debugger:选择gdbserver。
GDB debugger:填入gdb-multiarch,如图所示。
Host name or IP addrss:填入localhost。
Port number:填入1234。



单击“Apply”按钮来完成配置。
然后单击“Debug”按钮。 下面在QEMU模拟器中启动一个基于ARM架构的虚拟机,使用vexpress-a9机器模型,分配1024MB内存,加载指定的内核镜像和设备树,不显示图形界面,并启用串行控制台。

在 Eclipse 菜单的“Run”→“Debug History”中选择刚才创建的调试选项,或在快捷菜单中单击“小昆虫” 图标。

在 Eclipse 的 Debugger Console 控制台中输入“file vmlinux”命令, 导入调试文件的符号表;输入“set architecture arm”选择 GDB 支持的 ARM 架构。

在 Debugger Console 中输入“b do_fork”,在 do_fork 函数中设置一个断点。输入“c”命令,开始运行 QEMU 中的 Linux 内核,它会停在 do_fork 函数中。


寄存器:

变量:

五、在QEMU中添加文件系统的支持
1.Linux中创建一个64MB的镜像
首先创建 swap.img 文件

然后通过SD卡的方式加载swap.img到QEMU中


ctrl+a x退出。 下面创建一个ext4文件系统分区。先在Ubuntu中创建一个64MB大小的镜像,方法同上,然后格式化ext4.img成ext4格式。

挂载ext4文件系统需要打开如下配置的选项。


下面挂载SD卡到/mnt目录:


可能遇到的问题
1.在执行 qemu-system-arm 命令启动基于 ARM 架构的 QEMU 虚拟机时,遇到了 PulseAudio 相关的错误信息。

这表明在 QEMU 运行过程中,PulseAudio(一款 Linux 系统中常用的音频服务器)在尝试设置音频输入的音量和静音状态时失败了,原因是传入了无效的参数。
解决方法:重启PulseAudio服务器或者直接禁用音频输入。
2.使用 qemu-system-arm 启动一个无图形界面的 ARM 架构虚拟系统,在虚拟系统中将 ext4 格式的 SD 卡设备(/dev/mmcblk0 )挂载到 /mnt 目录时遇到如下报错:

这是因为我试图挂载的 ext4 文件系统开启了大文件(2TB 及以上 )支持特性。但当前内核没有开启 CONFIG_LBDAF 配置项,所以无法以可读写(RDWR )模式挂载该文件系统,最终导致 mount 命令失败,提示无效参数 。
解决方法:重新配置内核,进入内核配置菜单,运行make menuconfig。
在菜单中选择Enable the block layer,然后启用Support for large (2TB+) block devices and files,保存配置并重新编译内核。这是最彻底的解决方法,适用于需要支持大文件的场景。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)