从零自制操作系统 — 全书拆解
从零自制操作系统 — 全书拆解
30 秒导读: 这本书回答一个问题:一个没有任何操作系统的裸机,怎么一步步变成你桌面上那种能开窗口、跑应用的东西? 作者的回答是:不写一行依赖现成系统的代码,从借主板固件(UEFI)的手启动一个程序开始,先点亮一个像素,再写出字,再让鼠标动起来,再开窗口、多任务、读文件、跑应用——三十章之后你手里就有一个真的能用的操作系统 MikanOS。 本组文档是我们重写的完整拆解,三十章。读完不必看原书。
1. 先交代清楚:这是谁在什么时候写的
| 作者 | 内田公太(Kouta Uchida),日本工程师;高中时参加过《30天自制操作系统》一书的校对工作 |
| 成书 | 日文原著《ゼロからのOS自作入門》(Mynavi 出版社),中文译本 2025 年收录进本库 |
| 与旧书的关系 | 本书是 2006 年《30天自制操作系统》的「重制版」:那本书基于 Windows 时代的旧硬件,这本改用 UEFI 启动、64 位模式、USB 3.0 等现代硬件1 |
| 配套资源 | 全部源码(MikanOS)与支持站点开放在 GitHub 上(uchan-nos/mikanos),每章对应一个 osbook_dayNNx 的代码快照2 |
| 目标读者 | 写过几百行程序的人;C++ 不熟不要紧,书中按需讲解(补充:译者序也提醒这本书「从专业角度看有些过于简略」,它的定位是引路,不是权威)3 |
两件影响阅读的事:
第一,这是一本「日汉对照」版式的书。 转码文本里夹着不少日文原文残页,个别章节的中文部分不完整;我们的拆解以中文部分为准,日文残页只用来补齐中文缺失的内容,脚注里都注明了。
第二,这本书的讲法是「先跑起来,再讲原理」。 它不像 OSTEP 那样每章从一个理论问题出发,而是「今天加一个功能,它坏了/太慢了,我们测一下、修一下」。读它要接受这个节奏:很多概念是在改 bug 的过程中才真正讲透的。
2. 全书一条主线(读完这一节,你就懂了这本书)
这本书是一条从零到一的装配链:每一步解决上一步逼出来的那个「没有它就活不下去」的问题。 只看这一节,你也能把这本书讲给别人听。
① 裸机时代:你的代码根本跑不起来
电脑一开机,内存里是空的,CPU 只会按主板上的固件(UEFI BIOS)写死的步骤走: 设好 CPU、认出硬盘,然后在存储设备上找一个叫 BOOTX64.EFI 的文件,把它读进内存,跳过去执行。 所以「自制操作系统」的第一步根本不是操作系统,而是做一个 EFI 程序让固件肯加载它——而且固件还提供了读文件、画图的现成函数,连硬盘驱动都不用写。(第 01、02 章)
② 接管控制权:固件只是跳板
EFI 程序(引导加载器)的真正任务是把「真正的操作系统」(内核)从磁盘读进来,把内存地图、帧缓冲区(屏幕像素对应的那块内存)等关键信息交给它,然后一脚把 UEFI 踢开—— 因为 UEFI 占着一大块内存,还管着栈,不踢开内核就没有自己的地盘。(第 02、03 章)
③ 先点亮一个像素,再写字
屏幕只是一块像素阵列,每个像素在帧缓冲区里占几个字节。往内存写一个数,屏幕上就多一个点——全书第一个「啊哈」时刻。 字 = 字体(一张「字符编号 → 8×16 点阵」的表)+ 按表涂像素; 控制台 = 自动换行的字符打印机。到这里,黑底白字的黑客屏幕就有了。(第 03–05 章)
④ 鼠标动起来:轮询是个死胡同,中断来救
想知道鼠标动没动,最简单的办法是死循环不停问——问了十万次动了一次,CPU 全浪费在问上。 中断反过来:鼠标动时硬件主动拍 CPU 的肩膀,CPU 放下手里的活去处理。 但处理程序里不能干重活(会丢别的中断),于是事件先进队列,主循环慢慢消化。(第 06、07 章)
⑤ 内核要有自己的内存秩序
UEFI 走后,谁记得哪块内存空闲?内核自己。它按 UEFI 留下的内存地图建台账, 再把 CPU 从古老的「分段」模式拉进「分页」模式——每个程序以后都能拿「从 0 开始的地址」当自家地盘,互相不踩,这是后面一切的地基。(第 08 章)
⑥ 窗口 = 一叠透明纸,慢了就测、测完就改
窗口不是硬件概念,就是按高度顺序一叠图层:从底下的桌面往上,一张张把像素抄到屏幕上。 两张纸都要重画时,直接抄会闪——于是先抄到屏幕外的一块缓冲(后置缓冲)再整体覆盖; 重画太慢——于是拿定时器当秒表,一段段量,量出哪里慢就改哪里。 「先测量,再优化」这条纪律,这本书从第 9 章一直用到最后一章。(第 09、10 章)
⑦ 时间一进内核,多任务就活了
会测时间,就能「每隔 1 毫秒打断一次 CPU,换个人跑」——抢占式多任务。 再进一步:没活干的任务去睡觉(hlt),事件来了才叫醒;任务分优先级;CPU 空转时真正停下省电。 鼠标、键盘、定时器从此都是「事件」,操作系统从「一个 main 跑到底」变成「事件驱动的调度场」。(第 11–14 章)
⑧ 有了多任务,终端和命令才有意义
一个终端就是一个任务。窗口要分「活动/非活动」, 终端要能记住按过的键、执行 echo/clear/lspci、翻历史命令——用户第一次能「使唤」这个系统。(第 15、16 章)
⑨ 磁盘只是一条超长的字节串,文件系统是它的目录
硬盘不会说「文件」,它只会按编号存字节。文件系统干的事就是记账: 卷的布局(每簇多大、FAT 在哪)记在开头(BIOS 参数块),文件名 → 首个簇号记在目录项, 簇 → 下一个簇记在文件分配表。顺着表走就能读出任一个文件——ls 命令就是这么来的。(第 17 章)
⑩ 应用时代:让「别人的程序」在你的系统上跑
此前的代码都是内核自己。从第 18 章起写应用:用 Newlib 标准库算出 1+2, 再把它编译成可执行文件、按 ELF 格式解析、装进分页造好的地址空间——「我的系统能跑一个真正的程序了」,这是自制操作系统的成人礼。(第 18、19 章)
⑪ 系统调用:给应用的唯一一扇门
应用若能为所欲为,一个恶意程序就能偷走一切。 CPU 有特权级:内核在 ring 0,应用在 ring 3,敏感指令应用一碰就报错; 应用想求内核办事,只能敲一扇门——系统调用(触发一次受控的中断,进内核,办完回来)。 write、exit、开窗口……一扇扇门陆续打开。(第 20、21 章)
⑫ 门开得越多,应用越像回事
应用开始能画画、接鼠标键盘事件、计时、做动画、写游戏(打方块)。 操作系统的价值从「能跑」变成「好写应用」——API 设计第一次成为主题。(第 22、23 章)
⑬ 多个终端、多个应用:问题也跟着放大
终端能开好几个,应用能同时跑——窗口重叠的 bug、一个应用把系统拖死的问题全冒出来了。 修这些 bug 的过程中,异常保护、任务终止这些「严肃操作系统」的机制被一个个补上。(第 24 章)
⑭ 文件能读了,标准流来了
应用按「文件描述符」(一个整数编号)读写文件,键盘也伪装成一个文件(标准输入), 输出也能重定向进文件、灌进另一个应用——管道登场:小工具们从此能串成流水线。 grep、sort、more、cat 这些 Unix 老兵在一个 700 页的玩具系统上复活。(第 25–29 章)
⑮ 内存的终极玩法
分页单元用到极致:文件「映射」成内存(按需分页,用到哪页才从磁盘读哪页)、 复制内存先不真复制(写入时才复制)——这两个机制今天还在每一个正经操作系统里服役。(第 27 章)
⑯ 收尾
文本查看器、图像查看器、开机计时器——屏幕热闹起来,书就结束了。 作者最后的叮嘱很朴素:别停,这条路的前方还有宏内核/微内核、网络、安全这些大山。(第 30、31 章)
一句话收尾
操作系统不是某个天才设计出来的,是一件件「没有它就活不下去」的机制,被「先跑起来再说」的工程师精神,一个一个补出来的。 这本书把每一次「补」都让你亲手做了一遍。
3. 三十章地图
不用记任何术语——每章名字后面写的是「读完你能回答什么问题」。
| 章 | 读完你就能回答 |
|---|---|
| 01 环境与启动 | 裸机上的第一个程序怎么跑起来的?U 盘、QEMU、UEFI 各扮演什么角色? |
| 02 内存地图与指针 | 内存有多大、哪块能用,开机时谁知道?C 的指针到底是什么? |
| 03 第一个内核与第一个像素 | 怎么把 UEFI 踢开、让自己的「内核」接管?往哪块内存写数屏幕会亮? |
| 04 C++ 与构建 | 同样的画像素,用 C++ 的类和虚函数写为什么更好?vtable 是什么? |
| 05 字符与控制台 | 字符怎么变成一坨像素?控制台自动换行是怎么实现的? |
| 06 鼠标:PCI 与 USB | 怎么找到鼠标这个硬件?USB 鼠标的数字怎么读出来? |
| 07 中断与队列 | 硬件怎么「拍 CPU 的肩膀」?为什么中断里不能干重活? |
| 08 内存管理 | 内核怎么记账哪块内存空着?分段和分页是什么、为什么要分页? |
| 09 图层与加速 | 窗口叠加的原理是什么?画得太慢,怎么量出来慢在哪? |
| 10 窗口与闪烁 | 画面为什么闪?后置缓冲区怎么治好它?窗口怎么拖动? |
| 11 定时器 | 内核怎么知道「1 秒到了」?定时器不准怎么办? |
| 12 键盘与文本框 | USB 键盘的按键怎么变成字符?Shift 这类组合键怎么处理? |
| 13 多任务 | 一个 CPU 怎么「同时」跑多个任务?切换时要保存什么? |
| 14 休眠与性能 | 没活干的任务怎么睡觉?怎么测量任务切换的开销? |
| 15 窗口管理器 | 谁决定哪个窗口在最上面?终端窗口怎么做成一个任务? |
| 16 终端命令 | 终端怎么解析一行命令?历史记录怎么翻? |
| 17 文件系统 | 磁盘上的字节串怎么变成「文件」?ls 命令在磁盘上翻的是什么? |
| 18 FAT 与第一个应用 | FAT 表怎么把簇串成文件?第一个在用户态跑起来的程序长什么样? |
| 19 加载与运行应用 | 可执行文件怎么被搬进内存?分页怎么给它造出独立地址空间? |
| 20 系统调用入门 | 怎么防止应用把系统搞坏?ring 0/ring 3 和系统调用是什么? |
| 21 完善系统调用 | write/exit 怎么实现?应用怎么开出第一个窗口? |
| 22 应用的图形与事件(1) | 应用怎么画点、画线、等按键? |
| 23 应用的图形与事件(2) | 应用怎么接鼠标事件?打方块游戏怎么写出来的? |
| 24 多终端与应用管理 | 应用把系统拖死怎么办?出错了怎么只关应用不崩系统? |
| 25 文件读取 | 应用怎么按路径找到文件?grep 的搜索是怎么实现的? |
| 26 标准输入输出 | 键盘怎么变成一个「文件」?应用怎么写文件? |
| 27 内存进阶 | 按需分页、内存映射文件、写入时复制各解决了什么? |
| 28 字符编码与重定向 | ASCII/UTF-8/UTF-16 差在哪?输出怎么重定向到文件? |
| 29 管道与进程通信 | `ls |
| 30 收尾应用与前方的路 | more/cat/图像查看器怎么写?这本书之后该往哪走? |
推荐顺序: 01 → 30 顺序读,这也是原书的装配顺序;只想看「应用怎么跑起来」的,读 17–21;只想看「多任务怎么实现的」,读 11–14。
4. 这本书覆盖什么、不覆盖什么
覆盖(而且讲得比多数材料清楚)——读完你能回答:
- 从按下电源键到内核接管,这中间每一步是谁在跑、为什么非它不可;
- 屏幕上的字和窗口,本质上往哪块内存写了什么数;
- 中断从硬件信号到处理函数,中间经过哪几张表、几次特权级跳转;
- 多任务切换时到底保存了哪些东西,定时器凭什么能「抢占」;
- FAT 文件系统的三张表(参数块、目录项、FAT)各管什么,顺着它们怎么读出一个文件;
- 系统调用从应用里的函数调用到内核里的处理,全程经过哪些关口(特权级、TSS、IST);
- 按需分页、内存映射文件、写入时复制这三个「现代 OS 标配」的最小实现;
- 外加一条贯穿全书的工程纪律:先测量,再优化;先跑起来,再讲原理。
不覆盖(别指望在这本里找):
| 缺什么 | 说明 |
|---|---|
| 调度理论 | 只有轮转 + 简单优先级,没有 MLFQ、比例分享这些策略(OSTEP 有) |
| 并发原语 | 没有锁、信号量、条件变量——单 CPU 假设下用关中断代替了 |
| 网络 | 协议栈一个字没写 |
| SMP/多核 | 全书按单 CPU 写 |
| 安全体系 | 只有特权级 + 系统调用一条线,没有用户、权限、加密 |
| FAT 之外的世界 | 日志文件系统、崩溃一致性、SSD 磨损均衡都不在 |
| 严肃的语言理论 | C++ 特性讲到够用为止,编译原理、类型系统没有 |
| 硬件细节的权威来源 | xHCI、ACPI 等规范只讲「够实现 MikanOS 的部分」,深入要读规范原文 |
5. 今天读,要修正的三处
这本书写于 UEFI 已普及但 USB 键盘驱动还值得手写的年代,机制的讲解没有过时,但三处要带着今天的眼光看:
| 书里的世界 | 今天的对照 |
|---|---|
| 鼠标键盘靠手写 xHCI/USB 驱动(第 6、12 章) | 今天多数 OS 教学改用 PS/2 模拟或现成的驱动栈;但从零写一遍仍是理解 USB 的最好办法,只是别把这当行业惯例 |
| 分割内存用 2MiB 大页、四级页表手搓(第 8、19、27 章) | 机制仍是 x86-64 的分页,但五级页表、1GiB 大页、IOMMU 已常见;概念不变,常数变了 |
| C++ 手写一切、Newlib 补 POSIX(第 4、18 章) | 今天的新 OS 多用 Rust(Z core、Redox)或 Go;但「语言机制怎么在无 OS 环境落地」的道理完全一致,这本书是观察这件事的最好样本 |
判断(我们的,不是书里的): 这本书和 OSTEP 在书架上正好是互补的两极—— OSTEP 从理论问题出发(「如果应用捣乱怎么办」),这本从工程装配出发(「今天让鼠标动起来」)。 读法应该是:用这本书建立「每个机制长什么样、不装它行不行」的直觉,用 OSTEP 补上「为什么非得这样设计」的推理。 单读这本,容易把「MikanOS 的做法」误当「操作系统必须的做法」——它常常只是「个人一周能做出来的做法」。 如果错,会错在: 如果读者本来已经有丰富系统经验,这本书的增量可能只剩动手乐趣;那时它的价值排序应下调。
判断(我们的,不是书里的): 全书最该带走的是那条工程纪律——「先测量,再优化」在每一章都真的执行了: 叠加慢 → 拿定时器量出 50 倍差距 → 只重画变化的矩形;滚动慢 → 量出整屏 memcpy → 改成搬移。 这比任何单个机制都通用:它示范了性能工作的标准姿势,而不是玄学调参。 如果错,会错在: 演示用的性能数字都来自 QEMU 模拟器和作者手头一两台机器,量级可信、绝对值不可搬; 照抄「改完快了多少倍」到自己的环境会落空——姿势是对的,数字要重量。
判断(我们的,不是书里的): 「从零自制」的营销话术容易让人以为这本书不要基础。 实际上它对「写过几百行程序的人」友好,但对完全的新手不友好——默认读者知道 C 的指针、编译链接、十六进制。 我们的拆解把这些门槛都补在当场了;读原书前建议至少先会写 C。 如果错,会错在: 如果读者带着极强的耐心逐节啃,也可能零基础走完——作者确实在每个硬概念处给了最小解释,只是密度对纯新手偏陡。
拆解写于 2026-10-19,依据 2025 年中文译本(日汉对照版式)。原始书籍文件不入库,本组文档是我们自己的重写;每条引用都可用 node scripts/book-verify.mjs cong-ling-zi-zhi-cao-zuo-xi-tong 回核。
Footnotes
-
出处:「目标读者」第 9 段(
text/04-fm.txt:9,搜「2006年出版的《30天自制操作系统》」)。原文:「本书是2006年出版的《30天自制操作系统》的衍生成果……自该书出版以来,个人计算机已经发生了很大的变化,有些信息(尤其是与硬件相关的信息)已经过时。」 ↩ -
出处:「目标读者」第 7 段附近的支持站点说明与「0.4 享受自制操作系统的乐趣」(
text/09-ch00-04-0-4.txt,搜「GitHub Issues」)。原文说明读者可在 GitHub Issues 提问、用 GitHub Wiki 记笔记,支持站点是读者聚集的地方。 ↩ -
出处:「译者序」第 9 段(
text/02-fm.txt:9,搜「过于简略」)。译者序明写「从专业的角度来看,MikanOS这个操作系统有些过于简略」;「目标读者」第 13 段(text/04-fm.txt:13,搜「几百行代码」)写明「本书的目标读者是写过简单程序的人」。 ↩