ARTICLE DETAIL

资讯详情

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

Orange‘s操作系统实现光盘解析:从引导扇区到内核运行

Orange‘s操作系统实现光盘解析:从引导扇区到内核运行 简介《Oranges一个操作系统的实现》随书光盘面向操作系统初学者与爱好者配合原书讲解进程管理、内存管理、中断处理、文件系统等核心概念适合通过源码阅读与实验动手实践的读者使用。资源共2347个文件压缩包大小12.86MB主要包含858个C语言源文件、576个头文件、382个汇编文件、79个makefile构建脚本以及磁盘映像、Bochs虚拟机配置、启动引导相关文件等覆盖从引导、内核初始化到文件系统实现的完整实验环境。光盘中的源码和配置可直接用于搭建操作系统实验平台读者可在模拟器中编译、运行并调试一个简单内核观察进程调度与内存分配的实际效果。已有397人学习对希望深入理解操作系统底层机制并积累排错经验的学习者而言这份材料具有很高的实用价值。1. 项目概述1.1 这本书和随书光盘到底能让你学会什么我最早接触《Oranges一个操作系统的实现》这本书是在读本科那会儿。当时被网上各种“手写操作系统”的帖子勾得心痒又不太敢直接啃《操作系统设计与实现》那种大部头就在学长推荐下买了这本于渊的书。说实话光看书名和目录我以为它只是一本理论书直到我打开随书光盘才意识到这其实是“半只脚已经踩进内核开发大门”的完整套件。这本随书光盘的价值不是让你读一遍书就觉得自己会了而是给你一套可以直接编译、运行、调试的源码环境。它覆盖了一个微型操作系统最核心的骨架引导扇区、实模式到保护模式的切换、内存分页、中断处理、任务切换、系统调用甚至还有一个极简的文件系统。你不需要像写Linux内核那样面对几百万行代码你只需要跟着书里一章一章把代码敲进去或者偷懒一点直接改光盘里的源码然后看着一个真正属于你自己的操作系统在QEMU或者Bochs里跑起来。它的目标读者非常明确学过C语言和一点汇编又想搞懂操作系统底层原理的计算机专业学生、刚入行的嵌入式开发者以及那些被Linux内核源码劝退、想换个入口研究内核机制的自学党。如果你属于这几类人这本书和它的光盘是你绕不开的一站。1.2 先说结论光盘里的东西能干什么随书光盘里放的不是什么“资料合集”而是一套以章节为单位的完整工程。每一章对应书中一个阶段的操作系统形态从最初的光盘引导、打印字符到后面支持分页、任务调度和多进程代码都是渐进演进的。你可以在光盘的对应目录里找到该章节的完整源码、Makefile、编译脚本以及一些调试用的工具。当年我拿到光盘后的第一个冲动是把所有代码复制到Linux虚拟机里跑一遍结果发现事情远没有想象中顺利。要说清这张光盘到底能干什么、怎么才能跑起来、中途会踩哪些坑就得把它从里到外拆开看。这篇文章我就是来带你干这件事的。2. 随书光盘内容全解析2.1 光盘目录结构每一章都是一个可运行的内核光盘的目录结构其实很规整基本和书的章节一一对应。比如你在光盘里会看到类似 chapter1、chapter2、chapter3 这样的目录每个目录下一般就是该章对应的源码工程。以我手上这张光盘新版为例里面有tools 目录存放编译和运行需要的工具比如Bochs模拟器、NASM汇编器、dd工具等。注意工具版本都比较老后文会说怎么处理版本兼容问题。chapter 相关目录每章一个目录目录里通常有 boot/、 include/、 kernel/、 lib/ 等子目录分别对应引导代码、头文件、内核核心代码、公共库代码。每章的源码里有 Makefile 和 build 脚本用于把源码编译成软盘镜像Image文件或者硬盘镜像。有个细节值得注意早期的章节比如第2章实模式下的引导程序代码量很少可能就几百行汇编加一点C到了后面章节源码会从单文件变成一个多目录工程包含中断描述符表、页目录、任务状态段这些核心数据结构。这种从零到一、逐层叠加的组织方式是我觉得这本书最友好的地方——它让你看到一个内核是怎么从一行汇编长成一个多模块系统的。2.2 光盘自带的Bochs和工具链为什么一定要用虚拟机跑书里推荐用Bochs模拟器来跑编译出的镜像而不是直接在真机上启动。光盘里也附带了一个Bochs的配置文件bochsrc以及相应版本的Bochs可执行程序。为什么非要用Bochs因为这本书的代码在早期阶段根本不支持现代UEFI启动它写的是经典的BIOS中断调用来读取磁盘、设置显存这在如今的主流电脑上基本没法直接引导。Bochs相当于一个带调试器的“裸机模拟器”你不仅能让操作系统跑起来还能在任意内存地址下断点、查看寄存器、单步执行——这对理解内核代码的执行过程帮助极大。另一个原因是本书代码生成的镜像是一个软盘镜像通过dd命令写入到一个1.44MB的img文件里。现代机器上已经没有软驱了而Bochs可以模拟软驱并从镜像文件启动这就绕开了真机启动的硬件兼容问题。如果你手头的光盘附带的Bochs版本太老比如只支持32位Windows的exe在64位系统上可能会弹“无法运行”或者“不是有效的应用程序”之类的错误。我建议直接去Bochs官网下载新版或者用包管理器装一个。提示Bochs和QEMU在这本书的用途上不完全一样。QEMU启动快、操作简单适合快速看效果Bochs内置调试器适合研究代码细节。两者可以搭配使用。3. 在真实环境中搭建编译运行环境3.1 选Linux环境还是Windows环境我自己一开始是在Windows虚拟机里折腾的后来发现问题太多干脆换到Linux环境才顺畅起来。原因有三光盘里的Makefile和原始工具链几乎都是面向Linux的虽然也能在Windows下用MinGW跑但交叉编译和挂载镜像的体验差很多。Linux下生成软盘镜像非常方便一条dd命令就能把引导扇区写到镜像里Windows下还要找WinImage之类的工具。Bochs的调试器版本在Linux下有更丰富的命令行接口比如bdbBochs Debugger调试多任务切换时非常有用。如果你只有Windows也并不是不行。只是你需要额外做三件事装一个Linux虚拟机推荐Ubuntu 20.04或更新版本、再在虚拟机里装好NASM、GCC、make、binutils以及Bochs和QEMU。这条路线最省心。3.2 一键编译手写Makefile还是直接用光盘的光盘中每章都有一个Makefile理论上你只需要进入对应目录执行 make 就能生成镜像。但说实话老版本Makefile在我这边基本都会报错原因不外乎编译器版本变化导致的告警被当成错误处理-Werror。旧Makefile里用了绝对路径而你的拷贝目录不同。NASM版本太新某些指令的操作数格式处理变了。我当时的处理方式是先读懂Makefile里那几行关键命令然后自己写一个精简版。下面是我自己整理的、适合新环境使用的编译流程以书里第3章保护模式切换为例nasm -o boot.bin boot.asm # 编译引导扇区 nasm -o loader.bin loader.asm # 编译加载器 gcc -m32 -c -o kernel.o kernel.c # 编译内核C代码 ld -m elf_i386 -Ttext 0x30400 -o kernel.bin kernel.o # 链接内核 dd ifboot.bin ofa.img bs512 count1 convnotrunc # 写入引导扇区 dd ifloader.bin ofa.img bs512 seek1 count4 convnotrunc # 写入加载器 dd ifkernel.bin ofa.img bs512 seek4 count200 convnotrunc # 写入内核这里最关键的一步是ld -m elf_i386 -Ttext 0x30400它告诉链接器把内核的代码段放在 0x30400 这个内存地址这是书中加载器约定跳转的位置。如果你漏了这一步镜像就算写对了也会在运行到内核入口时直接跳飞。3.3 Bochs配置细节一看就懂的bochsrc写法Bochs启动需要一个配置文件光盘里一般有现成的但为了让新人少踩坑我贴一个简化版。保存为 bochsrc.txt然后执行bochs -f bochsrc.txt即可。megs: 32 romimage: file/usr/share/bochs/BIOS-bochs-latest vgaromimage: file/usr/share/bochs/VGABIOS-lgpl-latest floppya: 1_44a.img, statusinserted boot: a log: bochsout.txt mouse: enabled0 keyboard: typemouse, serialdisabled注意几个重点floppya那一行指定了当前目录下的 a.img 作为软盘镜像boot: a表示从软驱启动romimage和vgaromimage的路径要改成你机器上Bochs实际安装的BIOS和VGABIOS文件路径。如果你只用QEMU那更简单qemu-system-i386 -fda a.imgQEMU的好处是不需要写配置文件直接跑起来坏处是你没法方便地看到寄存器和内存变化。所以我建议调试时用Bochs看效果时用QEMU。4. 核心实操从引导到内核的完整流程4.1 引导扇区那512字节的门道操作系统启动的第一步是CPU在加电后跳到BIOSBIOS完成硬件自检后把软盘或硬盘第一个扇区512字节读入内存0x7C00处然后跳过去执行。这个扇区就是引导扇区。书中第2章的 boot.asm 就是干这件事的。它的核心逻辑很直接将自身从0x7C00拷贝到0x90000处重新定位。跳转到新的地址继续执行。读取磁盘后续扇区loader.bin到内存0x10000处。跳转到loader执行。我一开始特别不理解为什么要“重新定位”。后来才想明白引导扇区只有512字节而且它所在的内存区域很容易被后续加载的模块覆盖所以要先把自己挪到安全的地方再继续加载其他内容。这个细节如果你不去亲手运行调试光看书很难体会。4.2 保护模式切换从实模式的1MB限制里逃出来实模式下只能访问1MB内存而且没有内存保护。一旦进入保护模式CPU就能访问4GB内存还能通过页表实现虚拟内存。书中第3章的核心就是完成这个转换。loader.asm 里做的事情大致如下加载内核kernel.bin到内存。关闭中断。加载全局描述符表GDT。将A20地址线打开。设置CR0寄存器的PE位。执行一条远跳转刷新指令流水线。进入32位保护模式。这里面最容易出错的是GDT的构造。GDT里的每个段描述符都是64位你必须把它拆成高32位和低32位分别写入任何一位写错程序都会在切换保护模式后立刻崩溃。我当时就经常把段界限的20位填错导致代码段只能访问前64KB一跑就出 #GP 异常。所以我的建议是第一阶段不要自己敲GDT相关的代码直接用光盘里现成的先把整个流程跑通再回来改一两个字段验证理解。否则你在没有任何调试经验的情况下很容易被一堆类似“unexpected GP exception”的报错打垮信心。4.3 你的第一个“操作系统”亲手跑起来在没有Makefile的情况下我建议完整跑一遍下面这套命令看看系统是什么反应。这里以第二个章节的成果为例但你可以照着后面的章节逐步扩展。# 1. 准备目录复制第2章的boot.asm mkdir -p /tmp/oslab cd /tmp/oslab cp /path/to/chapter2/boot.asm . # 2. 编译并生成软盘镜像 nasm -o boot.bin boot.asm dd if/dev/zero ofa.img bs512 count2880 # 生成空白软盘镜像 dd ifboot.bin ofa.img bs512 count1 convnotrunc # 3. 启动 qemu-system-i386 -fda a.img如果一切顺利你会看到一个光标在屏幕左上角或者屏幕中央打印出一段字符串——这就说明操作系统已经跑起来了。虽然是极简的、什么都做不了的系统但它是你自己构建的。如果屏幕黑屏无任何输出不要把锅全甩给代码。先检查a.img是不是在qemu命令行指定的当前目录下再检查dd是否真的写入了用xxd a.img | head看前512字节是否有你编译出的二进制内容最后才是查boot.asm的逻辑。5. 常见问题与排查技巧实录5.1 高频错误速查表下面是我自己和身边朋友在实际操作中遇到的典型问题按出现频率从高到低排列错误现象可能原因解决方案make报错找不到目标文件光盘Makefile里的路径是旧的改成相对路径或者直接手动敲编译命令nasm报错invalid combination of opcode and operandsNASM版本太新/太旧导致语法不匹配换用NASM 2.14版本或者调整语法为[段地址: 偏移]形式ld报错无法识别的-m elf_i386系统默认GCC未安装32位支持库安装gcc-multilib和binutils-multiarchBochs启动时提示romimage文件不存在Bochs路径配置错误用which bochs或bochs -help查看安装路径修改配置QEMU启动后黑屏无输出镜像文件没有正确写入引导扇区用xxd查看镜像前512字节确认不是全零进入保护模式后出现#GP异常GDT描述符错误或段选择子错误用Bochs调试器在异常点查看寄存器检查GDT内容在64位Linux下编译出的镜像无法启动出现了64位代码或ELF格式错误强制用gcc -m32和ld -m elf_i386并确认链接地址正确5.2 为什么新版Bochs跑不起来老镜像如果你用新版Bochs2.7以上跑书中早期的镜像有时候会发现系统启动后光标闪了一下就停了。这不一定是你代码的问题而是新版Bochs对某些CPU指令的执行时序做了更精确的模拟早期代码里的时序假设比如直接往端口写入后立刻读取可能不再成立。解决办法有两种一是在Bochs配置里把cpu: modelpentium, ips1000000改为兼容性更好的模式二是直接把镜像放到QEMU里跑QEMU的兼容性在这个场景下通常更好。如果你需要调试就用Bochs但记得把mouse: enabled0加上否则鼠标中断可能会干扰早期内核的显示输出。5.3 我踩过的坑把镜像烧进U盘有段时间我头脑发热想把这个微型操作系统烧进U盘在真机上启动看看效果。结果折腾了一晚上发现现代主板要么不支持从U盘的软盘模式启动要么UEFI直接跳过了传统BIOS引导。后来我想通了这类实验性操作系统最适合的归宿就是虚拟机真机环境反而会因为各种硬件差异把简单问题复杂化。如果你真想在真机上跑建议找一台老旧的、支持传统BIOS启动的32位笔记本并且用USB FDD模式或者刻录成光盘启动。但是说实话这个操作的性价比太低我不建议再试。6. 延展学习从这本书出发还能去哪里6.1 和《30天自制操作系统》对照起来看如果你手头恰好也有《30天自制操作系统》这本书两本对照着读是个很高效的学习路线。《30天自制操作系统》的代码风格更偏“手工匠人”从C语言和汇编的混编出发几乎不需要太多底层知识就能跟着走而《Oranges一个操作系统的实现》则更偏“学院派”每个模块都紧扣操作系统原理适合你已经有一定基础、想系统搞懂保护模式和分页机制的人。我的建议是先快速刷一遍《30天自制操作系统》的前10天建立起“我也可以写操作系统”的信心然后回到《Oranges一个操作系统的实现》认真啃第3章到第6章把保护模式、分页、中断这三个硬骨头啃下来最后再去看Linux 0.11内核或者xv6你会发现那些“天书”突然变得有迹可循。6.2 结合Linux内核源码做横向对比光盘里的操作系统虽然微型但它五脏俱全。你学完分页机制之后再回头去看Linux内核源码里arch/x86/kernel/head_64.S初始化和mm/memory.c中的页表操作会有一种“老相识”的感觉。微型内核的每个设计决策都能在大内核里找到对应的实现只是规模和复杂度差了几个数量级。还有一条路是结合xv6。xv6是MIT教学用操作系统用现代C语言重写了Unix V6代码量比这本书的多一些但是注释和质量非常高。把这本书的代码和xv6对照阅读你既能理解“从零写系统”的层次感又能看到“在一个系统里实现进程调度”的完整形态。6.3 如何在现代技术栈下继续演进如果你跑通了这本书想继续把它往现代方向演进可以尝试下面几个方向把启动方式从软盘镜像改成硬盘镜像甚至支持GRUB引导。加入虚拟内存支持实现用户态和内核态的隔离。增加简单的网络驱动让它能通过虚拟网卡和宿主机通信。把中断处理从单核扩展到多核。这些方向你需要额外补充的知识包括ELF文件格式、PCI总线枚举、中断控制器如8259A和APIC、内存管理单元细节等。别急着一步到位每走一步都回到虚拟机里去反复调试能力是在这种循环里磨出来的。7. 实操心得少走弯路的几点建议最后说几个我亲测有效的习惯帮你把这套流程走得顺一些。第一不要急着删掉光盘里的原版代码。在你对每个文件背后的意图完全清楚之前原版代码就是你的“参考答案”。我见过太多人一开始就自己重写结果卡在某个时钟中断的细节上浪费好几天。先把原版跑通再逐文件翻写才是高效路径。第二给每个章节单独建一个虚拟机的快照。因为你每学一章都会在前一章的系统上叠加新功能如果改崩了你会需要快速回退到“上一章能正常跑”的状态。虚拟机的快照功能就是为你这种场景准备的。第三把Bochs调试器的几个核心命令背下来b下断点、c继续执行、s单步、info reg查看寄存器、x /16bx 地址查看内存。熟练使用这几个命令比你在源码里加一百句printk都管用。尤其是多任务切换和中断嵌套的调试没有调试器几乎寸步难行。第四学会阅读CPU异常的输出。Bochs报错信息里常见的是#GP通用保护异常、#PF页错误、#UD未定义指令。每一个异常都对应一种具体原因比如#PF基本意味着页表没配对#GP多半是段描述符没搭好。看到异常不要慌先查它是哪一类再往上定位代码。我在给很多新人做技术交流的时候发现大家最大的共同误区是想“一次写对”而不是“快速定位问题、高效修正问题”。而这本《Oranges一个操作系统的实现》恰好就是一本教你精准定位问题的实战书。光盘里的系统虽然小但该有的错误模式一个都不少你越早学会和调试器交朋友后面读任何内核代码都会轻松很多。如果你已经咬牙把前几章跑通了那后面的事情就比你想象的顺利多了。本文还有配套的精品资源点击获取
返回列表