手把手教你安装 Ubuntu 20.04和运行部署 QEMU:纯小白无需基础的虚拟化初体验(图文详解)
一、认识 QEMU
1.1 🔍 QEMU是什么?虚拟开发板的神奇工具
QEMU是一款完全开源免费的仿真软件,就像是程序员手中的\"魔法棒\"!它不仅能像VMware那样虚拟出一台完整的电脑,更厉害的是可以模拟各种嵌入式开发板(ARM、MIPS、RISC-V等架构通吃),而且还能模拟串口、网卡、USB等各种外设。
想象一下:不用花一分钱买开发板,就能在自己的电脑上搭建一个完整的嵌入式开发环境,运行U-boot、Linux内核和文件系统,这不是很酷吗?
1.2 💡 为什么新手要用QEMU学嵌入式?
1. 🤯 真实开发板的各种\"坑\"
- 买开发板要花钱(好板子不便宜!)
- 硬件连接各种问题(线接错了?电源不稳?)
- 环境配置复杂(驱动装不上?工具链不对?)
- 容易损坏硬件(手抖烧了芯片?)
2. ✨ QEMU带来的学习革命
- 零成本:完全免费,省下买开发板的钱
- 零风险:随便折腾,不会烧坏任何硬件
- 快速上手:几分钟就能搭建好开发环境
- 功能完整:支持从bootloader到应用层的全流程开发
- 跨平台:Windows/Mac/Linux都能用
1.3 🎯 QEMU能帮你学习哪些嵌入式技能?
💡 专家说:嵌入式开发80%的知识都可以在QEMU上学习!
1.4 🛠️ 本教程带你玩转什么?
我们将以ARM官方的vexpress开发板为例子,在Ubuntu系统上:
(1)安装配置QEMU
(2)移植U-boot引导程序
(3) 编译运行Linux内核
(4)搭建NFS根文件系统
(5)完成一个完整的嵌入式Linux开发环境
1.5 🌟 QEMU学习嵌入式5大优势
你是否想学嵌入式开发,但苦于没有开发板?
你是否想调试Linux内核,却怕搞坏真机?
QEMU 来拯救你啦!🎉
(1) 🛠️ 硬件仿真,0成本学习
- 支持 树莓派、i.MX、Vexpress、RISC-V 等上百种开发板!
- 不用买硬件,直接在电脑上仿真 ARM、MIPS、RISC-V 等 CPU!
(2)⚡ 极速开发,NFS 文件共享
- 挂载主机的
/home/nfs
作为根文件系统,直接修改代码,实时生效! - 编译内核、调试驱动,再也不用反复烧录 SD 卡!💾
(3) 🌐 网络配置超简单
- 支持 TFTP 加载 U-Boot 和内核,NFS 挂载根文件系统,一键启动!
- 不用折腾复杂的网络配置,开箱即用!🔌
(4)📚 适合各种学习场景
- 嵌入式入门:仿真开发板,跑通第一个 LED 驱动!💡
- Linux 内核调试:单步跟踪系统启动,再也不怕 panic!🐞
- RTOS 开发:跑 FreeRTOS、Zephyr,轻松验证代码!
(5)💻 跨平台支持,Mac/Win/Linux 都能玩
- 在 Ubuntu 上搭好环境,Windows 和 macOS 也能通过 WSL/Docker 使用!
1.6 📚 适合谁学习?
✔ 嵌入式开发入门新手
✔ 想学习Linux内核的小伙伴
✔ 预算有限的学生党
✔ 想验证想法的工程师
✔ 任何对嵌入式感兴趣的人!
二、Ubuntu虚拟机安装
2.1 安装 VMware Player
VMware Player 是一款虚拟机软件,通过它,用户可以在 Windows 操作系统上运行 Linux 系统。您可以访问 VMware Player 的官方网站进行下载:VMware Player 官方主页。
建议您安装版本 15 Pro 或者更新的版本。以下是 VMware Player 16 的下载链接:
VMware Player 16 下载链接。
2.2 下载 Ubuntu 20.04 镜像
为了在 VMware 虚拟机中运行 Linux 系统,我们选择使用 Ubuntu 20.04 版本。您可以通过以下链接下载 Ubuntu 20.04 镜像文件:
Ubuntu 20.04 镜像下载地址。
请确保选择 Desktop 版本的 64 位镜像进行下载。直接下载地址:Ubuntu 20.04 64-bit Desktop 镜像
2.3 配置Ubuntu20.04在VMware-Player上的虚拟机环境
1. 点击创建新虚拟机
2. 自定义虚拟机
3. 选择稍后安装操作系统
客户机操作系统选择Linux,版本选择Ubuntu 64位
4. 虚拟机向导配置
虚拟机名称自己设一个名称
设置安装位置,推荐尽量安装在硬盘剩余空间较多的分区里
虚拟磁盘推荐设为存储为单个文件,磁盘大小根据自己需要设置
后续有俩个都按推荐即可
5. 自定义硬件配置
这里需要把2.2下载的镜像文件使用ISO镜像导入
6. 开启虚拟机
7.给root用户设置密码:
sudo passwd root
8. 手动安装 VMware Tool
apt-get install open-vm-tools -y
apt-get install open-vm-tools-desktop -y
9. 添加网卡
这里我们需要两个网卡,一个用于上网,一个用于虚拟机和开发板桥接,添加两个网卡,都改为NAT连接
设置完以后,输入Ctrl+Alt+T,输入ping www.baidu.com,如果有反应,证明已经可以连上网络了
ping www.baidu.com
10. 设置软件源
软件源是存储软件包的集中式仓库,尤其在Linux发行版(如Ubuntu)中,提供方便的方式来管理和安装软件。Ubuntu采用集中式的软件仓库机制,将软件包分类存放在软件仓库中,便于用户管理和访问。这些仓库托管在各种镜像服务器上,当用户使用apt-get install
命令安装软件包时,系统会从这些服务器下载所需的软件包。
工作原理如下:首先,用户执行apt update
命令,程序分析/etc/apt/sources.list
文件,查看列出的软件源地址。然后,系统联网查找这些地址对应的Packages、Sources和Release列表文件。
/etc/apt/sources.list
文件下的软件源地址
在这里可以添加国内软件源地址
如果有更新,系统会下载并存储在/var/lib/apt/lists/
目录下。更改之后首先进行apt update
接着,当用户使用apt install
命令安装软件包时,系统从已更新的列表中查找并下载所需的软件包,进行安装。
以下是经常涉及的几个目录的整理:
/var/lib/dpkg/available:存储软件包的描述信息,包括软件源中所有软件包的信息,涵盖已安装和未安装的软件包。
/var/cache/apt/archives:APT安装软件包时的临时存放路径,下载的软件包会保存在此处。
/etc/apt/sources.list:此文件包含软件源站点的信息。在执行apt install
时,Ubuntu会从这些站点下载软件包并进行安装。
/var/lib/apt/lists:执行apt update
命令时,从/etc/apt/sources.list
中下载的软件包列表索引会保存到该目录中。
这些目录在软件包管理和更新过程中起着重要作用。
为了提高下载速率,这里我们把软件源设置为国内的软件源
点击左下角的九点状按钮(显示所有程序),点击全部,点击软件和更新,点击下载自,选择最佳服务器
这种机制使软件管理高效、便捷,确保软件的更新和安全性。通过软件源,用户可以轻松获取、安装和更新各种软件,简化软件管理的复杂性。
更换完毕以后,先更新软件源
#更新软件列表sudo apt update #只针对于vmware-tools安装没有成功的情况,安装成功了就不必执行了sudo apt install open-vm-tools sudo apt install build-essential openssh-server vim net-tools gcc-arm-linux-gnueabi git tree
安装内容:gcc、make、ssh、vim编辑器、网络基本工具(可以查网卡ip地址等)、arm编译器arm-linux-gnueabi-gcc、git、tree
推荐安装一个MobaXterm之类的终端管理器,方便通过ssh访问ubuntu
11. 重启系统
重启一下
reboot
三、在Ubuntu上安装QEMU
3.1 安装qemu
现在我们将正式启动 QEMU 的配置工作。本部分的所有设置都将基于 VExpress 开发板进行,因为 U-Boot 和内核对该开发板提供了全面的板级支持包(BSP),使得使用过程非常简单快捷。
Ubuntu 20.04 对 QEMU 的支持也很出色,我们可以直接通过 APT 包管理器安装。安装命令为:
sudo apt install qemu-system
通过这个命令,我们可以轻松地在 Ubuntu 20.04 系统中获取到 QEMU 的相关组件。
3.2 验证安装是否成功
(1)检查 QEMU 版本:
打开终端,输入以下命令查看 QEMU 的版本信息。如果 QEMU 安装成功,您将看到其版本号。
qemu-system-x86_64 --version
请注意,如果您安装的是其他架构的 QEMU(如 ARM),请将 x86_64
替换为相应的架构。
(2) 查看已安装的包:
使用以下命令查看系统中已安装的 QEMU 相关包:
dpkg -l | grep qemu
如果您看到与 QEMU 相关的包列表,说明安装成功。
(3) 运行 QEMU:
可以尝试运行一个简单的 QEMU 命令,例如启动虚拟机。可以使用以下命令创建一个虚拟机:
qemu-system-x86_64 -m 512 -nographic
这将启动一个虚拟机,分配 512MB 的内存,并且不使用图形界面。如果您能够看到 QEMU 的启动界面或命令行,说明安装成功。
(4)检查帮助文档:
输入以下命令查看 QEMU 的帮助文档。如果能够正常显示,说明程序运行正常:
qemu-system-x86_64 --help
查看支持的开发板
qemu-system-arm -M help
通过上述方法,可以确认 QEMU 是否已成功安装并能够正常运行。
四、编译Linux内核镜像
4.1 下载Linux内核源码
Linux 内核源码可以通过以下两种方式下载:
1. 官方网站下载:
- 您可以访问 Linux 内核的官方网站 www.kernel.org 下载最新的内核源码。
2. 国内镜像服务器下载:
- 为了获得更快的下载速度,您可以使用国内的镜像服务器。推荐的下载地址是: 清华大学镜像站
通过以上链接,您可以方便地获取到所需的 Linux 内核源码。
4.2 Linux 内核的编译和安装
1. 构建内核与配置编译选项
执行以下命令可以完成 Linux 内核的编译和安装工作:
# 进入主目录mkdir -p /home/wangbeiy/Desktop/linux-cache##(这里建议在非/home下重新建个文件夹)cd /home/wangbeiy/Desktop/linux-cache# 创建 tftpboot 目录sudo mkdir tftpboot# 修改权限sudo chmod 777 tftpboot# 进入 tftpboot 目录cd tftpboot# 创建 kernel 目录mkdir kernel# 进入 kernel 目录cd kernel# 下载指定版本的 Linux 内核源码wget https://mirrors.tuna.tsinghua.edu.cn/kernel/v5.x/linux-5.10.99.tar.xz# 解压下载的内核源码包tar -xvf linux-5.10.99.tar.xz# 进入解压后的内核源码目录cd linux-5.10.99# 使用文本编辑器打开 Makefile 文件,定位到第 371 行gedit Makefile
在 Makefile
的第 371 行,您需要进行以下修改。
这是arm架构:
ARCH ?= armCROSS_COMPILE = arm-linux-gnueabi-
这是arm64架构:
ARCH?= arm64CROSS_COMPILE = aarch64-linux-gnu-
完成以上修改后,可以继续执行内核编译的后续步骤。这通常包括:
(1)构建 Linux 内核:
make vexpress_defconfig
是用于构建 Linux 内核的一条命令,通常用于设置特定硬件平台(如 ARM 的 Versatile Express 平台)的默认内核配置。
vexpress_defconfig
是一个预定义的配置文件,包含了为 Versatile Express 开发板(VExpress)优化的内核选项。运行这条命令会将这些默认配置选项加载到 .config
文件中。
使用 defconfig
可以大大简化内核配置的过程,因为它为特定硬件提供了一个基本的、经过验证的配置,用户可以在这个基础上进行进一步的修改。
使用方法
进入内核源代码目录: 在终端中,导航到 Linux 内核的源代码目录。
运行命令:
make vexpress_defconfig
配置结果:
该命令会生成一个 .config
文件,里面包含了为 VExpress 平台优化的内核配置。可以使用 make menuconfig
或 make xconfig
进一步修改这些选项。
可能出现的问题:
这两个错误是由于没有安装 flex
和 bison
工具引起的。安装这两个工具后,问题就会得到解决。
(2)配置编译选项:
make menuconfig
是 Linux 内核和其他软件项目中用于配置编译选项的命令。它提供一个基于文本的用户界面,使用户能够方便地选择和修改编译选项,从而生成自定义的配置文件。
图形化界面: make menuconfig
提供了一个菜单驱动的界面,用户可以通过键盘导航,选择和修改各种配置选项,而不需要手动编辑 .config
文件。
模块选择: 用户可以选择要编译到内核中的模块,启用或禁用特定功能,对应的选项会自动更新到 .config
文件中。
依赖处理: 在选择某个选项时,make menuconfig
会自动处理依赖关系,确保所需的其他选项也被正确配置。
保存配置: 配置完成后,你可以保存所做的更改,生成或更新 .config
文件,以用于后续的编译过程。
使用方法
进入内核源代码目录: 在终端中,导航到 Linux 内核的源代码目录。
运行命令:
make menuconfig
进行配置: 使用方向键导航菜单,按 Enter
进入子菜单,使用空格键选择或取消选择选项。
保存并退出: 配置完成后,选择“Save”选项以保存配置,然后退出界面。
出现这个界面表明内核配置已经完成,可以放心进行编译了。
安装依赖
在某些系统中,使用 make menuconfig
前需要确保安装了必要的依赖项,如 libncurses-dev
。你可以通过以下命令安装这个包(以 Ubuntu/Debian 为例):
sudo apt-get install libncurses5-dev libncursesw5-dev
不安装依赖出现的报错:
2. 生成不同的内核映像和模块
在 Linux 内核的编译过程中,使用以下命令来生成不同的内核映像和模块。
(1)make zImage -j 6
-
功能:这条命令用于编译内核的
zImage
,它是一个压缩的内核映像,适用于嵌入式系统和一些特定的硬件平台。 -
参数解释:
-j 6
:表示使用六个并行工作线程进行编译,可以加速编译过程。如果系统有更多的 CPU 核心,可以将数字增加到更高的值,例如-j 4
或-j 8
。
(2)make modules -j 6
-
功能:这条命令用于编译内核模块。这些模块是可加载的组件,提供额外的功能,可以在运行时动态加载,而不是将它们直接编译到内核中。
-
参数解释:
-j 6
:同样表示使用六个并行线程进行编译。
(3)make dtbs -j 6
-
功能:这条命令用于生成设备树二进制文件(Device Tree Blobs,简称 DTB)。设备树是一种数据结构,用于描述硬件设备的配置信息,以便内核在启动时能够正确识别和配置硬件。
-
参数解释:
-j 6
:同样表示使用六个并行线程。
(4)make LOADADDR=0x60003000 uImage -j 6
-
功能:这条命令用于生成
uImage
,这是一个用于启动的内核映像,通常用于 U-Boot 引导加载程序。uImage
包含了额外的信息,例如加载地址和校验和,以便引导程序能够正确加载和验证内核。 -
参数解释:
LOADADDR=0x60003000
:指定内核将被加载到内存的地址。这个地址通常需要根据目标硬件的要求进行设置。-j 2
:同样表示使用两个并行线程进行编译。
zImage
是通用的内核映像文件,modules
是未直接集成到内核中的模块(通常是驱动程序,设置为 (M)
的内容),dtbs
是编译生成的设备树文件,而 uImage
是专门为 U-Boot 引导程序准备的内核映像。在当前情况下我们可能不需要 uImage
,但我们还是先进行编译,以便将来可能用到。在编译过程中可能会遇到一些错误。
装一个u-boot-tools即可解决
这些命令是编译 Linux 内核和相关组件的重要步骤。通过使用并行编译选项(-j
),可以显著缩短编译所需的时间。这些命令的成功执行将生成可用于启动和运行的内核映像、模块和设备树文件。
sudo apt install u-boot-tools
3. Linux内核源码文件介绍
在 Linux 内核源代码中,arch
目录包含与特定体系结构(architecture)相关的代码和文件。这是一个非常重要的目录,因为它实现了不同硬件平台的核心支持。
(1) arch
目录的主要内容
子目录:
arch
目录下通常会有多个子目录,每个子目录对应一个特定的硬件架构。这些架构可能包括:arm
:包含 ARM 架构的支持代码。x86
:包含 x86 和 x86-64 架构的支持代码。mips
:包含 MIPS 架构的支持代码。powerpc
(或ppc
):包含 PowerPC 架构的支持代码。riscv
:包含 RISC-V 架构的支持代码。s390
:包含 IBM Z 系列(S/390)架构的支持代码。sh
、ia64
等:可能还有其他架构的支持代码。
主要功能:
- 体系结构特定的代码:每个架构的实现会包含与该架构特有的硬件特性相关的代码,例如上下文切换、处理器启动、系统调用、异常处理等。
- 内存管理单元(MMU)支持:实现该架构的内存管理机制。
- 中断和异常处理:处理与硬件中断和异常相关的代码。
- 汇编代码:一些低级别的汇编语言代码,直接与硬件交互。
- 架构特定的驱动程序:与特定硬件组件(如CPU、总线等)相关的驱动程序。
(2) 关键文件和目录
-
CREDITS:包含了对 Linux 内核开发者的致谢和贡献者名单。
-
LICENSES:包含了与 Linux 内核相关的许可证文件,说明了代码的使用和分发条款。
-
Makefile:构建系统的核心文件,定义了如何编译内核以及如何组织源代码。
-
README:提供了关于内核源码树的基本信息和如何编译及使用内核的指导。
-
COPYING:包含了 GNU 通用公共许可证(GPL),这通常是 Linux 内核的许可证。
-
System.map:内核符号的映射文件,包含了内核中的所有符号及其地址,常用于调试。
-
vmlinux:最终生成的可执行内核映像文件,包含了所有内核代码。
-
vmlinux.o:通常是编译过程中生成的中间目标文件,包含内核代码。
(3) 目录
-
block:与块设备的驱动程序和相关代码有关,处理硬盘等设备的输入输出。
-
crypto:包含加密算法和加密相关的功能代码。
-
certs:与内核证书相关的目录,通常用于安全功能。
-
Documentation:包含内核的文档,提供了对于内核各部分的详细说明和使用指南。
-
drivers:包含各种硬件设备驱动程序的代码。
-
fs:文件系统相关的代码,包括不同类型的文件系统实现。
-
include:包含内核头文件,这些头文件定义了数据结构和函数原型,供其他内核代码使用。
-
init:内核初始化代码,负责在引导时设置系统。
-
kernel:核心内核代码,负责调度、进程管理等基本功能。
-
lib:内核的公用库代码,包含一些常用函数和工具。
-
mm:内存管理相关的代码,包括内存分配和管理的实现。
-
net:网络协议栈和网络设备处理代码。
-
security:安全模块和权限管理的相关代码。
-
sound:音频驱动和音频子系统的实现。
-
samples:示例代码,展示如何使用内核提供的功能。
-
scripts:包含用于构建和管理内核的各种脚本。
-
tools:与内核相关的用户空间工具,通常是用于调试或管理内核的工具。
-
virt:虚拟化相关的代码,处理虚拟机和虚拟设备。
(4)模块相关文件
-
modules.builtin:一个列表,展示了内核构建中包含的内置模块。
-
modules.builtin.modinfo:内置模块的元信息。
-
modules.order:记录模块的加载顺序。
-
Module.symvers:包含模块符号的版本信息,用于模块之间的依赖解析。
-
modules-only.symvers:仅包含模块符号的版本信息,通常在模块化内核构建中使用。
(5) Kbuild和Kconfig文件
-
Kbuild:定义了如何构建特定目录下的文件的规则。
-
Kconfig:用于配置内核的选项和模块,提供用户可选的内核配置。
(6)虚拟内存和地址映射
- vmlinux.symvers:提供了内核符号的版本信息,帮助模块开发者确保符号的一致性。
这些文件和目录共同组成了 Linux 内核的源代码树,提供了开发、编译、调试和使用 Linux 内核所需的所有资源。它们涵盖了内核的各个方面,从驱动程序到文件系统,从文档到构建系统,使得开发者能够高效地进行内核开发和维护。
4.3 复杂相关文件
编译好以后,再把镜像文件和设备树文件复制到工程目录里
在编译完成 Linux 内核及其相关组件后,可以通过以下命令将镜像文件和设备树文件复制到指定的工程目录中(在此示例中为 ~/Desktop/linux-cache/tftpboot
)。
1. 复制 zImage 文件
cp arch/arm/boot/zImage ~/Desktop/linux-cache/tftpboot/
- 功能:将编译好的
zImage
文件复制到~/Desktop/linux-cache/tftpboot/
目录下。 - zImage:这是一个压缩的内核映像,适用于嵌入式系统。
- 目标目录:
~/Desktop/linux-cache/tftpboot
是通常用于 TFTP(Trivial File Transfer Protocol)服务器的目录,方便通过网络启动设备。
2. 复制 uImage 文件
cp arch/arm/boot/uImage ~/Desktop/linux-cache/tftpboot/
- 功能:将编译好的
uImage
文件复制到~/Desktop/linux-cache/tftpboot
目录下。 - uImage:这是一个为 U-Boot 引导程序准备的内核映像,包含额外的元数据(如加载地址和校验和),用于启动过程。
3. 复制设备树文件
cp arch/arm/boot/dts/vexpress-v2p-ca9.dtb ~/Desktop/linux-cache/tftpboot/
- 功能:将编译好的设备树二进制文件(DTB)复制到
~/Desktop/linux-cache/tftpboot/
目录下。 - 设备树文件:
vexpress-v2p-ca9.dtb
描述了硬件的布局,确保 Linux 内核在启动时能够正确识别和配置所有硬件组件。
执行这些命令后,您将会将编译生成的内核映像和设备树文件复制到指定的工程目录中,方便后续通过网络引导或其他方式加载。
4.4 确认生成的内核镜像和设备树文件,并创建启动脚本
1. 确认生成的文件
检查编译后的镜像文件 zImage
和设备树文件 vexpress-v2p-ca9.dtb
是否存在,并记录下它们的路径。
# 确认文件存在ls ~/Desktop/linux-cache/tftpboot/zImagels ~/Desktop/linux-cache/tftpboot/vexpress-v2p-ca9.dtb
(请替换 ~/Desktop/linux-cache/tftpboot
为实际的内核源代码路径)
2. 创建启动脚本
在 ~/Desktop/linux-cache/tftpboot
目录下创建一个脚本 start.sh
:
# 进入目标目录cd ~/Desktop/linux-cache/tftpboot# 创建脚本文件touch start.sh# 修改文件权限chmod 777 start.sh# 使用文本编辑器编辑脚本内容gedit start.sh
3. 编辑 start.sh
文件
在 start.sh
文件中输入以下内容:
qemu-system-arm \\ -M vexpress-a9 \\ -m 512M \\ -kernel zImage \\ -dtb vexpress-v2p-ca9.dtb \\ -nographic \\ -append \"console=ttyAMA0\"
4. 以 root 用户登录并启动脚本
使用 su
命令切换到 root 用户,然后执行脚本:
# 切换到 root 用户su root# 启动脚本./start.sh
5. 启动结果
如果启动显示正常,说明内核已经成功挂载。但如果出现 “end Kernel panic” 的提示,通常是因为系统没有根文件系统。这意味着内核无法找到可以用作根文件系统的设备。
6. 解决 Kernel Panic 问题
有以上显示,证明内核挂载成功。最后提示end Kernel panic是因为没有根文件系统,完成下面的部分就可以解决这个问题。
五、制作根文件系统
5.1 什么是BusyBox 根文件系统?
1. 文件系统概述
文件系统是对存储设备上数据进行组织的机制,使数据能够高效地存储、检索和管理。它提供了一种结构化的方法来处理文件和目录,使用户和应用程序能够轻松访问和管理数据。
2. 为什么要使用文件系统
使用文件系统的原因包括:
- 组织数据:文件系统帮助用户将数据分类存储,使其易于查找和管理。
- 数据保护:通过权限和访问控制,文件系统可以保护数据不被未授权访问。
- 持久性:文件系统确保数据在设备重启或断电后仍然可用。
- 高效存储:文件系统优化了存储空间的使用,提高了读取和写入速度。
3. Linux的哲学:一切皆文件
在Linux操作系统中,\"一切皆文件\"的哲学意味着几乎所有的系统资源(例如设备、目录和进程)都可以通过文件系统进行访问。这种统一的视图简化了用户和程序与操作系统之间的交互。
4. 用户与操作系统的交互
用户与操作系统之间的主要交互工具是文件系统调用。这些调用允许程序访问和操作文件、目录以及其他存储资源,提供了高层次的接口来与底层存储设备进行沟通。
5. 根文件系统
根文件系统是Linux内核启动后第一个挂载的文件系统。它是整个文件系统结构的基础,包含了系统启动和运行所需的基本文件和目录。
- 组成:根文件系统主要由基本的shell命令、各种库、字符设备、配置脚本等组成,提供了根目录(/),并在此基础上可以挂载其他文件系统。
6. 存储介质
根文件系统(RFS)可以放置在多种存储介质上,包括:
- NOR/NAND Flash
- SD卡
- 硬盘
- 网络存储空间
这种灵活性使得Linux能够在各种设备上运行,从嵌入式系统到服务器和个人计算机。
文件系统在Linux中扮演着核心角色,它不仅组织和管理数据,还体现了Linux系统的基本哲学。通过提供一致的接口和可扩展性,文件系统使用户和开发者能够高效地与系统进行交互。
7. BusyBox 概述
BusyBox是一个集成了100多个Linux常用命令和工具的软件,旨在为嵌入式系统提供一个小型而功能强大的环境。它被称为“Linux的瑞士军刀”,因为它将多个命令和实用程序整合到一个单一的可执行文件中,极大地节省了存储空间和资源。
(1) 集成命令和工具:
- BusyBox包含了许多常用的Linux命令,如
ls
、cp
、mv
、rm
、cat
、echo
等,同时也包括一些网络工具和系统管理工具。这些命令都共享一个基本的框架,使得BusyBox的体积非常小。
(2)适用于嵌入式系统:
- BusyBox特别适合嵌入式文件系统的构建。由于许多嵌入式设备的存储和资源有限,BusyBox提供的精简和高效的命令集使得它成为嵌入式Linux系统的理想选择。
(3)可配置性:
- 在编译时,BusyBox允许用户根据需要选择启用或禁用某些功能,从而进一步减小最终生成的可执行文件的大小。这种灵活性使其能够满足不同嵌入式项目的需求。
(5)轻量级:
- BusyBox的设计目标是将多种工具合并到一个小型的二进制文件中,通常只有几百KB,因此非常适合存储受限的环境。
应用场景
- 嵌入式设备:如路由器、智能家居设备、工业控制系统等,BusyBox为这些设备提供了一个基本的命令行接口和实用工具。
- 恢复环境:在系统故障或恢复时,BusyBox可以提供一个轻量级的操作环境,便于故障排除和系统恢复。
- Linux发行版:一些轻量级的Linux发行版(例如Alpine Linux)也利用BusyBox来减少系统的总体大小。
BusyBox通过将多个命令和工具整合到一个可执行文件中,提供了一个高效、便捷的解决方案,适合在资源有限的嵌入式环境中使用。它的灵活性和轻量级特性使其成为众多嵌入式Linux应用的理想选择。
5.2 编译和安装 BusyBox 根文件系统
1. 进入工作目录:
cd ~/Desktop/linux-cache/tftpboot
2. 创建文件系统目录:
mkdir filesyscd filesys
3. 下载 BusyBox 源代码:
wget https://busybox.net/downloads/busybox-1.35.0.tar.bz2
4. 解压下载的文件:
tar -xvf busybox-1.35.0.tar.bz2cd busybox-1.35.0
5. 创建目标根文件系统目录:
sudo mkdir -p /home/wangbeiy/Desktop/linux-cache/nfs
6. 更改目录权限:
sudo chmod 777 /home/wangbeiy/Desktop/linux-cache/nfs
5.3 编译 BusyBox
1. 配置 BusyBox的Makefile:
在编译 BusyBox 或其他类似项目时,配置 Makefile
是确保编译正确的关键步骤。下面是如何配置 Makefile
以支持 ARM 架构的详细说明,包括 ARCH
和 CROSS_COMPILE
变量。
(1) 打开 Makefile:
使用文本编辑器(如 gedit
或任何您喜欢的编辑器)打开 Makefile
。这里我们使用 gedit
并定位到文件的第191行:
gedit Makefile +191
(2)设置 ARCH 和 CROSS_COMPILE:
在 Makefile
中,找到以下行并进行修改(如果这些行不存在,可以添加):
ARCH ?= armCROSS_COMPILE ?= arm-linux-gnueabi-
解释:
ARCH
:指定目标架构为 ARM。CROSS_COMPILE
:指定用于交叉编译的工具链前缀。在此示例中,arm-linux-gnueabi-
是一个常见的 ARM 交叉编译器前缀。
(3) 保存并退出:
确保保存对 Makefile
的修改,然后关闭编辑器。
2. 配置 BusyBox
(1)进入配置菜单:
在 BusyBox 源代码目录中,使用以下命令进入配置菜单:
make menuconfig
(2)设置编辑器环境:
-
在配置菜单中,选择
Settings
选项。
-
找到
[ ] vi-style line editing commands
选项并勾选(标记为[*]
)。这将使您在编辑配置时能够使用vi
风格的命令,更方便地操作。
(3)设置安装路径:
-
在
Settings
下,找到Destination path for ‘make install’
选项。
-
输入您的根文件系统路径,例如
./_install/home/wangbeiy/Desktop/linux-cache/nfs
。确保该路径是您之前创建的用于存放 BusyBox 根文件系统的路径。
3. 编译与安装
编译和安装:
使用以下命令进行安装:
make install -j 6
-j 6
表示使用六个并行作业,这样可以加快编译速度。BusyBox 会在安装之前自动进行编译,无需单独运行make
命令。
此时安装成功!
生成各种命令和软连接,shell命令已经全部生成。
4. 安装 BusyBox 说明
(1)编译与安装:
- 在执行
make install
时,如果未手动编译,系统会自动先编译再进行安装。因此,无需单独输入编译命令。这简化了安装过程,用户只需关注安装目录的设置。
(2) 用户权限注意事项:
-
不要以 root 用户或使用 sudo 进行编译和安装:
-
强烈建议在普通用户下执行
make install
,以避免将 BusyBox 安装到系统根目录(/
),从而保护您的 Ubuntu 系统不受破坏。 -
如果以 root 用户身份执行,可能会错误地将文件安装到系统根目录,导致系统功能异常或崩溃。
-
(3)设置正确的安装路径:
- 确保将安装路径设置为用户可写的目录:
- 通常,可以将安装路径设置为
/home/wangbeiy/Desktop/linux-cache
,因为这个目录通常已经设置了最高权限,确保可以顺利安装。 - 如果安装路径设置错误(例如设置为
/
),可能会因为权限不足而导致安装失败,从而避免潜在的系统崩溃。
- 通常,可以将安装路径设置为
(4)安装过程中的权限问题:
- 如果您误将安装目录设置为根目录,系统会因为权限不足而无法完成安装,错误信息将指明安装未成功。这样,您能及时发现错误,从而避免系统受到影响。
5.4 完成完整的根文件系统的构建
在成功建立基本系统功能后,接下来需要进行以下几个步骤,以确保系统能够正常运行并具备所需的功能
1. 安装动态链接库:
确保系统中包含必要的动态链接库,以支持应用程序和工具的正常运行。这些库为系统提供了基本的运行时支持。
在使用 APT 安装 ARM 编译器的过程中,动态链接库通常位于 /usr/arm-linux-gnueabi/lib
目录下。为了确保这些库能够在根文件系统中正常使用,您需要将它们复制到相应的目录。
(1)复制动态链接库
创建目标目录:
首先,进入您设置的 NFS 文件系统目录并创建 lib
目录:
cd /home/wangbeiy/Desktop/linux-cache/nfsmkdir lib
复制动态链接库:
然后,进入 ARM 编译器的库目录并复制所有共享库文件到 NFS 文件系统的 lib
目录下:
cp bash~/Desktop/linux-cache/filesys/busybox-1.35.0/_install ./cd /usr/arm-linux-gnueabi/libcp *.so* /home/wangbeiy/Desktop/linux-cache/nfs/lib -d
(2)解决运行时问题
如果在运行 linuxrc
时遇到失败,可能是由于动态库版本不兼容导致的。为了解决这个问题,可以选择在编译 BusyBox 时配置为静态编译:
配置静态编译:
在 BusyBox 的配置菜单中,选择以下选项:
[*] Build static binary (no shared libs)
这样配置后,BusyBox 将生成一个静态二进制文件,运行时不再依赖于动态库。
通过以上步骤,可以将动态链接库复制到根文件系统中,并通过静态编译 BusyBox 来避免因动态库不兼容而导致的运行问题。这将确保 linuxrc
在启动时能够顺利运行,增强系统的稳定性和兼容性。
2. 创建设备节点:
设置必要的设备节点,如 /dev/console
和 /dev/null
等,以便系统能够正确识别和使用硬件设备。
(1)进入目标目录:
cd /home/wangbeiy/Desktop/linux-cache/nfsmkdir devcd dev
(2) 创建设备节点:
使用 mknod
命令创建所需的设备节点,具体步骤如下:
# 创建虚拟终端设备节点sudo mknod -m 666 tty1 c 4 1sudo mknod -m 666 tty2 c 4 2sudo mknod -m 666 tty3 c 4 3sudo mknod -m 666 tty4 c 4 4# 创建控制台设备节点sudo mknod -m 666 console c 5 1# 创建空设备节点sudo mknod -m 666 null c 1 3
(1)设备节点说明
tty1
,tty2
,tty3
,tty4
:这些是虚拟终端设备节点,分别对应于系统的四个终端。console
:控制台设备节点,通常用于系统的输出和输入。null
:一个特殊的设备节点,所有写入该节点的数据都会被丢弃,读取时返回 EOF。
(2)权限设置
- 使用
-m 666
选项设置设备节点的权限,允许所有用户进行读写操作。这在某些情况下可以方便测试和调试,但在生产环境中应根据需要设置更严格的权限。
3. 配置初始化进程:
配置系统的初始化进程,确保在系统启动时能够正确加载和执行 BusyBox,从而管理系统的启动过程。
设置初始化进程 /etc/rcS
进入目标目录:
首先,导航到 NFS 根文件系统的路径:
cd ~/Desktop/linux-cache/nfs
创建必要的目录:
创建 etc/init.d
目录,如果该目录已经存在,可以跳过这一步:
mkdir -p etc/init.d
创建 rcS
脚本文件:
进入 etc/init.d
目录并创建 rcS
文件:
cd etc/init.dtouch rcS
设置文件权限:
设置 rcS
文件的权限,以便可以读写和执行:
chmod 777 rcS
编辑 rcS
文件:
使用文本编辑器(如 gedit
)打开 rcS
文件并添加初始化命令。示例内容如下:
gedit rcS
在编辑器中,可以添加以下内容:
#!/bin/shPATH=/bin:/sbin:/usr/bin:/usr/sbin export LD_LIBRARY_PATH=/lib:/usr/lib/bin/mount -n -t ramfs ramfs /var/bin/mount -n -t ramfs ramfs /tmp/bin/mount -n -t sysfs none /sys/bin/mount -n -t ramfs none /dev/bin/mkdir /var/tmp/bin/mkdir /var/modules/bin/mkdir /var/run/bin/mkdir /var/log/bin/mkdir -p /dev/pts/bin/mkdir -p /dev/shm/sbin/mdev -s/bin/mount -aecho \"-----------------------------------\"echo \"*****welcome to vexpress board*****\"echo \"-----------------------------------\"
以下是对这段Shell脚本的逐行详细注释:
#!/bin/sh
#!/bin/sh
:这是一个shebang行,指示系统使用/bin/sh
作为脚本的解释器。这是一个标准的Unix shell,通常用于执行脚本。
PATH=/bin:/sbin:/usr/bin:/usr/sbin
PATH=/bin:/sbin:/usr/bin:/usr/sbin
:设置环境变量PATH
,指定可执行文件的搜索路径。它包含了常见命令的目录,确保在执行命令时能够找到它们。
export LD_LIBRARY_PATH=/lib:/usr/lib
export LD_LIBRARY_PATH=/lib:/usr/lib
:设置并导出环境变量LD_LIBRARY_PATH
,指示动态链接器在/lib
和/usr/lib
目录中查找共享库。这对于程序运行时动态链接到正确的库是必要的。
/bin/mount -n -t ramfs ramfs /var
/bin/mount -n -t ramfs ramfs /var
:将ramfs
类型的内存文件系统挂载到/var
目录。-n
选项表示不在/etc/mtab
中记录此挂载点,ramfs
是一种基于内存的文件系统,通常用于临时数据存储。
/bin/mount -n -t ramfs ramfs /tmp
/bin/mount -n -t ramfs ramfs /tmp
:将另一个ramfs
类型的内存文件系统挂载到/tmp
目录。/tmp
目录通常用于存储临时文件。
/bin/mount -n -t sysfs none /sys
/bin/mount -n -t sysfs none /sys
:将sysfs
文件系统挂载到/sys
目录。sysfs
提供了对内核对象和设备的访问,通常用于设备管理。
/bin/mount -n -t ramfs none /dev
/bin/mount -n -t ramfs none /dev
:将ramfs
类型的内存文件系统挂载到/dev
目录,创建一个临时的设备文件系统,通常用于存放设备节点。
/bin/mkdir /var/tmp
/bin/mkdir /var/tmp
:在/var
目录下创建一个tmp
子目录,通常用于存放临时文件。
/bin/mkdir /var/modules
/bin/mkdir /var/modules
:在/var
目录下创建一个modules
子目录,用于存放内核模块。
/bin/mkdir /var/run
/bin/mkdir /var/run
:在/var
目录下创建一个run
子目录,通常用于存放运行时文件和进程信息。
/bin/mkdir /var/log
/bin/mkdir /var/log
:在/var
目录下创建一个log
子目录,用于存放日志文件。
/bin/mkdir -p /dev/pts
/bin/mkdir -p /dev/pts
:在/dev
目录下创建pts
子目录,-p
选项表示如果上级目录不存在则同时创建。/dev/pts
用于支持伪终端(PTY),这是多用户环境下的终端支持。
/bin/mkdir -p /dev/shm
/bin/mkdir -p /dev/shm
:在/dev
目录下创建shm
子目录,用于共享内存(shared memory)区域,允许不同进程之间进行高效的内存共享。
/sbin/mdev -s
/sbin/mdev -s
:运行mdev
命令以初始化设备节点。mdev
是一个轻量级的设备管理器,用于创建和删除设备文件。
/bin/mount -a
/bin/mount -a
:挂载在/etc/fstab
文件中列出的所有文件系统。这一步骤确保系统启动时所需的所有文件系统都被正确挂载。
echo \"-----------------------------------\"
echo \"-----------------------------------\"
:打印一行分隔符,用于在终端上格式化输出。
echo \"*****welcome to vexpress board*****\"
echo \"*****welcome to vexpress board*****\"
:打印欢迎信息,提示用户已经成功进入vexpress开发板环境。
echo \"-----------------------------------\"
echo \"-----------------------------------\"
:再次打印一行分隔符,结束欢迎信息的输出。
该脚本主要用于在vexpress开发板上初始化系统环境,包括挂载必要的文件系统、创建必要的目录并设置设备管理器。通过这些步骤,脚本为系统的后续操作提供了一个良好的基础环境。
保存并退出编辑器:
保存您所做的更改并退出编辑器。
4. 设置文件系统/etc/fstab:
确保文件系统的目录结构合理,以便于后续的操作和管理,确保各个组件能够正常访问。
(1) 进入目标目录:
首先,导航到 NFS 根文件系统的 etc
目录:
cd ~/Desktop/linux-cache/nfs/etc
(2) 创建 fstab
文件:
创建 fstab
文件:
touch fstab
(3) 编辑 fstab
文件:
使用文本编辑器(如 gedit
)打开 fstab
文件:
gedit fstab
(4) 输入内容:
在 fstab
文件中输入以下内容,这将定义需要挂载的文件系统:
proc /proc proc defaults 0 0none /dev/pts devpts mode=0622 0 0mdev /dev ramfs defaults 0 0sysfs /sys sysfs defaults 0 0tmpfs /dev/shm tmpfs defaults 0 0tmpfs /dev tmpfs defaults 0 0tmpfs /mnt tmpfs defaults 0 0var /dev tmpfs defaults 0 0ramfs /dev ramfs defaults 0 0
(5) 保存并退出编辑器:
保存您所做的更改并退出编辑器。
(6) 说明
-
每一行的格式如下:
-
解释各列:
- proc:挂载点为
/proc
,使用proc
文件系统。 - none /dev/pts:挂载点为
/dev/pts
,使用devpts
文件系统,设置模式为0622
。 - mdev:挂载点为
/dev
,使用ramfs
文件系统,这是用于临时设备节点的。 - sysfs:挂载点为
/sys
,使用sysfs
文件系统。 - tmpfs:挂载点为
/dev/shm
、/dev
、/mnt
,这些使用tmpfs
文件系统,以提供临时存储。 - var /dev:这里的
var
行似乎有点重复,可能需要根据您的具体需求考虑是否需要保留。
- proc:挂载点为
设置好 /etc/fstab
文件后,系统在启动时将自动挂载这些文件系统,确保系统的正常运行。
5. 设置初始化脚本/etc/inittab:
设置初始化脚本 /etc/inittab
以确保系统在启动时能够正确配置运行级别和初始化进程。
(1)设置初始化脚本 /etc/inittab
进入目标目录:
首先,导航到 NFS 根文件系统的 etc
目录:
cd ~/Desktop/linux-cache/nfs/etc
创建 inittab
文件:
创建 inittab
文件:
touch inittab
编辑 inittab
文件:
使用文本编辑器(如 gedit
)打开 inittab
文件:
gedit inittab
输入内容:
在 inittab
文件中输入以下内容:
::sysinit:/etc/init.d/rcS::askfirst:-/bin/sh::ctrlaltdel:/bin/umount -a -r
保存并退出编辑器:
保存您所做的更改并退出编辑器。
(2)说明
- 解释各行:
-
::sysinit:/etc/init.d/rcS
:- 这行表示在系统初始化时执行
/etc/init.d/rcS
脚本。这个脚本负责执行系统启动所需的操作,比如挂载文件系统、启动服务等。
- 这行表示在系统初始化时执行
-
::askfirst:-/bin/sh
:- 这行表示在启动后进入一个交互式的 shell。如果用户在启动时按下回车,系统将提供一个 shell 提示符,允许用户进行手动操作。
askfirst
选项告诉 init 进程在进入这个 shell 前先询问用户。
-
::ctrlaltdel:/bin/umount -a -r
:- 这行表示当用户按下
Ctrl+Alt+Del
组合键时,系统将执行/bin/umount -a -r
命令。这条命令会卸载所有文件系统并重新启动系统。
- 这行表示当用户按下
-
6. 设置环境变量 /etc/profile
(1)进入目标目录:
首先,导航到 NFS 根文件系统的 etc
目录:
cd ~/Desktop/linux-cache/nfs/etc
(2)创建 profile
文件:
创建 profile
文件:
touch profile
(3) 编辑 profile
文件:
使用文本编辑器(如 gedit
)打开 profile
文件:
gedit profile
(4)输入内容:
在 profile
文件中输入以下内容:
USER=\"root\"LOGNAME=$USERexport HOSTNAME=`cat /etc/sysconfig/HOSTNAME`export USER=rootexport HOME=/rootexport PS1=\"[$USER@$HOSTNAME \\W]\\# \"PATH=/bin:/sbin:/usr/bin:/usr/sbinLD_LIBRARY_PATH=/lib:/usr/lib:$LD_LIBRARY_PATHexport PATH LD_LIBRARY_PATH
(5)保存并退出编辑器:
保存您所做的更改并退出编辑器。
(6)说明
- 解释各行:
USER=\"root\"
:设置USER
环境变量为root
,表示当前用户。LOGNAME=$USER
:将LOGNAME
环境变量设置为与USER
相同的值。export HOSTNAME=\\
cat /etc/sysconfig/HOSTNAME`:从
/etc/sysconfig/HOSTNAME文件中读取主机名并将其导出为环境变量
HOSTNAME`。这要求该文件存在且包含有效的主机名。export USER=root
:明确导出USER
环境变量。export HOME=/root
:设置HOME
环境变量为/root
,表示 root 用户的主目录。export PS1=\"[$USER@$HOSTNAME \\W]\\# \"
:设置命令提示符格式,显示当前用户、主机名和当前工作目录。PATH=/bin:/sbin:/usr/bin:/usr/sbin
:设置可执行文件的搜索路径。LD_LIBRARY_PATH=/lib:/usr/lib:$LD_LIBRARY_PATH
:设置动态链接库的搜索路径,确保系统能够找到所需的库文件。export PATH LD_LIBRARY_PATH
:导出PATH
和LD_LIBRARY_PATH
变量,以便子进程也能访问这些环境变量。
以下是在 /etc/sysconfig/HOSTNAME
文件中设置主机名的步骤:
7. 设置主机名 /etc/sysconfig/HOSTNAME
(1) 进入目标目录:
首先,导航到 NFS 根文件系统的 etc
目录:
cd ~/Desktop/linux-cache/nfs/etc
(2)创建 sysconfig
目录:
创建 sysconfig
目录(如果尚未存在):
mkdir sysconfig
(3) 进入 sysconfig
目录:
进入刚创建的 sysconfig
目录:
cd sysconfig
(4)创建 HOSTNAME
文件:
创建 HOSTNAME
文件:
touch HOSTNAME
(5) 编辑 HOSTNAME
文件:
使用文本编辑器(如 gedit
)打开 HOSTNAME
文件:
gedit HOSTNAME
(6)输入主机名:
在 HOSTNAME
文件中输入以下内容:
vexpress
(7) 保存并退出编辑器:
保存您所做的更改并退出编辑器。
(8)说明
- 主机名设置:
- 文件
/etc/sysconfig/HOSTNAME
中的内容是系统主机名,主机名vexpress
将被用作识别该系统的名称。 - 该主机名在系统启动时会被读取,并设置为系统的主机名。
- 文件
以下是创建指定文件夹的步骤,以便在 NFS 根文件系统中构建必要的目录结构。
8. 创建剩下的文件夹
(1)进入目标目录:
首先,导航到 NFS 根文件系统的根目录:
cd ~/Desktop/linux-cache/nfs
(2)创建文件夹:
使用 mkdir
命令创建所需的文件夹。您可以一次性创建多个文件夹,命令如下:
mkdir mnt proc root sys tmp var
(3)说明
- 各个目录的用途:
mnt
:用于挂载文件系统的临时挂载点。proc
:用于访问内核和进程信息的虚拟文件系统,包含有关系统状态的信息。root
:用于指定 root 用户的主目录,通常包含该用户的配置文件和数据。sys
:用于访问内核和设备信息的虚拟文件系统,提供有关设备驱动程序和设备的接口。tmp
:用于存放临时文件的目录,一般会定期清理。var
:用于存放可变数据的目录,如日志文件、邮件和数据库等。
5.5 封装构建好的根文件系统,并挂载
用于封装根文件系统、挂载并创建启动脚本 start.sh
。
1. 封装根文件系统并挂载
(1)进入 ~/Desktop/linux-cache/nfs/
目录:
cd ~/Desktop/linux-cache/nfs
(2) 创建临时目录:
创建一个用于挂载根文件系统的临时目录:
sudo mkdir mnt
(3)制作SD卡文件系统镜像:
使用 dd
命令创建一个大小为 32MB 的空文件 rootfs.ext3
(可看作SD卡):
sudo dd if=/dev/zero of=rootfs.ext3 bs=1M count=32
(4)格式化磁盘映像为 ext3 文件系统:
将文件格式化为 ext3 文件系统:
sudo mkfs.ext3 rootfs.ext3
(5)挂载文件系统到临时目录:
将刚创建的文件挂载到临时目录:
sudo mount -t ext3 rootfs.ext3 ~/Desktop/linux-cache/nfs/mnt -o loop
(6)复制 NFS 根文件系统的内容:
将 ~/Desktop/linux-cache/nfs
目录下的所有内容复制到挂载的临时目录中:
sudo cp -r nfs/* ~/Desktop/linux-cache/nfs/mnt
(7)卸载临时目录:
卸载挂载的文件系统:
sudo umount temp
(8)移动磁盘映像文件:
将 rootfs.ext3
文件移动到 tftpboot
目录:
sudo mv rootfs.ext3 /home/wangbeiy/Desktop/linux-cache/tftpboot
2. 创建和修改启动脚本 start.sh
(1)进入 tftpboot
目录:
cd /home/wangbeiy/Desktop/linux-cache/tftpboot
(2)创建和编辑启动脚本 start.sh
:
使用文本编辑器创建或编辑启动脚本 start.sh
:
sudo gedit start.sh
(3) 添加启动命令:
在 start.sh
中输入以下内容,以启动 QEMU 虚拟机:
#!/bin/bash# 启动 QEMU 虚拟机,要求使用 root 登录qemu-system-arm \\ -M vexpress-a9 \\ -m 512M \\ -kernel zImage \\ -dtb vexpress-v2p-ca9.dtb \\ -nographic \\ -append \"root=/dev/mmcblk0 rw console=ttyAMA0\" \\ -sd rootfs.ext3
qemu-system-arm \\
qemu-system-arm
:这是QEMU虚拟机监控程序的ARM架构仿真器。它允许用户模拟ARM硬件环境并运行ARM架构的操作系统。
-M vexpress-a9 \\
-M vexpress-a9
:指定模拟的机器类型为vexpress-a9
,这是一种基于ARM Cortex-A9的开发板,适合用于嵌入式开发和测试。
-m 512M \\
-m 512M
:分配给虚拟机的内存大小为512MB。这决定了运行在虚拟机中的操作系统和应用程序可以使用的内存量。
-kernel zImage \\
-kernel zImage
:指定要使用的内核映像文件,这里使用的是zImage
。zImage
是一种压缩的Linux内核映像格式,适合在内存有限的环境中使用。
-dtb vexpress-v2p-ca9.dtb \\
-dtb vexpress-v2p-ca9.dtb
:指定设备树二进制(Device Tree Blob)文件。设备树描述了硬件的配置和特性,vexpress-v2p-ca9.dtb
是与vexpress-a9
平台匹配的设备树文件,提供了关于硬件设备的信息。
-nographic \\
-nographic
:禁止使用图形用户界面,所有输入和输出通过控制台(通常是命令行界面)进行。这适用于服务器或嵌入式环境中,不需要图形界面。
-append \"root=/dev/mmcblk0 rw console=ttyAMA0\" \\
-append \"root=/dev/mmcblk0 rw console=ttyAMA0\"
:向内核传递启动参数。这些参数的意义如下:root=/dev/mmcblk0
:指定根文件系统的位置为/dev/mmcblk0
,这通常是SD卡或eMMC设备的标识,虚拟机将在此设备上查找根文件系统。rw
:表示以读写模式挂载根文件系统。console=ttyAMA0
:指定控制台输出设备为ttyAMA0
,这是与ARM开发板串行端口相关的设备,允许用户通过串行接口与虚拟机进行交互。
-sd rootfs.ext3
-sd rootfs.ext3
:指定一个SD卡镜像文件rootfs.ext3
,作为虚拟机的存储介质。该文件系统通常包含根文件系统及其内容,QEMU将在此镜像中查找并挂载根文件系统。
这条命令启动了一个基于ARM Cortex-A9的虚拟机,使用了特定的内核映像和设备树文件,配置了内存和存储,并通过命令行接口与虚拟机进行交互。这种设置用于嵌入式开发、测试和调试ARM架构的操作系统和应用程序。
(4)保存并退出编辑器:
保存对 start.sh
的更改并退出编辑器。
(5)设置可执行权限:
确保脚本具有可执行权限:
sudo chmod +x start.sh
使用 su
命令切换到 root 用户,然后执行脚本:
# 切换到 root 用户su root# 启动脚本./start.sh
如图,挂载成功,内核版本也是5.10.99版本
这里就是上面我们制作的根文件系统
如果您想将 start.sh
脚本修改为支持 LCD 启动,并将控制台输出设置为通过 LCD 显示,而不是串口,需将 console=ttyAMA0
修改为 console=tty0
并且删除-nographic
。
修改后的 start.sh
#!/bin/bash# 启动 QEMU 虚拟机,使用 LCD 显示qemu-system-arm \\ -M vexpress-a9 \\ -m 512M \\ -kernel zImage \\ -dtb vexpress-v2p-ca9.dtb \\ -append \"root=/dev/mmcblk0 rw console=tty0\" \\ -sd rootfs.ext3
更新说明
console=tty0
:
- 这一部分的修改将控制台输出重定向到首个虚拟终端,适用于 LCD 显示。
其他参数:
- 并且删除
-nographic
,保持了其他参数不变,确保虚拟机在启动时能够使用您提供的内核和设备树文件,并正确挂载根文件系统。
保存和设置执行权限
确保您保存了对 start.sh
的更改,并确保脚本具有可执行权限:
sudo chmod +x start.sh
这样,就完成了对 start.sh
的修改,可以通过此脚本启动 QEMU 虚拟机并使用 LCD 显示输出。
3. 说明
- 确保在执行这些步骤前,您已经准备好所需的
zImage
和vexpress-v2p-ca9.dtb
文件,并且它们在正确的位置。 - 根据需要,您可以在根文件系统内进行额外的配置,以确保系统启动时允许 root 登录。
通过以上可以成功封装根文件系统并创建启动脚本 start.sh
,以便使用 QEMU 启动 ARM 虚拟机。
4. 修改网卡信息并设置桥接(Bridge)
确定有以下脚本文件
(1)查看当前网卡信息
ifconfig# 或ip a
找到你要桥接的网卡名称(如 ens33
、eth0
等)。
(2)修改 netplan
配置文件
Ubuntu 18.04+ 使用 netplan
管理网络,配置文件通常位于 /etc/netplan/
目录下,如:
sudo nano /etc/netplan/01-network-manager-all.yaml
(如果文件名不同,请根据实际情况修改)
修改/etc/netplan/01/01-network-manager-all.yaml的信息配置,输入以下内容:
network: version: 2 renderer: networkd ethernets: ens33: #这里设置的是你还需要上网的网卡, ifconfig查看 dhcp4: no ens37: #这里设置的是br0桥接到的网卡 dhcp4: yes bridges: br0: #这里设置的是br0网桥 dhcp4: yes interfaces: - ens33 #声明br0网桥接入的网卡是ens33
应用配置:
sudo netplan applyifconfig # 检查 br0 是否生效
当出现以下报错时:
如果文件存在,检查格式问题:
sudo netplan --debug try
如果提示 Invalid YAML,说明配置文件格式错误,请检查缩进、冒号等。
使用 yaml 在线校验工具(如 YAML Lint)检查语法。
(3)修改/etc/qemu-ifdown信息配置
/etc/qemu-ifdown
配置
你可以使用任何文本编辑器(如 nano
、gedit
)来编辑这个文件。
打开 /etc/qemu-ifdown
文件:
sudo gedit /etc/qemu-ifdown
复制并粘贴上述内容,保存并关闭文件。
#! /bin/sh# Script to shut down a network (tap) device for qemu.# Initially this script is empty, but you can configure,# for example, accounting info here.echo sudo brctl delif br0 $1sudo brctl delif br0 $1echo sudo tunctl -d $1sudo tunctl -d $1echo brctl showbrctl show
(4)/etc/qemu-ifup
配置
#!/bin/shecho sudo tunctl -u $(id -un) -t $1sudo tunctl -u $(id -un) -t $1echo sudo ifconfig $1 0.0.0.0 promisc upsudo ifconfig $1 0.0.0.0 promisc upecho sudo brctl addif br0 $1sudo brctl addif br0 $1echo brctl showbrctl showsudo ifconfig br0 192.168.214.138 # 这里设置的是网桥br0的地址
5. 编写脚本
-
内核支持:确保虚拟机中的 Linux 内核支持 9p 文件系统。可以通过以下命令检查:
grep CONFIG_NET_9P /proc/config.gz
-
权限问题:如果在虚拟机中无法访问共享文件夹,可以检查宿主机上的文件权限。
chmod -R 755 ~/Desktop/linux-cache/tftpboot/shared-dir
如果未启用,需要重新编译内核并启用以下选项:
CONFIG_NET_9P
CONFIG_NET_9P_VIRTIO
CONFIG_9P_FS
CONFIG_9P_FS_POSIX_ACL
- 网络配置:如果虚拟机使用的是用户网络模式(-net user),可能需要调整防火墙设置,以允许 9p 协议的通信。
开启权限
cd ~/linux-kernel-source-code/tftpbootchmod 777 start.shls -l
编辑 start.sh
使用 gedit
或任何您喜欢的文本编辑器来编辑 start.sh
文件:
gedit start.sh
整理 start.sh
文件内容
#!/bin/bashsudo /home/wangbeiy/下载/prefix/bin/qemu-system-aarch64 \\ -M vexpress-a9 \\ -m 1024M \\ -kernel ./zImage \\ -dtb vexpress-v2p-ca9.dtb \\ -append \"root=/dev/mmcblk0 rw console=tty0\" \\ -device virtio-9p-device,fsdev=host_share,mount_tag=host_share \\ -fsdev local,id=host_share,path=/home/wangbeiy/Desktop/linux-cache/tftpboot/shared-dir,security_model=none \\ -sd rootfs.ext3
保存并退出
在 gedit
中,完成编辑后,点击“保存”按钮,然后关闭编辑器。
确认文件权限
确保 start.sh
文件的权限正确:
ls -l start.sh
您应该看到类似于以下内容的输出,表示文件权限为 777:
说明
tunctl -u $(id -un) -t $1
:
- 创建一个新的 TAP 设备,命名为
$1
(通常是tap0
)。 $(id -un)
自动获取当前用户的用户名。
ifconfig $1 0.0.0.0 promisc up
:
- 将 TAP 设备设置为混杂模式并激活。
brctl addif br0 $1
:
- 将创建的 TAP 设备加入到桥接接口
br0
,允许它参与网络通信。
brctl show
:
- 显示当前网桥的状态,可以查看哪些设备已经加入桥接。
sudo ifconfig br0 192.168.214.138
:
- 设置网桥
br0
的 IP 地址为192.168.214.138
。
5.6 QEMU 通过网络实现共享文件
1.通过 virtfs 共享文件
在 QEMU 中,virtfs 是一种高效且灵活的文件共享方式,基于 9p 文件系统协议,允许宿主机和虚拟机之间进行高效的文件共享。通过 virtfs,用户可以将宿主机上的目录共享给虚拟机,使得虚拟机能够像访问本地文件一样访问宿主机的文件。
使用 virtfs 的主要步骤包括在宿主机上设置共享目录,将文件放入该目录,然后在启动 QEMU 时使用 -virtfs
或 -fsdev
选项配置共享目录,指定路径、安全模型和挂载标签等参数。
具体而言,-virtfs local
表示使用本地路径作为共享文件夹,path
指定宿主机上的共享目录,mount_tag
用于在虚拟机中识别共享目录,security_model
定义安全模型,常用的有 passthrough
,它允许虚拟机直接访问宿主机的文件权限和用户 ID。
可以使用 -fsdev
选项定义共享目录并通过 -device virtio-9p
或 -device virtio-9p-mmio
将其挂载到虚拟机中。在虚拟机中,需要创建挂载点,然后使用 mount
命令将共享目录挂载到指定位置,通常需要指定文件系统类型为 9p,并设置相应的传输协议和版本。
成功挂载后,虚拟机将能够访问宿主机上的共享文件。为了实现自动挂载,可以将挂载命令添加到虚拟机的 /etc/fstab
配置文件中,确保在虚拟机重启后也能自动挂载。此外,需要注意的是,虚拟机的 Linux 内核必须支持 9p 文件系统,并且在宿主机上设置的共享目录的权限需要允许虚拟机访问。
下面是对您提供的关于在 QEMU 中使用 virtfs
进行文件共享及网络配置的详细步骤的整理和补充。
2. 在宿主机上设置共享目录
(1)创建一个共享目录:
cd ~/Desktop/linux-cache/tftpboot mkdir -p shared-dir
(2)将需要共享的文件放入此目录中。
3 启动 QEMU 配置
使用 -virtfs
选项
-virtfs
选项用于指定共享目录的路径、安全模型以及挂载点等参数。以下是一个完整的命令示例:
sudo /home/wangbeiy/下载/prefix/bin/qemu-system-aarch64 \\ -M vexpress-a9 \\ -m 1024M \\ -kernel ./zImage \\ -dtb vexpress-v2p-ca9.dtb \\ -nographic \\ -append \"root=/dev/mmcblk0 rw console=ttyAMA0\" \\ -virtfs local,path=/home/wangbeiy/Desktop/linux-cache/tftpboot/shared-dir,id=host_share,security_model=none
-virtfs local
: 表示使用本地目录作为共享文件夹。/home/wangbeiy/Desktop/linux-cache/tftpboot/shared-dir
: 指定宿主机上的共享目录路径。id=host_share
: 定义一个挂载标签,用于在虚拟机中识别共享目录。security_model=none
: 直接将文件系统的权限和用户 ID 映射到虚拟机中。
使用 -fsdev
和 -device
选项
#!/bin/bashsudo /home/wangbeiy/下载/prefix/bin/qemu-system-aarch64 \\ -M vexpress-a9 \\ -m 1024M \\ -kernel ./zImage \\ -dtb vexpress-v2p-ca9.dtb \\ -nographic \\ -append \"root=/dev/mmcblk0 rw console=ttyAMA0\" \\ -device virtio-9p-device,fsdev=host_share,mount_tag=host_share \\ -fsdev local,id=host_share,path=/home/wangbeiy/Desktop/linux-cache/tftpboot/shared-dir,security_model=none \\ -sd rootfs.ext3
-fsdev local,id=host_share,path=/home/wangbeiy/Desktop/linux-cache/tftpboot/shared-dir,security_model=none
-fsdev
:这是用于定义文件系统设备的选项。local
:这表示使用本地文件系统作为共享目录。id=host_share
:为这个文件系统设备指定一个唯一的标识符host_share
,在后续的其他配置中引用。path=/home/wangbeiy/Desktop/linux-cache/tftpboot/shared-dir
:指定宿主机上要共享的目录路径。这里是宿主机的一个具体目录/home/wangbeiy/Desktop/linux-cache/tftpboot/shared-dir
。security_model=none
:表示不施加任何安全限制,虚拟机可以直接以宿主机的权限访问共享目录中的文件。
-device virtio-9p-device,fsdev=host_share,mount_tag=host_share
-device
:用于添加设备到虚拟机。virtio-9p-device
:表示使用 Virtio 9P 设备,这是一种高效的文件系统共享设备,基于 9P 协议。fsdev=host_share
:将之前定义的文件系统设备host_share
连接到这个 Virtio 9P 设备。mount_tag=host_share
:为虚拟机中挂载的共享目录指定一个挂载标签host_share
,这样在虚拟机内可以通过这个标签来引用和挂载这个共享目录。
通过这两个选项,可以在虚拟机中设置一个名为 host_share
的共享目录。虚拟机将能够直接访问宿主机
/home/wangbeiy/Desktop/linux-cache/tftpboot/shared-dir
目录中的文件,而不受额外的权限限制。这种配置非常适合开发和测试环境,能够简化文件共享的流程。
注意:如果使用
-device virtio-9p-pci
可能会遇到 “No ‘PCI’ bus found” 错误,此时请使用-device virtio-9p-mmio
替代。
4 虚拟机中的配置
(1)手动挂载
创建挂载点:
mkdir -p /mnt/shared
挂载共享文件夹:
mount -t 9p -o trans=virtio,version=9p2000.L host_share /mnt/shared
验证挂载:
ls /mnt/shared
(2)自动挂载(可选)
为了在虚拟机重启后自动挂载共享文件夹,可以将挂载命令添加到 /etc/fstab
文件中。例如:
echo \"host_share /mnt/shared 9p trans=virtio,version=9p2000.L,rw,share,nobootwait,posixacl,msize=104857600 0 0\" >> /etc/fstab
然后运行以下命令使配置生效:
mount -a
5.7 网络配置
1. NAT 模式
步骤 1:启动 QEMU 并配置 NAT 网络
启动 QEMU 时,使用 -netdev user
和 -device virtio-net
参数来配置 NAT 网络。以下是一个示例命令:
#!/bin/bashsudo /home/wangbeiy/下载/prefix/bin/qemu-system-aarch64 \\ -M vexpress-a9 \\#!/bin/bashsudo /home/wangbeiy/下载/prefix/bin/qemu-system-aarch64 \\ -M vexpress-a9 \\ -m 1024M \\ -kernel ./zImage \\ -dtb vexpress-v2p-ca9.dtb \\ -nographic \\ -netdev user,id=net0,ipv6=off,hostfwd=tcp::8022-:22 \\ -append \"root=/dev/mmcblk0 rw console=ttyAMA0\" \\ -device virtio-net-device,netdev=net0 \\ -device virtio-9p-device,fsdev=host_share,mount_tag=host_share \\ -fsdev local,id=host_share,path=/home/wangbeiy/Desktop/linux-cache/tftpboot/shared-dir,security_model=none \\ -sd rootfs.ext3
-netdev user,id=net0,ipv6=off,hostfwd=tcp::8022-:22
:配置 NAT 网络,hostfwd
参数将虚拟机的 22 端口(SSH)映射到宿主机的 8022 端口。-device virtio-net-device,netdev=net0
:将虚拟网卡连接到 NAT 网络。
步骤 2:在虚拟机中配置网络
虚拟机启动后,可以使用以下命令检查网络配置:
ip addr show
如果未分配 IP 地址,可以尝试使用 DHCP 获取 IP 地址:
udhcpc
如果 DHCP 无法获取 IP 地址,可以手动配置静态 IP 地址:
# 启动网络接口ifconfig eth0 up# 配置 IP 地址busybox ifconfig eth0 10.0.2.15 netmask 255.255.255.0# 添加默认路由busybox route add default gw 10.0.2.2
步骤 3:验证网络连接
在虚拟机中,使用以下命令测试网络连接:
ping 8.8.8.8
步骤 4:配置 DNS
如果虚拟机可以 ping 通外网,但无法解析域名,可以编辑 /etc/resolv.conf
文件,添加 DNS 服务器地址:
echo \"nameserver 8.8.8.8\" >> /etc/resolv.confecho \"nameserver 8.8.4.4\" >> /etc/resolv.conf
2. Tap 模式(网桥模式)
步骤 1:创建 TAP 接口
创建 TAP 接口:
sudo ip tuntap add dev tap0 mode tapsudo ip link set tap0 up
步骤 2:创建桥接接口
创建桥接接口:
sudo ip link add name br0 type bridgesudo ip link set br0 up
将物理网络接口添加到桥接接口:
sudo ip link set eth0 master br0
将 TAP 接口添加到桥接接口:
sudo ip link set tap0 master br0
步骤 3:配置 QEMU 启动命令
启动 QEMU 并配置 TAP 接口:
#!/bin/bashsudo /home/wangbeiy/下载/prefix/bin/qemu-system-aarch64 \\ -M vexpress-a9 \\#!/bin/bashsudo /home/wangbeiy/下载/prefix/bin/qemu-system-aarch64 \\ -M vexpress-a9 \\ -m 1024M \\ -kernel ./zImage \\ -dtb vexpress-v2p-ca9.dtb \\ -nographic \\ -netdev user,id=net0,ipv6=off,hostfwd=tcp::8022-:22 \\ -append \"root=/dev/mmcblk0 rw console=ttyAMA0\" \\ -netdev tap,id=net0,ifname=tap0,script=no,downscript=no \\ -device virtio-net-device,netdev=net0 \\ -device virtio-9p-device,fsdev=host_share,mount_tag=host_share \\ -fsdev local,id=host_share,path=/home/wangbeiy/Desktop/linux-cache/tftpboot/shared-dir,security_model=none \\ -sd rootfs.ext3
3. 注意事项
- 权限问题:创建 TAP 接口和配置桥接接口需要管理员权限。
- 网络配置:确保宿主机的网络配置支持桥接模式,特别是防火墙和网络策略。
3. 命令示例
以下是一些 QEMU 启动命令的示例:
bzImagePath=/home/liuqz/work/linux/linux-6.12.5/arch/x86/bootimage: cp ${bzImagePath}/bzImage ./ramfs: cd ./rootfs && find . -print0 | cpio -ov --null --format=newc | gzip -9 > ../initramfs.imgrun: qemu-system-x86_64 \\ -kernel ${bzImagePath}/bzImage \\ -initrd initramfs.img \\ -m 1G -nographic \\ -virtfs local,path=/mnt/shared,mount_tag=host_share,security_model=none \\ -net user,id=net0,ipv6=off,hostfwd=tcp::8022-:22 \\ -net nic,model=virtio \\ -append \"earlyprintk=serial,ttyS0 console=ttyS0 nokaslr\"debug: qemu-system-x86_64 \\ -kernel ${bzImagePath}/bzImage \\ -initrd initramfs.img \\ -m 1G -nographic \\ -S -s \\ -append \"earlyprintk=serial,ttyS0 console=ttyS0 nokaslr\"run2: qemu-system-x86_64 \\ -kernel ${bzImagePath}/bzImage \\ -m 1G -nographic \\ -device qemu-xhci \\ -virtfs local,path=/mnt/shared,mount_tag=host_share,security_model=none \\ -net user,id=net0,ipv6=off,hostfwd=tcp::8022-:22 \\ -net nic,model=virtio \\ -drive file=root0.img,format=raw \\ -append \"earlyprintk=serial,ttyS0 console=ttyS0 root=/dev/sda rw\"run3: qemu-system-x86_64 \\ -kernel ${bzImagePath}/bzImage \\ -m 1G -nographic \\ -device qemu-xhci \\ -virtfs local,path=/mnt/shared,mount_tag=host_share,security_model=none \\ -net user,id=net0,ipv6=off,hostfwd=tcp::8022-:22 \\ -net nic,model=virtio \\ -drive file=root1.img,format=raw \\ -append \"earlyprintk=serial,ttyS0 console=ttyS0 root=/dev/sda rw nokaslr\"
以上是通过 QEMU 使用 virtfs
进行文件共享及网络配置的详细整理,涵盖了共享目录的设置、网络配置与示例命令等内容。
六、在开发板上运行应用和内核驱动程序
6.1 编写测试应用程序
在~/Desktop/linux-cache/tftpboot/shared-dir下 gedit 得到text.c
#include int main(){printf(\"hello world!!!\\n\");return 0;}
使用交叉编译工具进行编译代码
在qemu中则成功运行
6.2 编写测试内核驱动程序
hello.c测试程序
#include #include MODULE_LICENSE(\"GPL\");static int hello_init(void) { printk(KERN_ALERT \"---1\\n\"); printk(KERN_ALERT \"Hello World!\\n\"); printk(KERN_ALERT \"Hello vexpress!\\n\"); printk(KERN_ALERT \"---2\\n\"); return 0;}static void hello_exit(void) { printk(KERN_ALERT \"Goodbye, crazy world!\\n\");}module_init(hello_init);module_exit(hello_exit);
Makefile 代码:
.PHONY: all cleanifneq ($(KERNELRELEASE),)obj-m := hello.oelseEXTRA_CFLAGS += -DDEBUGKDIR := ~/Desktop/linux-cache/linux-5.10.99all:make CROSS_COMPILE=arm-linux-gnueabi- ARCH=arm -C $(KDIR) M=$(PWD) modulesclean:rm -fr *.ko *.o *.mod.o *.mod.c *.symvers *.order .*.ko .tmp_versionsendif
代码说明
.PHONY 声明:
PHONY: all clean
表示all
和clean
是伪目标。
条件判断:
ifneq ($(KERNELRELEASE),)
用于判断当前是否在内核构建过程中。- 如果在内核构建过程中,设置
obj-m
为目标模块hello.o
。
构建设置:
EXTRA_CFLAGS += -DDEBUG
:如果不在内核构建过程中,增加调试选项。KDIR := ~/Desktop/linux-cache/linux-5.10.99
:指定内核源代码的路径。
目标 all
:
- 使用
make
命令编译模块,指定交叉编译工具链CROSS_COMPILE
和目标架构ARCH
,使用-C
选项指定内核源代码目录$(KDIR)
,M=$(PWD)
表示当前目录。
目标 clean
:
- 删除生成的文件,包括模块文件、对象文件、临时文件等。
开发板进行测试
#安装驱动insmod hello.ko#卸载驱动rmmod hello.ko#查看驱动lsmod
到此,我们的环境都已搭建完成,诸君!这不仅是一个全新的开始,更是我们追求技术卓越与创新的旅程。每一步的努力和坚持,都将为我们的未来铺就更加广阔的道路。
无论是在编写代码、调试程序,还是在解决问题的过程中,大家都将面临挑战,但请相信,通过我们的合作与坚持,必能克服困难,迎接成功的曙光。
让我们携手并进,共同探索未知的领域,创造出更加美好的明天。未来属于勇敢者,属于不断学习、追求进步的人。无论何时,请牢记我们的初心,保持对技术的热爱与激情!
让我们一起奋勇向前,成就更精彩的明天!