news 2026/10/2 1:43:13

用 Rust 从零编写最小 x86_64 内核:blog_os 实战(目标平台配置、build-std 与 VGA 输出)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用 Rust 从零编写最小 x86_64 内核:blog_os 实战(目标平台配置、build-std 与 VGA 输出)
  • 文档
  • 教程
  • 技术博客
  • 操作系统

【免费下载链接】blog_os

Writing an OS in Rust

项目地址:https://gitcode.com/GitHub_Trending/bl/blog_os
点击查看免费下载

本篇技术指南聚焦于 Rust 操作系统开发项目 blog_os("Writing an OS in Rust")系列的第二篇教程,讲解如何基于上一篇的"独立式 Rust 可执行程序"(freestanding binary),构建一个能够真实引导启动、并向屏幕打印 "Hello World!" 的最小 64 位内核。读完本文,你将掌握自定义裸机目标平台的 JSON 规范编写、通过build-std为裸机重编译core库、利用bootimage工具将内核与引导程序链接成磁盘映像,以及用 QEMU 和真机 USB 启动内核的完整链路。

本文对应的原始教程文档位于 blog/content/edition-2/posts/02-minimal-rust-kernel/index.md(本仓库同时收录了俄文、简体中文等多语言翻译版本),完整源码托管在仓库的post-02分支。在动手之前,建议先阅读同系列的 《独立式 Rust 可执行程序》 掌握no_std、no_main、#[panic_handler]与_start入口点的基础。

计算机的启动过程:固件、BIOS 与引导程序

当我们按下电源开关,主板 [ROM](只读存储器)中存储的固件代码开始执行:它会运行加电自检(Power-On Self-Test),检测可用内存,并预初始化 CPU 与硬件,随后寻找可引导的磁盘并开始引导操作系统内核。

在 x86 架构上存在两种固件标准:

  • BIOS(Basic Input/Output System,基本输入输出系统):标准古老,但实现简单,自 1980 年代以来几乎所有 x86 机器都支持;
  • UEFI(Unified Extensible Firmware Interface,统一可扩展固件接口):更现代、功能更多,但配置更复杂。

本教程当前只提供 BIOS 支持,UEFI 支持已在计划之中(相关讨论见仓库历史 issue #349)。

BIOS 启动:从 16 位实模式到 64 位长模式

几乎所有 x86 系统都支持 BIOS 启动,包括使用模拟 BIOS 的现代 UEFI 机器——这意味着同一套启动逻辑可以覆盖数十年的硬件。但宽泛兼容性也是 BIOS 最大的缺点:启动前 CPU 必须进入 16 位的兼容模式(实模式,real mode),以便 1980 年代的老式引导程序仍能运行。

完整的 BIOS 启动流程如下:

  1. 电脑开机后,从主板上的特殊闪存加载 BIOS 固件;
  2. BIOS 执行硬件自检与初始化例程,然后寻找可引导磁盘;
  3. 找到后,控制权转交给磁盘开头的 512 字节可执行代码——引导程序(bootloader);
  4. 大多数引导程序超过 512 字节,因此通常拆分为:能塞进 512 字节的第一阶段,以及由第一阶段随后加载的第二阶段。

引导程序承担三项核心职责:

  • 确定内核镜像在磁盘上的位置,并将其加载进内存;
  • 将 CPU 从 16 位实模式切换到 32 位保护模式(protected mode),再切换到 64 位长模式(long mode)——只有在长模式下才能访问全部 64 位寄存器与完整主内存;
  • 向 BIOS 查询特定信息(如内存映射表 memory map)并传递给内核。

编写引导程序颇为繁琐:需要汇编语言,还要做大量"把这个魔数写进那个寄存器"之类的无趣工作。因此本教程不讲解引导程序的实现,而是使用bootimage工具,它能把引导程序自动附加到你的内核前面。

Multiboot 标准及其局限

为避免每个操作系统都实现只兼容自家内核的引导程序,自由软件基金会(FSF)于 1995 年制定了开放标准Multiboot,定义了引导程序与操作系统之间的统一接口,使任何符合 Multiboot 的引导程序都能加载任何符合 Multiboot 的操作系统。其参考实现是 Linux 系统上最流行的引导程序GNU GRUB。

让内核兼容 Multiboot 只需在文件开头插入一个 Multiboot 头。但 GRUB 与 Multiboot 标准存在若干问题:

  • 仅支持 32 位保护模式,切换到 64 位长模式的 CPU 配置仍需自行完成;
  • 设计目标是简化引导程序而非内核:例如内核必须以调整过的默认页大小链接,否则 GRUB 找不到 Multiboot 头;传递给内核的引导信息也充满架构相关的结构体,而非干净的抽象;
  • GRUB 与 Multiboot 标准文档稀少;
  • 创建可引导磁盘映像要求在宿主机安装 GRUB,使 Windows 或 macOS 上的开发更困难。

鉴于上述缺点,本系列决定不使用 GRUB 或 Multiboot 标准(但计划在 bootimage 工具中增加 Multiboot 支持;若想编写 Multiboot 兼容内核,可参考仓库中的 第一版(edition-1)教程)。

UEFI 现状

截至目前,本系列暂未提供 UEFI 支持,处于计划阶段。在 UEFI 上的引导加载需要在启动环境中调用 UEFI 固件提供的协议与服务,与 BIOS 的实模式加载路径完全不同,需要单独的引导程序实现。

最小内核:从宿主系统到目标系统

现在,我们对启动机制有了整体认识,目标明确:创建一个启动后在屏幕上打印 "Hello World!" 的磁盘映像。实现建立在上一篇的独立式 Rust 可执行程序之上。

上一篇中,我们用cargo构建独立式二进制,但入口点名称与编译标志随操作系统而异——因为cargo默认针对宿主系统(你正在运行的系统)编译。对内核来说这毫无意义:运行在 Windows 之上的内核算什么内核?我们需要的,是面向一个明确定义的目标系统(target system)编译。

安装 Rust Nightly

Rust 有三个发布通道:stable、beta、nightly。构建操作系统需要一些仅 nightly 提供的实验性功能,因此必须安装 nightly 版本。

推荐使用 [rustup] 管理 Rust 安装:它可以并排安装 nightly、beta、stable 编译器,并简化升级。有两种方式让当前目录使用 nightly:

rustup override set nightly

或者在项目根目录创建内容为nightly的rust-toolchain文件。可通过rustc --version验证:版本号末尾应包含-nightly。

nightly 编译器允许通过源码顶部的特性标签(feature flag)开启实验功能,例如在main.rs顶部写#![feature(asm)]即可启用实验性的内联汇编asm!宏。需要强调的是,这些实验特性完全不稳定,未来版本可能随时修改或移除,因此只应在绝对必要时使用。

自定义目标平台规范(Target Specification)

cargo通过--target参数支持不同的目标系统,目标由目标三元组(target triple)描述:CPU 架构、厂商、操作系统与 ABI。例如x86_64-unknown-linux-gnu表示 x86_64 CPU、厂商未知、Linux 系统、GNU ABI。Rust 支持大量目标三元组,如 Android 的arm-linux-androideabi、WebAssembly 的wasm32-unknown-unknown。

但我们的目标系统需要特殊配置(例如没有底层操作系统),现成的目标三元组都不满足。幸运的是,Rust 允许通过 JSON 文件自定义目标。作为参考,描述x86_64-unknown-linux-gnu的 JSON 长这样:

{ "llvm-target": "x86_64-unknown-linux-gnu", "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": "linux", "executables": true, "linker-flavor": "gcc", "pre-link-args": ["-m64"], "morestack": false }

字段分三类:大部分是 LLVM 生成该平台代码所必需的(如data-layout定义了各种整数、浮点数与指针类型的大小);有些供 Rust 条件编译使用(如target-pointer-width);还有些定义 crate 如何构建(如pre-link-args指定传给链接器的参数)。

内核同样面向 x86_64,因此规范与上面非常相似。创建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 }

注意:llvm-target与os都改成了none,因为我们运行在裸机上。接着逐项添加针对内核构建的配置:

"linker-flavor": "ld.lld", "linker": "rust-lld",

平台默认链接器可能不支持目标平台,这里改用随 Rust 一起分发的跨平台链接器LLD。

"panic-strategy": "abort",

该设置声明目标不支持 panic 时的栈展开(stack unwinding),panic 时应直接中止。这与Cargo.toml中panic = "abort"效果相同,因此可以从 Cargo.toml 移除该选项。注意:与 Cargo.toml 选项不同,这个目标级选项在稍后重新编译core库时同样生效——即使你保留 Cargo.toml 选项,也务必在目标规范中包含它。

"disable-redzone": true,

我们编写内核,迟早要处理中断。为了安全地处理中断,必须禁用一种叫红区(red zone)的栈指针优化,否则会导致栈损坏。红区是 System V ABI 允许函数在不调整栈指针的情况下临时使用的栈帧下方 128 字节区域;一旦异常或硬件中断发生时 CPU 与异常处理程序覆盖了这块数据,被中断的函数将无法正确恢复执行,可能引发极难排查的诡异 bug。详细原理见仓库中的 《禁用红区》短文。

"features": "-mmx,-sse,+soft-float",

features字段启用/禁用目标 CPU 特性:前缀-禁用,前缀+启用。注意各标志之间不能有空格,否则 LLVM 无法解析该字符串。

mmx与sse决定单指令多数据(SIMD)指令的支持程度。SIMD 虽能显著加速程序,但大型 SIMD 寄存器在内核中会引发性能问题:内核必须在继续执行被中断的程序前恢复所有寄存器,这意味着每次系统调用或硬件中断都要把完整 SIMD 状态(512~1600 字节)保存到主内存。由于中断可能非常频繁,这些额外存取会严重损害性能。因此我们对内核禁用 SIMD(注意:不针对运行在内核之上的应用程序)。详细背景可阅读仓库中的 《禁用 SIMD》短文。

禁用 SIMD 带来一个新问题:x86_64 上浮点运算默认依赖 SIMD 寄存器。解决办法是启用soft-float特性,它用基于普通整数的软件函数模拟全部浮点运算。

完整的目标规范

把所有配置项整合起来,最终的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" }

编译内核:入口点与core库的缺失

针对新目标编译时,因为ld.lld链接器风格指示 LLVM 以-flavor gnu方式编译,会遵循 Linux 约定——需要名为_start的入口点(正如上一篇所述)。src/main.rs的最小骨架:

// src/main.rs #![no_std] // 不链接 Rust 标准库 #![no_main] // 禁用所有 Rust 层级的入口点 use core::panic::PanicInfo; /// 这个函数将在 panic 时被调用 #[panic_handler] fn panic(_info: &PanicInfo) -> ! { loop {} } #[no_mangle] // 不重整函数名 pub extern "C" fn _start() -> ! { // 链接器默认寻找名为 `_start` 的函数,因此这就是入口点 loop {} }

无论宿主操作系统是什么,入口点都必须叫_start。现在把 JSON 文件名作为--target传入编译:

> cargo build --target x86_64-blog_os.json error[E0463]: can't find crate for `core`

编译失败了!错误说明 Rust 编译器找不到core库。core包含Result、Option、迭代器等基础类型,会被隐式链接到所有no_stdcrate。

原因在于:core库是随 Rust 编译器以预编译形式分发的,只对受支持的主机三元组(如x86_64-unknown-linux-gnu)有效,对我们的自定义目标无效。要为其他目标编译代码,必须先为这些目标重新编译core。

build-std:按需重编译标准库

cargo 的build-std特性恰好解决此问题:它允许按需重编译core及其他标准库 crate,而不是使用随 Rust 安装分发的预编译版本。该特性很新、尚未完工,被标记为 "unstable",仅 nightly Rust 可用。

启用方式是在.cargo/config.toml(.cargo目录与src同级)写入:

# in .cargo/config.toml [unstable] build-std = ["core", "compiler_builtins"]

这指示 cargo 重编译core与compiler_builtins——后者是core的依赖,必不可少。重编译需要 Rust 源码,安装方式:

rustup component add rust-src

注意:unstable.build-std配置键要求至少 2020-07-15 之后的 Rust nightly 版本。

配置好unstable.build-std并安装rust-src组件后,重新构建:

> cargo build --target x86_64-blog_os.json Compiling core v0.0.0 (/…/rust/src/libcore) Compiling rustc-std-workspace-core v1.99.0 (/…/rust/src/tools/rustc-std-workspace-core) Compiling compiler_builtins v0.1.32 Compiling blog_os v0.1.0 (/…/blog_os) Finished dev [unoptimized + debuginfo] target(s) in 0.29 secs

可以看到cargo build现在为自定义目标重编译了core、rustc-std-workspace-core(compiler_builtins的依赖)与compiler_builtins三个库。

内存相关内置函数(Memory Intrinsics)

Rust 编译器假定所有系统都提供一组内建函数,大部分由刚重编译的compiler_builtins提供。但其中一些内存相关函数默认未启用,因为它们通常由系统 C 库提供:

  • memset:把内存块中所有字节设为给定值;
  • memcpy:把一个内存块复制到另一个;
  • memcmp:比较两个内存块。

虽然编译当前内核还用不到它们,但只要代码一复杂起来(例如复制结构体)就会需要。由于无法链接宿主操作系统的 C 库,必须另寻出路。

一个直观方案是自行实现这些函数并加#[no_mangle]属性(避免编译期自动改名)。但这很危险:底层函数任何微小的错误都可能引发未定义行为。例如用for循环实现memcpy可能造成无限递归——for循环会隐式调用IntoIterator::into_itertrait 方法,而它可能再次调用memcpy。因此复用现成的、经过良好测试的实现才是正解。

幸运的是compiler_builtins已包含全部所需实现,只是默认禁用,以免与 C 库实现冲突。启用方式是设置 cargo 的build-std-features标志为["compiler-builtins-mem"]。它既可像build-std一样以-Z标志传参,也可配置在.cargo/config.toml的unstable表中。因为我们始终需要它,配置文件方式更合理:

# in .cargo/config.toml [unstable] build-std-features = ["compiler-builtins-mem"] build-std = ["core", "compiler_builtins"]

(compiler-builtins-mem特性支持是近期才加入的,因此至少需要 2020-09-30 之后的 Rust nightly 版本。)

该标志在幕后为compiler_builtinscrate 启用了mem特性,效果是让其中的memcpy等实现带上#[no_mangle]属性,从而对链接器可见。经过这一步,内核拥有了编译器要求的全部函数实现,即使代码变复杂也能继续编译。

设置默认编译目标

每次cargo build都传--target很繁琐,可以在.cargo/config.toml中覆盖默认目标:

# in .cargo/config.toml [build] target = "x86_64-blog_os.json"

有了这份配置,当没有显式传入--target时,cargo 会自动使用x86_64-blog_os.json——现在一条cargo build就能为裸机编译内核。

向屏幕输出:VGA 文本缓冲区

现在内核可以编译了,但_start入口点(将来会被引导程序调用)仍是空循环。是时候让它输出点什么了。

现阶段打印文本最直接的方式是VGA 文本缓冲区:一块映射到 VGA 硬件的特殊内存区域,其内容就是屏幕显示的内容。它通常包含 25 行、每行 80 个字符单元;每个字符单元显示一个 ASCII 字符,并带有前景色与背景色。(缓冲区精确的内存布局将在下一篇《VGA 文本模式》中讨论,那里我们会编写第一个小型驱动。)

打印 "Hello World!" 只需知道两点:缓冲区位于地址0xb8000,每个字符单元由一个 ASCII 字节加一个颜色字节组成。实现如下:

static HELLO: &[u8] = b"Hello World!"; #[no_mangle] pub extern "C" fn _start() -> ! { let vga_buffer = 0xb8000 as *mut u8; for (i, &byte) in HELLO.iter().enumerate() { unsafe { *vga_buffer.offset(i as isize * 2) = byte; *vga_buffer.offset(i as isize * 2 + 1) = 0xb; } } loop {} }

代码要点:

  1. 定义静态字节字符串HELLO;
  2. 将整数0xb8000转换为裸指针(raw pointer);
  3. 用enumerate迭代HELLO的每个字节,同时取得序号i;
  4. 在循环体内用offset偏移裸指针并解引用:偶数偏移写字符串字节,奇数偏移写颜色字节(0xb是淡青色)。

注意所有内存写入都被unsafe块包围。原因很简单:编译器无法证明我们创建的裸指针有效——它们可能指向任何地方、导致数据损坏。放进unsafe块,等于向编译器声明"我确信这些操作是合法的"。需要澄清:unsafe块并没有关闭 Rust 的安全检查,它只是额外允许你做少数几件特定的事情(如解引用裸指针)。

必须强调:这不是 Rust 推荐的做法!在unsafe块内操作裸指针极易出错——稍不留意就可能写出缓冲区末尾。因此应尽可能最小化unsafe的使用。Rust 的做法是创建安全抽象:例如设计一个封装全部不安全操作的 VGA 缓冲区类型,从外部保证"不可能做错任何事"。这样只需要最小量的unsafe代码,并能确信没有违反内存安全。下一篇文章就会创建这样一个安全的 VGA 缓冲区抽象。

运行内核:从 ELF 到可引导磁盘映像

内核已能产生可见输出,是时候运行它了。第一步,把编译好的内核与引导程序链接成可引导磁盘映像;第二步,在 QEMU 虚拟机中运行,或用 USB 在真机上启动。

创建可引导映像

如前所述,引导程序负责初始化 CPU 并加载内核。我们不自己编写引导程序(那本身就是个独立项目),而是使用bootloadercrate——它用纯 Rust 与内联汇编实现了一个基础 BIOS 引导程序,没有任何 C 依赖。在Cargo.toml添加依赖:

# in Cargo.toml [dependencies] bootloader = "0.9"

注意:本教程仅兼容bootloader v0.9。更新版本采用了不同的构建系统,照本教程操作会引发构建错误。

仅添加依赖还不够:我们需要在编译完成后把内核与引导程序链接起来,而 cargo 原生不支持构建后脚本(见相关 issue #545)。

为此,本系列提供了bootimage工具:它先编译内核与引导程序,再把二者链接成可引导磁盘映像。安装命令(建议在 cargo 项目目录之外执行):

cargo install bootimage

运行bootimage并编译引导程序需要 rustup 组件llvm-tools-preview:

rustup component add llvm-tools-preview

安装完毕后,回到项目目录执行:

> cargo bootimage

可以看到该工具先用cargo build重编译我们的内核,因此会自动拾取你做的任何修改;随后编译引导程序(可能较慢),但与所有 crate 依赖一样只编译一次并被缓存,后续构建会快得多;最后bootimage把引导程序与内核组合成可引导磁盘映像。

执行完成后,target/x86_64-blog_os/debug目录下会出现bootimage-blog_os.bin。可以把它放进虚拟机启动,或复制到 U 盘在真机上启动。(注意:这不是 CD 映像,格式不同,刻录到光盘不起作用。)

bootimage 在幕后做了什么?

bootimage工具实际执行三个步骤:

  1. 把内核编译成 ELF 文件;
  2. 把bootloader依赖编译成独立可执行文件;
  3. 把内核 ELF 文件的字节拼接到引导程序之后。

启动时,引导程序读取并解析拼接在后的 ELF 文件:将程序段映射到页表中的虚拟地址,清零.bss段,建立栈;最后读取入口点地址(即我们的_start函数)并跳转过去。

在 QEMU 中启动

在 QEMU 虚拟机中引导磁盘映像:

> qemu-system-x86_64 -drive format=raw,file=target/x86_64-blog_os/debug/bootimage-blog_os.bin warning: TCG doesn't support requested feature: CPUID.01H:ECX.vmx [bit 5]

会弹出一个独立窗口:

屏幕左上角显示青蓝色的 "Hello World!"——我们的最小内核成功运行了。

在真机上运行

也可以把映像写入 U 盘在真机启动:

> dd if=target/x86_64-blog_os/debug/bootimage-blog_os.bin of=/dev/sdX && sync

其中sdX是你的 U 盘设备名。务必仔细核对设备名,因为该设备上的所有数据都会被覆盖!写入后用特殊启动菜单或在 BIOS 配置中调整启动顺序,从 U 盘引导即可。注意:由于bootloadercrate 尚无 UEFI 支持,目前在 UEFI 机器上无法启动。

使用cargo run一键启动

为简化在 QEMU 中的运行,可给 cargo 设置runner配置键:

# in .cargo/config.toml [target.'cfg(target_os = "none")'] runner = "bootimage runner"

target.'cfg(target_os = "none")'表适用于所有目标配置文件"os"字段为"none"的目标——包括我们的x86_64-blog_os.json。runner键指定cargo run应调用的命令:成功构建后,该命令会以可执行文件路径作为第一个参数被调用。

bootimage runner专为runner角色设计:它把给定可执行文件与项目的引导程序依赖链接,然后启动 QEMU。至此,cargo run一条命令即可编译内核并在 QEMU 中启动。

下一步

下一篇将深入探讨 VGA 文本缓冲区,为它编写安全的 Rust 接口,并添加println!宏支持。在继续之前,可以回顾仓库中的相关支撑材料:

  • 第一篇:独立式 Rust 可执行程序——no_std、#[panic_handler]、_start入口点与链接器错误处理;
  • 禁用红区详解——为什么内核必须关闭红区优化;
  • 禁用 SIMD 详解——MMX/SSE/AVX 与soft-float的取舍;
  • 本系列整体说明见仓库根目录 README.md,每篇教程的完整源码对应post-01、post-02等独立分支。
  • 文档
  • 教程
  • 技术博客
  • 操作系统

【免费下载链接】blog_os

Writing an OS in Rust

项目地址:https://gitcode.com/GitHub_Trending/bl/blog_os
点击查看免费下载

相关推荐

上一篇:PDFMathTranslate项目中的字体下载问题分析与解决方案
下一篇:一张消费级4090跑MistoLine?这份极限"抠门"的量化与显存优化指南请收好

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 1:40:42

STM32CubeMX从入门到实战:配置、点灯、SPI Flash与FreeRTOS

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/2 1:40:09

EMC预测试:从电流路径建模到Layout级EMI扼杀

1. 为什么“预测试”不是加个探头测一测那么简单?EMC预测试这个词,现在被很多工程师挂在嘴边,但真正把它当成本职工作来做的团队,不到三成。我见过太多项目——原理图刚定稿,PCB还在画,大家就忙着讨论“等板…

作者头像 李华
网站建设 2026/10/2 1:38:51

编译原理课设实战:C++手写词法分析器与LL(1)语法分析器

简介:这是一份面向计算机专业学生与编译器爱好者的编译原理前端实践资源,聚焦词法分析与语法分析两大核心模块的C实现。资源包共9个文件,以cpp源码、txt文法与token说明、exe可执行程序及md说明文档为主,压缩包约937KB&#xff0c…

作者头像 李华
网站建设 2026/10/2 1:38:14

Linux 命令详解:mktemp 安全创建临时文件(linux-command 速查手册)

文档教程 【免费下载链接】linux-command Linux命令大全搜索工具,内容包含Linux命令手册、详解、学习、搜集。https://git.io/linux 项目地址: https://gitcode.com/GitHub_Trending/linux/linux-command 点击查看 免费下载 本篇以 command/mktemp.md 为…

作者头像 李华
网站建设 2026/10/2 1:38:09

PL0编译器功能扩充:重建教学型编译器的可扩展骨架

简介:本资源是一份面向计算机专业高年级学生与编译原理初学者的PL/0编译器功能扩充实验报告,聚焦事业编考试中常涉及的系统底层与语言实现能力考查场景。文档完整呈现了在经典教学编译器PL/0基础上扩展整型一维数组、IF-THEN-ELSE条件分支及REPEAT循环语…

作者头像 李华
网站建设 2026/10/2 1:38:02

YOLO自动瞄准助手实战:从目标检测到云台PID闭环

简介:基于YOLO的自动瞄准助手C项目源码,将实时目标检测与输入模拟结合,面向图像识别、机器学习及AI应用开发方向的读者。项目演示了YOLO以单神经网络完成从图像像素到边界框坐标与类别概率的映射,让目标定位在桌面端实现成为可能。…

作者头像 李华