自研操作系统怎么验证?从最小内核到虚拟机实测指南 📅 发布时间:2026/8/30 2:28:22 👁 浏览次数: 看到“爆肝自研内核、自研引擎、自研架构的操作系统”这类标题很多人的第一反应是“牛”第二反应是“真的假的”。我在实际开发中见过不少自称自研系统的项目有的确实是从引导扇区开始写的有的只是把开源内核换了个名字所谓自研架构也只是把 Linux 内核源码目录改了改。我想解决一个很实际的问题当一个项目说自己“自研操作系统”时应该从哪些角度判断它的真实水平以及如果你自己也想动手做一个小内核最早能跑起来的路径是什么。如果你只是想看热闹只需要记住一个判断自研操作系统不是靠关键词堆出来的要看你能否在虚拟机里完成引导、打印字符、处理中断、管理内存。这篇文章适合对操作系统原理感兴趣、想从零开始写内核、或者平时需要评估某个系统方案的开发者。我会把“内核”“引擎”“架构”三个词拆开讲再用一个最小内核示例带你跑通第一轮最后给出验证自制系统真实水平的实测方法。1. 先分清“内核”“引擎”“架构”这三件事再聊自研“自研内核、自研引擎、自研架构”三个词叠在一起听起来像是一个系统从底层到顶层全部自己写。但操作系统领域里这三个词并不是同一层的东西混在一起说最容易模糊边界。内核是操作系统的核心负责 CPU 调度、内存管理、进程间通信、文件系统、设备驱动、网络协议栈等。任何称得上操作系统的程序都必须有一个内核在硬件之上运行。自研内核意味着这些核心模块不是直接从 Linux、FreeBSD 或其他开源内核复制来的而是自己设计、自己实现。引擎这个词在操作系统语境里并不统一。它可能指图形渲染引擎比如窗口合成、字体渲染、OpenGL/Vulkan 驱动也可能指应用运行时引擎比如 WebAssembly、JavaScript、Python 解释器。如果你看到“自研引擎”要先问它到底是什么引擎。一个系统可以没有自研引擎但不影响它成为操作系统也可以有很好的自研图形引擎但底层内核是开源的。引擎自研和操作系统自研是两件事。架构通常指系统模块的划分方式。常见的操作系统架构包括宏内核、微内核、混合内核、外核等。如果你的系统选择微内核把驱动和文件系统放到用户态那这是自研架构如果你只是把 Linux 的宏内核拆了几个目录核心调度算法还是原来的就很难说架构是自研的。1.1 三者的常见混淆点实际评估项目时我经常看到三类混淆。第一类是把“用了开源内核”说成“自研内核”。比如项目基于某个开源内核只做了裁剪和配置对外却强调“内核完全自研”。这种情况在启动日志里通常能看到原版内核的版本号或者模块目录里保留着大量原始文件。第二类是把“自研图形引擎”等同于“自研操作系统”。图形引擎解决的是画面的生成和呈现它可以在普通操作系统之上运行也可以做成内核态组件。自研一个图形引擎说明团队在图形领域有积累但操作系统还需要处理 CPU、内存、进程、文件系统这些核心模块仍然不能缺席。第三类是把“改了目录结构”说成“自研架构”。架构是设计出来的不是整理出来的。如果一个系统模块之间的依赖关系、数据流向、接口边界都是从其他项目继承来的只是改了文件名那它仍然是别人架构的一种复刻。1.2 用一张表快速区分概念通常指什么自研的难点常见误区内核CPU、内存、进程、文件系统、驱动核心中断、调度、内存管理、并发稳定性把“基于某内核”说成“自研内核”引擎图形渲染、运行时、浏览器、GUI 相关性能、帧率、跨硬件支持、生态自研了绘图引擎就说成自研系统架构模块划分、依赖关系、内核/用户态边界设计权衡、性能与安全、可维护性改目录结构就号称自研架构这里最值得关注的是一个真正的自研操作系统至少要有一个自研内核引擎和架构可以是自研的加分项但不能替代内核。项目标题里把三者并列本质上是强调“底层到上层都是自己的”。要验证这三点不能只看文档要从引导开始一步步检查。2. 判断一个操作系统是否“真自研”可以从引导层到应用层拆成四层看对于一个宣称“自研内核、自研引擎、自研架构”的项目我的经验是不要急着看架构图也不要先跑分。先看它能不能在虚拟机或裸机上完成引导。很多项目所谓“可运行”其实是在 Linux 用户态跑一个模拟器真正的内核根本没有接触硬件。区分这一点非常关键。2.1 引导层能不能从零启动自研系统的第一道门槛是引导。普通程序由操作系统加载内核则必须由固件、引导程序或约定格式加载。常见的路径有三条BIOS 下从启动扇区读 512 字节自己写引导代码。UEFI 下加载 EFI 应用从固件直接进入内核。通过 GRUB 等引导器按照 Multiboot 规范加载 ELF 文件。如果一个系统能通过 QEMU 的-kernel参数启动说明至少它的 ELF 格式和入口地址是符合规范的。如果只能在某个现成操作系统里“安装后运行”那它可能只是一个普通应用程序不是操作系统。在这一层可以做的测试很简单新建一个虚拟机不挂载任何现有系统镜像只挂载这个系统的启动镜像看能不能进入自己的内核入口。如果连这一步都走不通后面所有“自研”都无从谈起。2.2 内核层中断、内存、调度是否真实存在引导成功之后下一个要看的是内核自己做了什么。一个最小内核至少要能做这些事情初始化 GDT/IDT处理中断。初始化内存管理至少能管理物理内存页。初始化一个最简单的进程或任务模型。提供某种输出机制比如串口或 VGA 文本模式。判断标准不是“有没有这些模块”而是“出问题时系统怎么表现”。比如触发一个除零异常如果你的内核能打印异常信息并挂起说明 IDT 和异常处理是你自己写的如果直接黑屏重启说明中断处理链路还不完整。再比如向一个非法地址写数据如果内核能报出 page fault 的地址说明页表管理起了作用如果什么都不提示说明内存管理可能只是空壳。2.3 驱动层代码来源和硬件覆盖范围操作系统要能用到实际硬件必须有设备驱动。自研驱动和自研内核是两回事很多项目内核是自研的但驱动直接使用开源代码或厂商二进制。这里要关注的不是“不能使用开源驱动”而是项目是否如实说明。如果宣称“完全自研”驱动却不能解释来源那就存在较大水分。驱动层还有一个容易踩的坑支持某一种显卡或网卡不代表支持所有硬件。我在虚拟机上测试内核时最常遇到的就是虚拟显卡和真实显卡差异很大。一个系统如果只在某一种固定配置下能跑说明它的驱动层覆盖范围有限这在早期 Demo 阶段可以接受但离生产级系统还有距离。2.4 应用层工具链和生态是否成立操作系统的最终价值要由应用软件体现。如果应用层只能运行项目方自己写的一套脚本那它更像是一个演示环境。自研系统如果提供标准 C 库、系统调用接口或二进制格式才能让外部开发者参与。这里要看的是开发工具链编译器、汇编器、链接器是不是从开源工具链获得系统接口是不是和 POSIX 兼容如果没有这些自研系统就只能在闭环生态里运行很难扩大使用范围。这一层的结论是宣传中的“自研”要分层验证。内核层看启动和中断驱动层看代码来源和硬件支持应用层看工具链和生态。四层都满足才是完整的自研系统只满足第一层那只是一个自研引导程序。3. 自己动手写一个最小内核环境准备与最小代码骨架如果你看完前面部分想自己验证“从零启动到底难不难”最好的方法是亲手写一个几十行代码的最小内核跑在虚拟机上。这个最小内核不会实现完整操作系统但它能让你理解引导、入口、输出和中断的基本链路。我建议环境尽量简单Linux 或 macOS 都可以Windows 上用 WSL 也行关键是安装 QEMU 和交叉编译器。3.1 环境准备QEMU 加交叉编译器我习惯用 QEMU 做测试。QEMU 不需要真机重启能快速验证启动流程还能方便地输出串口日志。交叉编译器方面可以安装i686-elf-gcc或类似工具链。普通系统的gcc不一定能生成独立的 freestanding 内核因为它默认依赖宿主机的启动文件和 C 库。所以内核编译时一定要用-ffreestanding -nostdlib -no-pie这些参数。如果不方便安装交叉编译器也可以先用系统自带 clang 配合--targeti686-none-elf。这里给的示例以比较常见的i686-elf-gcc为例整体步骤是写一个汇编入口文件、一个 C 入口函数、一个链接脚本最后用 QEMU 启动。3.2 引导入口从 Multiboot 开始我选择用 Multiboot 规范因为 QEMU 的-kernel参数可以直接加载符合 Multiboot 的 ELF 文件跳过了自己写引导扇区的麻烦。先创建一个boot.s.section .multiboot .align 4 .long 0x1BADB002 .long 0x03 .long -(0x1BADB002 0x03) .section .text .global _start _start: mov $kernel_stack, %esp call kmain cli hlt loop: jmp loop .section .bss .align 16 kernel_stack: .skip 16384这段代码做了三件事声明 Multiboot 头让引导器能识别设置栈顶调用 C 函数kmain。如果没有这个.bss栈C 函数执行时无法压栈系统会直接崩溃。这里最容易踩坑的就是栈很多最小内核跑不起来都是因为忘了给 C 代码准备正确的栈。3.3 C 入口、链接脚本和运行命令然后写一个kernel.c在 VGA 文本模式打印一行字符然后死循环void kmain(void) { const char *msg Hello, tiny kernel!; char *vga (char *)0xB8000; int i 0; while (msg[i]) { vga[i * 2] msg[i]; vga[i * 2 1] 0x07; i; } while (1) {} }这段代码不依赖任何 C 库直接把字符写到地址0xB8000这是 VGA 文本模式的显存区域。Multiboot 引导时GRUB 已经进入保护模式所以这段代码可以直接访问 32 位内存地址。如果完全从软盘引导就要自己切换保护模式那是另一个更复杂的工程。链接脚本linker.ld的一个简单写法ENTRY(_start) SECTIONS { . 1M; .text : { *(.multiboot) *(.text) } .data : { *(.data) } .bss : { *(.bss) } }编译和运行i686-elf-gcc -c -ffreestanding -nostdlib -no-pie boot.s -o boot.o i686-elf-gcc -c -ffreestanding -nostdlib -no-pie kernel.c -o kernel.o i686-elf-gcc -T linker.ld -o kernel.elf -ffreestanding -nostdlib -no-pie boot.o kernel.o qemu-system-i386 -kernel kernel.elf如果顺利QEMU 窗口会显示一行绿色文字。这个结果说明从引导、入口、栈初始化到文本输出已经跑通。但这还不能叫操作系统因为它没有中断、内存管理、进程调度。这就是最小可运行内核。很多项目就是从这段代码开始逐步加入 GDT/IDT、分页、内存分配器、定时器、任务切换最后才形成真正的自研内核。注意以上命令中的编译器和汇编器版本可能因系统不同而有差异。如果你用的是 64 位宿主机的默认 gcc目标格式可能是elf64-x86-64和 Multiboot 头不匹配的情况下 QEMU 可能无法识别。建议优先使用 i686-elf 工具链。4. 自研系统最容易翻车的四个细节前面写过最小内核你会发现在虚拟机里打印一行字并不难。问题在于从一个能打印字符的 Demo到一套能稳定运行、可扩展的自研系统中间隔了太多细节。下面四个问题是我在测试和阅读自制系统项目时经常遇到的。4.1 启动成功不等于内核完成我见过不少项目宣传“成功启动”点进去看却发现只是在 QEMU 里打印了一行 Hello World然后没有中断、没有输入、没有出错处理。这种项目作为学习 Demo 是合格的但作为“自研操作系统”来说离完成还有很长距离。真正要完成内核至少要能处理异常和中断。我建议你写一个除零指令或者故意触发 page fault看系统能否输出异常向量。不能输出异常信息的内核后续很难调试。从启动到内测还有一个很容易忽略的点重启稳定性。连续启动几十次每次都能进入同一个入口、输出同样内容才算基本稳定。启动过程中即使一个小错误也可能只在某次启动时触发出现随机重启或挂起。4.2 自研架构很容易变成“换皮 Linux”“基于 Linux 改改”和“自研 Linux 兼容层”是完全不同的。有些项目把 Linux 内核源码拉下来修改了系统名称、版本号然后放出一些截图就声称自研架构。遇到这种项目最直接的验证方式是查看内核启动日志里的版本标识以及查看模块来源。如果项目确实自己维护了大量代码也要看这些代码是在内核态还是用户态。只在用户态加了一套桌面环境本质上还是个 Linux 发行版不是自研操作系统。当然这也不是贬义Linux 发行版同样有很高价值但应该准确描述自己的定位而不是模糊成自研内核。4.3 硬件兼容性往往比功能列表更复杂自制系统最常见的运行环境是 QEMU、VirtualBox、VMware 这类虚拟机。虚拟机的硬件相对固定驱动好写但现实硬件千差万别。一个系统在虚拟机能跑到了真实笔记本上可能连键盘中断都没有。我常用的一种判断方式是把同一个镜像分别放到 QEMU 和 VirtualBox 里测试再看有没有针对真实显卡、声卡、网卡的驱动代码。没有驱动代码就说明硬件支持范围非常有限。这里也经常会出现热搜词里那种虚拟机报错比如“客户机操作系统已禁用 CPU。请关闭或重置虚拟机”或者“VirtualBox 未能启动虚拟电脑可能缺少操作系统”。这类问题绝大多数不是系统内核本身的问题而是虚拟机设置或启动介质没选对。常见排查顺序是先确认 CPU 虚拟化是否开启再确认启动顺序是否正确最后看镜像是否包含引导记录。4.4 “引擎”这个词经常被拿来包装非核心能力如果一个系统声称自研了渲染引擎你要看它渲染的是什么。是文字终端、2D 图形窗口还是完整的 GUI 合成器自研一个能在帧缓冲上画矩形的代码和自研一个支持窗口管理、输入事件分发、多应用调度的图形引擎工作量差两三个数量级。同样自研浏览器引擎更是长期工程。我看到过一个还算可靠的经验如果一个项目把所有热词都堆在标题上但仓库里没有对应的日志、测试用例、架构文档那大概率是概念设计阶段。反过来说真正在做自研系统的人更常说的是“目前只实现了内存管理”或“调度器还得重写”因为他们知道自己卡在哪里。5. 用四组实测方法判断一个自研系统的真实水平前面都是定性判断真要验证一个自研系统的水平最好设计几组可以重复的测试。下面这四组是我评估自制内核时优先跑的不需要很复杂但能过滤掉大部分概念项目。5.1 冷启动与重启稳定性第一组测试看启动。在虚拟机中把系统启动 20 次记录每次是否都能进入内核主函数并输出标志信息。如果系统支持重启指令再测试 restart 后是否还能回到干净环境。这个测试可以暴露早期初始化的随机问题比如栈没有清零、内存分配器重复初始化、中断没有禁用干净。如果启动过程不稳定大部分原因集中在这几个地方入口地址错误、栈指针初始化不一致、BSS 段未清零、多核启动逻辑没处理好。我一般会在启动早期打印一个递增计数器用串口输出快速定位是在哪一阶段卡住。5.2 中断与时钟响应第二组测试看中断。在最小内核中打开时钟中断设置一个定时器每毫秒触发一次中断在中断处理函数中递增计数。如果计数稳定说明 IDT、可编程中断控制器和异常处理链路基本可用。如果系统跑一段时间就死机优先检查中断处理结束前是否发送了 EOI以及中断处理函数是否使用了不安全的 C 库调用。再进一步可以测试键盘中断。在虚拟机上模拟按键看内核能否读取到键盘扫描码。这一步能验证用户的输入链路也是很多自研系统从“能看”到“能用”的关键转折。5.3 内存越界是否可诊断第三组测试看内存管理。写一个内存分配器尝试申请 4KB、1MB、64MB 三种大小的页观察返回值和对齐方式。然后故意访问一个非法地址看内核能给出什么信息。好的内核会打印类似page fault at 0x12345678并挂起等待调试差的内核会直接重启或无限循环。这一项最能看出内存管理是否真实存在。很多 Demo 内核对内存分配是“直接返回一个固定地址”不做页表管理。在单任务环境下似乎没问题一旦切换任务地址空间隔离完全没有任何一个指针越界都会破坏整个系统。5.4 能否脱离厂商工具链独立开发第四组测试看开发链。一个自研系统如果只能通过项目方提供的专属 IDE 或专用编译器构建外人很难判断它是系统本身的能力还是工具包装得好。反过来如果可以用通用编译器、链接器和 Makefile 构建同时提供系统调用接口这个系统就具备被第三方评估的基础。我建议观察它的 SDK 和文档有没有说明系统调用号有没有描述进程如何创建、终止有没有提供交叉编译最小案例这些比宣传文案更有说服力。5.5 宣传话术与实测对照宣传文案建议实测方式通过标准自研启动流程用 QEMU 冷启动关掉默认引导能打印内核日志自研内存管理申请大页、故意越界访问有 page fault 异常输出自研任务调度创建 3 个线程观察调度顺序和切换有调度日志无死锁自研图形引擎渲染动态画面切换窗口帧率稳定输入事件正确自研工具链用通用交叉编译器构建不依赖封闭 IDE这组测试跑完一个自研系统的水平基本就清楚了。如果连第一、三组都过不了那它的“自研”就更偏向学习项目或概念展示。6. 从零到可演示内核建议的学习路线与里程碑最后聊一个很多读者真正关心的问题我也想做一个自己的操作系统该从哪开始我的建议是不要一开始就想着做“自研引擎”“自研架构”先把最小内核的每一步走扎实。6.1 先从裸机编程开始再进入内核领域建议先学习操作系统原理和计算机组成原理至少理解 CPU 如何执行指令、内存如何寻址、中断如何产生。然后跟着一些经典的教程做裸机编程比如用汇编在屏幕上打印字符、读取键盘输入、实现简单的引导扇区。这些练习能建立“没有操作系统时程序如何运行”的感觉是写内核的前提。推荐几个方向xv6、MIT 6.S081、CSAPP 的异常控制流部分以及网上一些操作系统开发博客。不要直接去看大型内核源码容易迷失。先做最小环节比如清空屏幕、打开 A20、加载 GDT、进入保护模式。6.2 按里程碑推进而不是按功能堆叠一个可持续的内核项目应该有阶段目标。我建议按下面顺序引导并打印字符串。初始化 GDT/IDT能处理异常和时钟中断。实现简单的物理内存页分配器。实现内核堆分配器。开启分页建立最简单的页表。实现多任务切换让两个任务轮流打印。增加一个最简单的系统调用接口。每完成一个里程碑就提交一次代码并写清楚如何测试。不要在同一时间既做音视频驱动又做网络协议栈。内核的复杂度是叠加出来的前面的坑没填平后面只会越来越难查。6.3 项目结构要尽早按模块拆分即使最小内核也建议按这种方式组织目录boot/ 启动代码 kernel/ 内核主逻辑比如调度、内存、中断 drivers/ 设备驱动比如键盘、串口、VGA lib/ 内核使用的公共库 arch/ CPU 架构相关代码比如 x86 的 GDT/IDT Makefile 构建脚本这样拆分的核心原因是你迟早要面对“驱动、内核、架构”分离的问题。如果所有代码都堆在几个文件里后面每加一个功能都要全量回归测试排错成本会迅速上升。一开始就维护一个清晰的模块边界反而是最省事的。6.4 什么时候才算“入门级自研操作系统”我的判断标准有三个能独立引导内存管理不是摆设任务调度器能反复切换不崩溃。达到这三个标准你完全可以把自己的项目称作“自制操作系统”但这离“自研架构”“自研引擎”还有距离。如果日后做大了你可以说自己的系统采用微内核架构或者自研了图形合成引擎但每一项都要有代码和测试来支撑。我自己的经验是从零到能稳定完成上述三个里程碑通常需要几个月时间期间会踩很多坑。最大的坑往往不是“不会写”而是“不知道怎么写测试”。如果你想长期维护这个项目建议从一开始就把串口日志、自动化启动测试、崩溃时打印堆栈都做进去。没有这些基础设施后面的开发会非常痛苦。所以再看到“爆肝自研内核、自研引擎、自研架构的操作系统”这类标题时我建议你先别急着转发或质疑直接按这篇文章里的方法把它丢进虚拟机里跑一遍能不能从零启动能不能处理中断能不能稳定打印日志。把这些现象记录下来比争论“是不是自研”更有价值。如果只是自己学习也请记住先写一个能打印字符的最小内核再谈引擎和架构。跑通一行字是所有宏大设计的第一步。