Skip to content

导引:点亮什么与为什么

001 让内核能自己读写磁盘扇区了,但读出来的是一坨 512 字节的裸数据——没有名字、没有「这是哪个文件」的概念。要从「扇区」走到「文件」,内核得先学会认一种文件格式。这一章,我们让内核解析一个 ustar(就是 tar)格式的归档,把里面每个文件的名字和大小列出来。先把丑话说在前头:这一章的归档不是从 001 的 AHCI 盘上读来的,而是构建期就被嵌进了内核镜像里、随内核一起加载进内存的。这是个有意的简化——先用最省事的办法把「文件」这个抽象立起来,至于「从磁盘上读真正的根文件系统」,留到后面。

这一章我们要点亮什么

一件能力,一个里程碑。

能力是:内核第一次理解「文件」是什么。我们准备一个 tar 归档(里头放几个测试文件:hello.txtreadme.txtetc/passwd),在编译时把它整个塞进内核二进制;内核启动后,解析这个归档,逐个认出里面的文件,把名字和大小打到串口上。从这一章起,内核不再只认识「扇区」,它认识「文件」了。

里程碑是 mount 之后的那几行串口输出:

text
[RAMDISK] Archive at 0x..., size 10240 bytes
[RAMDISK]   FILE: hello.txt  (18 bytes)
[RAMDISK]   FILE: readme.txt  (23 bytes)
[RAMDISK]   DIR:  etc/
[RAMDISK]   FILE: etc/passwd  (11 bytes)
[RAMDISK] 3 file(s) found in initrd.

看到这些文件名被正确解析出来,说明内核吃透了 ustar 归档的格式:头怎么排、大小怎么读、数据块怎么跳过。

不过得诚实划清边界:这一章只是「列出归档里的文件名和大小」。它没有「打开-读-关闭」的 API、不能按名字取文件内容、不能写、不维护目录树——RamdiskEntry 这个描述单文件的结构体虽然定义了,但本章的 mount 还不填充它(它是给后面按名查找预留的形状)。这一章是「认得文件长什么样」,不是「能操作文件」。

为什么现在需要它

为什么紧跟在 001 之后。001 给了扇区读写,但扇区是「设备语言」,不是「用户语言」。你没法跟 shell 说「帮我跑 hello.txt」——shell 听不懂扇区号。要往「能从盘上加载并运行程序」走,中间必须有一层「文件」抽象:把一串扇区解释成「有名字、有大小的文件」。这一章就是来补这层抽象的。

那为什么不直接用 001 的 AHCI 驱动从盘上读一个文件系统?因为那是两件事叠一起:既要「从盘上读对扇区」(块设备层),又要「把扇区解释成文件」(文件系统层)。两层一起上,调试时分不清是驱动读错了还是格式解析错了。这一章做了个聪明的解耦:它不碰磁盘,把一个现成的 tar 归档直接嵌进内核镜像——数据是构建期固定好的、确定无误的,内核只要专心学「怎么解析 tar 格式」这一件事。001 的 AHCI 驱动在这一章没参与加载,这是有意的;等到格式解析这关过了,再谈「从盘上读真正的根文件系统」,那时候驱动和格式才组合起来。

还有一笔技术上的账,关于「数据怎么进内核」。内核二进制本身是一堆 .text/.data 段,被 bootloader 整个加载进内存。要把一个外部文件(initrd.tar)也弄进来,得在编译期把它转成一段可链接的 ELF 数据、放进一个专门的段(.initrd)、再由链接器排进内核镜像里。内核代码靠两个链接器符号(_binary_initrd_start / _binary_initrd_end)在运行时找到这块数据的边界。这套「把任意文件嵌进内核」的流水线,是这一章的另一半工程,后面嵌入用户程序二进制(023 那条路)也是同款手法。

035_multi_terminal-45-gf25de18 · f25de18 · 2026-08-04