当前位置: 首页 > news >正文

qemu和gcc编译

编译qemu-arm

公司的系统中没有这个软件,设置外部源也下载不了,只能自己编译qenu-arm。

1. 安装编译依赖

sudo dnf install git gcc make ninja-build glib2-devel pixman-devel zlib-devel python3

2. 克隆并编译(仅构建 ARM 目标,大幅缩短编译时间)

gitclone https://gitlab.com/qemu-project/qemu.gitcdqemu# 1. 告诉系统优先去 Conda 环境里找库文件exportPKG_CONFIG_PATH=$CONDA_PREFIX/lib/pkgconfig:$PKG_CONFIG_PATH# 2. 回到 QEMU 源码目录,彻底清理旧的编译缓存cd~/code/gitlab/qemurm-rfbuildmkdirbuild&&cdbuild# 3. 强制指定编译器,并带上更新后的环境变量重新配置CC=$CONDA_PREFIX/bin/gccCXX=$CONDA_PREFIX/bin/g++../configure --target-list=aarch64-softmmu,arm-softmmu --enable-slirpmake-j$(nproc)

3. 编译产物位于 build/ 目录,无需安装即可直接使用

./build/qemu-system-aarch64 --version

编译时是gcc版本太低。没有办法,无法安装源里的高版本gcc,只能再编译一个高版本的gcc
—2026-05-24 通过conda安装gcc的问题解决了。conda设置镜像源有坑,除了设置镜像源,还要修改默认通道。
conda里的gcc 包括arm和x86的,是15.x

下载编译gcc

下载 GCC 12.4.0(最新 12.x 稳定版)

清华 TUNA 镜像(仅当 GNU FTP 不通时使用)

wget https://mirrors.tuna.tsinghua.edu.cn/gnu/gcc/gcc-12.4.0/gcc-12.4.0.tar.xz
tar xf gcc-12.4.0.tar.xz
cd gcc-12.4.0

【关键】自动下载 gmp, mpfr, mpc 三大数学库依赖

该脚本会从 GNU 内部可信源拉取,通常不受企业代理限制

./contrib/download_prerequisites

创建独立构建目录(严禁在源码目录内直接 configure)

mkdir build && cd build

配置:安装到当前 conda 环境,仅启用 C/C++,禁用多架构以加速编译

../configure\--prefix=$CONDA_PREFIX\--enable-languages=c,c++\--disable-multilib\--disable-bootstrap

并行编译(根据服务器核心数调整 -j 参数)

make -j$(nproc)

安装到 $CONDA_PREFIX

make install

$ gcc --version
gcc (GCC) 12.4.0

编写一个最简的OS

编写一个最简单的 ARM OS 是学习操作系统原理(如启动流程、内存管理、异常处理)的绝佳途径。对于初学者,强烈建议使用 QEMU 模拟 ARM64 (AArch64) 架构,而不是直接操作物理开发板。QEMU 提供了完美的调试环境,且 AArch64 的文档比老旧的 ARM32 更清晰、更现代化。

以下是一个“最小可运行”的 ARM64 OS 学习路线与调试指南。

🛠️ 1. 环境准备

你需要一套交叉编译工具链和 QEMU 模拟器:

# Ubuntu/Debiansudoaptinstallgcc-aarch64-linux-gnu binutils-aarch64-linux-gnu qemu-system-arm gdb-multiarchmake--这个armgcc,最后通过conda下载的。conda还有演进版 pixi,推荐用这个代替conda# macOS (Homebrew)brewinstallaarch64-elf-gcc aarch64-elf-binutils qemu gdb

💻 2. 最小 OS 代码结构

一个能跑起来的最小 OS 只需要两个文件:一个汇编启动文件和一个 C 语言主函数。

boot.S- 引导与初始化

OS 启动时 CPU 处于特定状态,必须用汇编完成最基础的初始化才能跳转到 C 代码。

.section ".text.boot" .global _start _start: // 1. 检查处理器ID,确保只有核心0执行后续代码 mrs x0, mpidr_el1 and x0, x0, #0xFF cbz x0, primary_core // 非核心0进入死循环 b . primary_core: // 2. 设置栈指针 (SP),C语言运行必须有栈 // 假设我们将栈顶设置在内存 0x48000000 处 ldr x0, =0x48000000 mov sp, x0 // 3. 清零 BSS 段(未初始化全局变量) ldr x0, =__bss_start ldr x1, =__bss_end clear_bss: cmp x0, x1 b.ge jump_to_c stp xzr, xzr, [x0], #16 b clear_bss jump_to_c: // 4. 跳转到 C 语言入口 bl kernel_main // 如果 kernel_main 返回,进入死循环 b .
kernel.c- 内核主逻辑

最简单的内核:向 UART 串口打印字符,证明 OS 已接管硬件。

// PL011 UART 基地址 (QEMU virt 机器默认)#defineUART_BASE0x09000000voiduart_putc(charc){volatileunsignedint*uart_dr=(unsignedint*)(UART_BASE+0x00);*uart_dr=c;}voiduart_puts(constchar*s){while(*s)uart_putc(*s++);}voidkernel_main(void){uart_puts("Hello, ARM64 OS!\n");// 简单的异常测试:触发一个未定义指令异常// asm volatile(".word 0x00000000");while(1){// 内核空闲循环asmvolatile("wfi");}}
linker.ld- 链接脚本

告诉链接器如何将代码和数据放置到正确的内存地址。这是 OS 开发中最容易出错的地方

ENTRY(_start) SECTIONS { /* QEMU virt 机器的 RAM 起始地址 */ . = 0x40080000; .text : { *(.text.boot) *(.text*) } .rodata : { *(.rodata*) } .data : { *(.data*) } . = ALIGN(16); __bss_start = .; .bss : { *(.bss*) *(COMMON) } __bss_end = .; }
Makefile- 构建与运行
# ========================================== # 交叉编译工具链配置 # ========================================== CROSS_COMPILE = aarch64-linux-gnu- CC = $(CROSS_COMPILE)gcc LD = $(CROSS_COMPILE)ld OBJCOPY = $(CROSS_COMPILE)objcopy # ========================================== # 编译与链接选项 # ========================================== CFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -mcpu=cortex-a72 LDFLAGS = -T linker.ld -nostdlib # ========================================== # 目标文件定义 # ========================================== TARGET = kernel.img SRCS = boot.S kernel.c # 自动将 .S 和 .c 后缀替换为 .o OBJS = $(SRCS:.S=.o) OBJS = $(OBJS:.c=.o) # ========================================== # 默认目标:执行 make 时默认构建 kernel.img # ========================================== all: $(TARGET) # ========================================== # 编译规则:将 .S 和 .c 文件编译为 .o 目标文件 # ========================================== %.o: %.S $(CC) $(CFLAGS) -c $*.S -o $*.o %.o: %.c $(CC) $(CFLAGS) -c $*.c -o $*.o # ========================================== # 链接规则:将所有 .o 目标文件链接成 kernel.bin # ========================================== kernel.bin: $(OBJS) $(LD) $(LDFLAGS) $(OBJS) -o kernel.bin # ========================================== # 格式转换:将 ELF 格式的 kernel.bin 转为纯二进制 kernel.img # ========================================== kernel.img: kernel.bin $(OBJCOPY) -O binary kernel.bin kernel.img # ========================================== # 运行与调试目标 # ========================================== run: $(TARGET) qemu-system-aarch64 -M virt -cpu cortex-a72 -nographic -kernel kernel.img debug: $(TARGET) qemu-system-aarch64 -M virt -cpu cortex-a72 -nographic -kernel kernel.img -s -S & gdb-multiarch -ex "target remote localhost:1234" -ex "symbol-file kernel.bin" # ========================================== # 辅助目标 # ========================================== clean: rm -f $(OBJS) kernel.bin $(TARGET) .PHONY: all run debug clean help help: @echo "可用的 make 命令:" @echo " make - 编译生成 kernel.img(默认)" @echo " make run - 使用 QEMU 运行内核" @echo " make debug - 启动 QEMU 并进入 GDB 调试模式" @echo " make clean - 清理所有编译生成的文件"

🔍 3. 如何高效调试

OS 开发没有printf可用(在你自己实现之前),必须依赖调试器。

GDB + QEMU 联合调试

运行make debug后,GDB 会连接到 QEMU 的内置 GDB Server。你可以像调试普通程序一样调试 OS:

GDB 命令作用OS 调试特殊用途
b kernel_main在 C 入口设断点验证汇编跳转是否成功
info registers查看所有寄存器检查 SP、ELR_EL1、ESR_EL1
x/16xb 0x40080000查看内存十六进制验证代码是否正确加载到指定地址
set $sp=0x48000000手动修改寄存器修复因栈设置错误导致的崩溃
stepi/si单步执行一条汇编跟踪启动阶段每一条指令
关键调试技巧
  1. 永远先调通串口输出:在写任何复杂功能前,先让uart_putc工作。它是你后续所有调试信息的唯一出口。
  2. 善用 QEMU 的-d参数:当 OS 莫名重启或挂起时,使用qemu-system-aarch64 -d int,unimp,guest_errors -D qemu.log ...,QEMU 会将中断、未实现指令、客户机错误记录到日志文件中,这比 GDB 更能定位底层问题。
  3. 检查异常级别 (EL):AArch64 有 EL0-EL3 四个特权级。QEMU virt 默认从 EL1 启动。如果你的代码试图访问 EL2/EL3 寄存器,会触发异常。用mrs x0, CurrentEL确认当前特权级。

📚 4. 推荐学习路径与资源

不要试图从零发明一切,站在巨人肩膀上:

  1. 入门教程:搜索“Writing an Operating System for Raspberry Pi 3”(GitHub: isometimes/rpi3-os-tutorial)。虽然针对树莓派,但前几章完全适用于 QEMU ARM64,且讲解极其细致。
  2. 进阶参考“Operating Systems: From 0 to 1”或 MIT 6.S081 (RISC-V,但概念完全相通)。
  3. 官方文档:ARM Architecture Reference Manual (DDI0487)。不需要全读,重点看System RegistersException Model章节。
  4. 下一步演进:在跑通上述最小 OS 后,按此顺序添加功能:
    • 实现完整的异常向量表 (vbar_el1)
    • 实现简单的内存分配器 (buddy system)
    • 实现进程/线程上下文切换
    • 实现一个简单的 Shell

⚠️重要提醒
OS 开发中 90% 的 Bug 来自链接脚本地址错误对齐问题。当你遇到无法解释的崩溃时,第一反应应该是检查linker.ld中的地址是否与 QEMU 的内存映射一致,以及数据结构是否满足 ARM 的对齐要求。

conda修复好了

什么意思,我们设置了镜像源,还要修改默认通道吗

● 是的,需要理解 conda 的通道机制:

Conda 通道的工作原理

当你设置 channels: 时,你只是添加了额外的通道,但 defaults 这个特殊通道仍然指向官方源。

你的配置:

channels:
- https://mirrors.ustc.edu.cn/anaconda/cloud/conda-forge/
- defaults # 这个是特殊的!

defaults 通道的实际含义:

  • defaults 不是一个 URL,而是一个别名
  • 它默认展开为:
    • https://repo.anaconda.com/pkgs/main/
    • https://repo.anaconda.com/pkgs/r/

所以即使你设置了镜像源,只要 defaults 还在,conda 仍会尝试访问 repo.anaconda.com。

解决方案

有两种方式:

方案 1:完全禁用 defaults(推荐)

channels:
- https://mirrors.ustc.edu.cn/anaconda/cloud/conda-forge/
只从 conda-forge 安装包。

方案 2:重写 defaults 指向镜像

default_channels:
- https://mirrors.ustc.edu.cn/anaconda/pkgs/main/
- https://mirrors.ustc.edu.cn/anaconda/pkgs/r/
channels:
- https://mirrors.ustc.edu.cn/anaconda/cloud/conda-forge/
- defaults

为什么会这样设计?

这是 Anaconda 的设计问题。defaults 是硬编码的特殊通道,不像其他通道可以直接替换 URL。很多用户都不知道这个坑。


简单记忆:用国内镜像时,要么去掉 defaults,要么必须显式重写 default_channels。

http://www.jsqmd.com/news/881048/

相关文章:

  • 从单用户到团队协作:给你的Ubuntu服务器配置多用户SSH访问权限(附sudo权限管理)
  • AI agent案例汇总:基于 LangGraph 的智能对话 Agent 实现
  • 文章三:Elasticsearch 集群恢复和索引分布
  • 2026年当前,上海别墅大宅新风系统可靠服务商深度解析 - 2026年企业推荐榜
  • 机器学习数据集详解,公开免费数据集获取渠道汇总
  • Try和expect的正确使用方式
  • 连锁董事网络指标数据(2001-2024)
  • 2026电工杯数学建模竞赛A题论文、代码、数据
  • 数据结构:线性表和顺序表
  • 2026槽式电缆桥架优质推荐指南:网格电缆桥架、铝合金走线架、不锈钢电缆桥架、北京电缆桥架厂家、托盘式电缆桥架选择指南 - 优质品牌商家
  • Claude Code 在安装vscode插件时遇到的问题。
  • 告别图形界面!5个CUPS命令行技巧,让你在Linux终端高效管理打印机
  • 2026微型舵机优质推荐榜:小型舵机/尾翼用方扁舵机/工业舵机/德晟舵机/数字舵机/无人机舵机/无刷舵机/最小的舵机/选择指南 - 优质品牌商家
  • 2026电工杯数学建模竞赛A题论文、代码、数据(改进)
  • # 网页设计学习感悟
  • 朝晖玻璃钢:玻璃钢保温水箱/玻璃钢消防水箱/玻璃钢罐化粪池/碳钢水箱/立式不锈钢水箱/组合式玻璃钢水箱/雨水一体化提升泵站/选择指南 - 优质品牌商家
  • 别再手动下载DLL了!用Windows自带工具SFC/SCANNOW一键修复kernel32.dll错误
  • 别再让系统‘无家可归’:给已用满空间的Win10 SSD无损创建EFI引导分区指南
  • 2026年紫外线杀菌除藻灯优质厂家深度解析:聚焦技术、产能与服务三角 - 2026年企业推荐榜
  • Titanic数据集分析避坑指南:新手常犯的3个错误及如何修正
  • ubuntu2026.04部署k8s1.36版本的傻瓜式教程(注:运行时为docker,网络插件为calico)
  • 一文讲清楚规则、Skill、MCP
  • 2026泛塞封密封圈优质品牌推荐:聚四氟乙烯密封圈/铁氟龙密封圈/高分子材料密封圈/O型圈/PEEK密封圈/PU密封圈/选择指南 - 优质品牌商家
  • 别再让Ubuntu卡成PPT!手把手教你用swapfile把交换空间从1G扩容到64G(附权限修复)
  • 【iOS】底层原理:理解dyld
  • 告别虚拟机!手把手教你用U盘给新电脑装Win11+统信UOS 1060双系统(保姆级分区教程)
  • Win10开机WiFi列表全空?先别慌,按这个‘服务状态排查流程图’走一遍
  • 2026靠谱仪器推荐:Trim200离子束刻蚀机、Essent Optics分光光度计、LINZA分光光度计、LensCheck MTF传函仪选择指南 - 优质品牌商家
  • 告别下载量低迷,5套实操方法打通用户增长
  • MacBook新手福音:用Final Cut Pro 10.6.5搞定你的第一门视频课(附保姆级设置与导出指南)