ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Writing an OS in Rust:以纯 Rust 工具链构建内核——第二版里程碑解析

Writing an OS in Rust:以纯 Rust 工具链构建内核——第二版里程碑解析 文档教程技术博客操作系统【免费下载链接】blog_osWriting an OS in Rust项目地址https://gitcode.com/GitHub_Trending/bl/blog_os点击查看免费下载本文围绕 blog_os 仓库Writing an OS in Rust 系列博客的源代码仓库于 2018-03-09 发布的一篇里程碑式新闻展开历经六个月努力该系列成功让内核能够在 Windows、macOS 和 Linux 上原生构建且不依赖任何非 Rust 工具。文章将还原第一版对 C 工具链的依赖清单、跨平台构建的痛点以及第二版如何通过自研 Bootloader基于global_asm与随 Rust 分发的 LLD 链接器彻底摆脱这些依赖并结合仓库中的 第一版文章、第二版文章 与 Dockerfile 等证据逐层展开。读完你将掌握这套纯 Rust 构建链路的技术构成、关键配置与运行方式。背景第一版博客的 C 工具链依赖第一版博客edition-1从 最小 Multiboot 内核 讲起引导读者编写 x86 汇编启动代码、手工配置分页并最终切换到 64 位长模式。这套流程在构建侧依赖了相当多的 C 生态工具GRUB 引导程序负责把内核引导起来。为了把内核做成可启动的磁盘/光盘镜像还需要grub-mkrescue工具而它在 Windows 上非常难以运行。xorrisogrub-mkrescue内部依赖的镜像工具也必须一并安装。nasm 汇编器GRUB 只能启动到 32 位保护模式因此从保护模式进入 64 位长模式的初始化代码必须用汇编编写而当时用 Rust 的global_asm直接内联汇编的成熟度还不够需要借助独立的nasm汇编器编译.asm文件。GNU ld 链接器与自定义链接脚本需要把汇编目标文件与 Rust 代码链接到一起且必须使用自定义 链接脚本如把内核起始地址设为1M、保证.multiboot_header段位于文件开头才能被 GRUB 正确识别。make用来串联汇编 → 编译 Rust → 链接 → 调用grub-mkrescue的多个构建步骤。以第一版第一篇为例其构建流程大致是先用nasm multiboot_header.asm生成 Multiboot 2 头再用nasm boot.asm生成启动代码在0xb8000处写入 VGA 文本缓冲打印OK随后通过ld配合自定义链接脚本将两者链接为 ELF 可执行文件最后由grub-mkrescue打包成可启动镜像。可以看到读者必须完整安装一套 GNU/汇编工具链才能把内核跑起来。痛点macOS 与 Windows 上的艰难环境以及 Docker 临时方案这套依赖带来了大量反馈在 macOS 与 Windows 上配置如此复杂的工具链相当痛苦仓库中对应记录见第一版文章页提及的 macOS 已知问题。作为临时缓解手段项目曾在 docker/README.md 与 docker/Dockerfile 中提供了 Docker 支持——只需git、make和docker即可在容器内完成构建。从 Dockerfile 的apt-get install一行可以直观看到第一版完整依赖清单RUN apt-get update \ apt-get install -q -y --no-install-recommends \ nasm \ binutils \ grub-common \ xorriso \ grub-pc-bin \ ... cargo install xargo \ rustup component add rust-src容器内要预装nasm、binutils提供ld、grub-common、grub-pc-bin、xorriso还要安装xargo与rust-src组件。尽管 Docker 方案降低了环境差异但它仍然要求用户额外安装并理解一个依赖。因此当团队决定创作第二版初衷之一是原版的文章顺序导致难度跳跃过大时核心思考变成了能否彻底避免这些 C 依赖解决方案总览逐项替换只剩两个硬骨头先看哪些依赖可以轻松替换make有大量替代品包括 Rust 生态的just、cargo-make等任务运行器nasm可以用 Rust 的global_asm特性在 Rust 代码中直接内联汇编从而不再需要独立汇编器。替换完这两项之后剩下的两个真正的难题是引导程序bootloader与链接器linker。第二版正是围绕攻克这两个难题展开的。攻克难题一自研 Rust Bootloader为了摆脱对 GRUB 的依赖、并让内核开发更符合人体工程学团队决定用 Rust 的global_asm特性编写自己的引导程序即 rust-osdev 组织维护的bootloadercrate。这一选择带来一个显著收益内核可以大幅简化。因为切换到长模式与初始页表布局这类最底层的活全部可以在 bootloader 中完成第二版博客就可以跳过第一版开头的汇编层级文章如进入长模式直接从高层 Rust 代码起步。从 第二版最小 Rust 内核 可以看到 bootloader 当时已经具备的核心能力完成从保护模式到 64 位长模式的切换以64 位 ELF 二进制形式加载内核按照 ELF 文件中的声明执行正确的页表映射含正确的读/写/执行权限生成初始物理内存映射memory map供内核使用。原文也如实标注了它的状态当时仍是早期原型。后续计划是让 bootloader 更加稳定、补齐文档并最终在博客中新增Writing a Bootloader系列详细讲解其内部工作原理——这也解释了为什么第二版文章在使用 bootloader 时会强调版本兼容性第二版教程 中明确指出该篇仅兼容bootloader v0.9新版本使用了不同的构建系统。攻克难题二用随 Rust 分发的 LLD 完成跨平台链接自研 bootloader 到位后最后一个问题是平台无关的链接。解决方案是LLD——LLVM 项目的跨平台链接器它在x86架构上已经非常稳定。更重要的是LLD随 Rust 一起分发当时的 Rust PR 将其捆绑进工具链也就是说开发者无需任何额外安装即可使用。在 第二版最小 Rust 内核 的自定义目标文件中可以看到 LLD 的具体落地方式。内核并不面向现有操作系统因此需要一个自定义 target 描述文件例如x86_64-blog_os.json其中关键构建字段如下{ llvm-target: x86_64-unknown-none, data-layout: e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128, arch: x86_64, target-endian: little, target-pointer-width: 64, target-c-int-width: 32, os: none, executables: true, linker-flavor: ld.lld, linker: rust-lld, panic-strategy: abort, disable-redzone: true, features: -mmx,-sse,soft-float }这里linker: rust-lld正是使用随 Rust 分发的 LLD 作为链接器——不再依赖系统默认的 GNUld。其余几个字段也与内核构建场景直接相关panic-strategy: abort裸机目标不支持 panic 时的栈展开直接中止disable-redzone: true禁用红区优化为后续处理中断时的栈安全做准备features: -mmx,-sse,soft-float禁用 SIMD内核在系统调用/中断时保存庞大的 SIMD 寄存器状态会显著损害性能同时用soft-float以软件方式模拟浮点运算。第二版落地从 freestanding binary 到可启动内核当工具链彻底纯 Rust 化之后第二版教程已于仓库 第二版索引 处上线的构建体验变成了这样一条完整链路第一步freestanding Rust 二进制第二版第一篇 用#![no_std]与#![no_main]创建不链接标准库的裸机可执行文件这是内核开发的起点#![no_std] // 不链接 Rust 标准库 #![no_main] // 禁用所有 Rust 层入口点 use core::panic::PanicInfo; #[panic_handler] fn panic(_info: PanicInfo) - ! { loop {} } #[no_mangle] pub extern C fn _start() - ! { loop {} }第二步为自定义 target 重新编译 core由于core库是随编译器预编译的只对官方支持的 host triple 有效自定义 target 需要借助 nightly 的build-std功能按需重编core与compiler_builtins。配置位于.cargo/config.toml# in .cargo/config.toml [unstable] build-std-features [compiler-builtins-mem] build-std [core, compiler_builtins]其中compiler-builtins-mem用于启用memset/memcpy/memcmp等内存相关内建函数裸机环境下没有 C 库可链接。前置条件是通过rustup component add rust-src安装 Rust 源码。随后在.cargo/config.toml中设置默认 target[build] target x86_64-blog_os.json这样一条简单的cargo build就能产出裸机内核。第三步bootimage 生成可启动镜像内核链接之后还需要与 bootloader 结合成可启动的磁盘镜像。由于 cargo 不支持 post-build 脚本项目配套了bootimage工具cargo install bootimage rustup component add llvm-tools-preview cargo bootimage其幕后流程是先用cargo build编译内核为 ELF → 把bootloader依赖编译成独立可执行文件 → 将内核 ELF 的字节链接到 bootloader 之后。引导时bootloader 解析追加在尾部的 ELF 文件将各程序段按页表映射到虚拟地址、清零.bss段、设置栈最后读取入口地址即_start并跳转。产出物为target/x86_64-blog_os/debug/bootimage-blog_os.bin。第四步在 QEMU 或真机运行用 QEMU 验证qemu-system-x86_64 -drive formatraw,filetarget/x86_64-blog_os/debug/bootimage-blog_os.bin也可以写到 USB 启动真机注意会覆写设备上所有数据需谨慎选择设备名dd iftarget/x86_64-blog_os/debug/bootimage-blog_os.bin of/dev/sdX sync为了让开发更顺手还可以在.cargo/config.toml中配置runner直接cargo run完成编译 启动 QEMU[target.cfg(target_os none)] runner bootimage runner值得注意的是这条链路中不再出现grub-mkrescue、xorriso、nasm、ld或make中的任何一项——这正是里程碑的意义所在只要装了 Rust nightly含 LLD与 rustup 组件在任何主流桌面系统上都能以完全相同的方式构建和运行内核。里程碑的验证与后续演进从当前仓库结构可以印证这一里程碑的实际落地效果第二版文章目录 下已按计划迭代铺开——01-freestanding-rust-binary、02-minimal-rust-kernel、03-vga-text-buffer、04-testing、05-cpu-exceptions、06-double-faults、07-hardware-interrupts、08-paging-introduction、09-paging-implementation、10-heap-allocation、11-allocator-designs、12-async-await共十二篇覆盖从裸机二进制到异步执行的完整内核开发路径而第一版内容则以不同顺序、伴随各种改进被逐步迁移进新版本。仓库 README.md 还约定每篇文章对应一个独立 git 分支post-XX可用git worktree add code post-10之类的方式随时查看某篇文章的中间态源码方便读者对照学习。结语这篇 2018 年的里程碑宣告了一项关键工程决策的成功通过自研 Rust bootloader与随 Rust 分发的 LLD把内核构建链路中最后两个 C 依赖替换成了纯 Rust 方案。对后来的开发者而言这套设计直接转化为一次安装 nightly 一条cargo bootimage的低门槛入门体验也让用 Rust 写操作系统这一主题可以被更广泛的平台、更少的环境摩擦所触及。赞分享文档教程技术博客操作系统【免费下载链接】blog_osWriting an OS in Rust项目地址https://gitcode.com/GitHub_Trending/bl/blog_os点击查看免费下载相关推荐Security-101 课程解读基础设施安全能力实战指南——漏洞管理、CSPM 与 CNAPP 全解析Security 101 课程解读基础设施安全能力实战指南——漏洞管理、CSPM 与 CNAPP 全解析 本技术指南基于 Security 101 开源课程的文档教程技术博客操作系统Writing an OS in Rust微内核架构模块化设计思想Writing an OS in Rust微内核架构模块化设计思想 引言从单体内核到微内核的演进之路 在操作系统设计的历史长河中架构模式经历了从单体内核文档教程技术博客操作系统OpenTelemetry Collector 内部测试工具模块 testutil 实战指南可用端口分配、基准测试与 FIPS 场景跳过OpenTelemetry Collector 内部测试工具模块 testutil 实战指南可用端口分配、基准测试与 FIPS 场景跳过 go.opentel文档教程技术博客操作系统上一篇MMSegmentation 实战基于 Vampire 数据集的眼底荧光血管造影血管分割与 UNet 训练指南下一篇WindowResizer突破Windows窗口限制的智能调整工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表