Linux 找到根文件系统之后:initramfs 怎样把 PID 1 叫醒?
内核会管理硬件,却还不是能登录使用的完整系统。我们顺着 initramfs、根文件系统和 PID 1,走到服务与登录画面出现。
上一篇里,U-Boot 把内核、设备树和可能存在的 initramfs 放进内存,再把控制权交给 Linux。现在内核已经开始运行,也逐渐认识了处理器、内存和设备。但屏幕还没有登录框,网络服务也没开始:内核不是整套 Linux 系统,它还要找到真正的系统文件,并启动第一个普通程序。
可以把内核想成刚拿到房屋说明书的维修队长。他认识电线和水管,也能控制工具;但家里还没有开门营业,桌上没有电话簿,工作人员也还没到齐。根文件系统是整间店的货架和房间,PID 1 则像第一位正式到岗、负责叫醒其他工作人员的值班经理。
根文件系统里的“根”是什么?
Linux 把文件和设备整理成一棵目录树。最上面的目录叫根目录,写作 /。系统的程序、配置、设备节点和用户文件,都从这个入口往下排列。内核需要找到一套包含可运行程序、配置和必要文件的文件系统,把它挂到 /,之后程序才能按熟悉的路径找到东西。
这套文件通常在硬盘、固态硬盘、闪存或网络服务器上。固件和启动程序已经把内核送进来了,但内核未必马上会用这块存储:它可能还缺少磁盘控制器驱动、文件系统驱动、解密工具,或者需要先组装 RAID、启用 LVM。
启动参数里常会看到 root=,它说明正式根文件系统在哪儿。例如它可以指向某个设备、分区标识,或网络根文件系统。对没有早期用户空间的简单系统,内核可以按这个信息直接尝试挂载根目录;用了 initramfs 时,早期脚本也常读取同一条启动命令行来找根设备。
为什么要先启动一个临时小系统?
如果内核一开始就找不到硬盘,系统就无法从硬盘读出帮助它找硬盘的驱动和工具。这像钥匙锁在屋里,而开门钥匙也在屋里。
initramfs 就是解决这个绕圈问题的临时工具箱。它会提前放进内存,里面装有早期启动需要的程序和模块。内核先从工具箱运行 /init,由它加载驱动、等待硬盘、解锁加密分区、组装存储阵列,再找到正式系统。
不同 Linux 发行版装进 initramfs 的东西不一样。桌面发行版要照顾很多硬盘、文件系统和加密配置;精简开发板可能只需要几个驱动和一段小脚本;固定用途的设备也可能完全不用 initramfs,直接从闪存启动系统。
initramfs 到底是什么?
名字里的 ram 表示内存,fs 表示文件系统。initramfs 通常是一个 cpio 格式的文件归档,有时还会经过 gzip 等方式压缩。启动程序把它作为内存里的启动资料交给内核;内核把里面的目录和文件展开到自己的临时根文件系统 rootfs 中。
它看起来像一个小型 Linux 根目录,里面可以有 /init、/bin、/sbin、/lib 和设备工具。它并不是另一个完整、长期使用的 Linux 安装,而是开机早期借来用的工作台。完成交接后,系统通常会改用硬盘或闪存上的正式根文件系统。
旧资料还常说 initrd。它是较早一类内存启动盘机制,内核把一个文件系统映像当成 RAM 磁盘来挂载。现代常见的 initramfs 是 cpio 文件被展开到内核准备好的 rootfs,两者历史和内部做法不同;但很多启动工具、文件名和日常说法仍沿用 initrd 这个旧名字。
initramfs 像开门前摆在门口的工具箱:先帮内核找到真正的家,工作完成后再把工具箱让到一旁。
/init:临时系统里的第一位值班员
当内核展开了 initramfs,如果里面有 /init,就会尝试执行它。这个 /init 是用户空间程序,不是内核函数;它可能是 shell 脚本,也可能是一个小型二进制程序。程序虽然很小,却已经能调用内核提供的文件、设备和进程服务。
在主系统的 PID 命名空间里,内核创建的第一个普通用户程序会拿到进程号 1。进程号简称 PID,所以临时系统里的 /init 通常一开始就是 PID 1。这个号码不是它在 Linux 历史里的排名,而是内核分配给进程的一张特殊工牌。
/init 可能先挂载 /proc、/sys、/dev 等早期要用的目录,寻找并加载设备模块,扫描启动参数,等待存储设备出现,再处理加密、RAID 或 LVM。它也可能在屏幕上显示进度或错误提示。哪些步骤存在,取决于设备和发行版。
临时工具箱怎样找到真正的根?
假设 U-Boot 和设备树已经让内核知道存储控制器在哪里。内核里的驱动让系统能够读取 SATA、NVMe、eMMC 或 SD 卡;initramfs 里的程序随后根据 root= 参数或发行版配置,判断哪个分区才是根文件系统。
如果根分区经过加密,早期程序要先打开它;如果使用软件 RAID,就要先把几块盘组合起来;如果根文件系统在 LVM 逻辑卷里,还得先激活对应卷组。接着系统挂载根文件系统,并检查里面是否确实有 /sbin/init 等后续程序。
可以把这个过程想成:先用备用发电机照亮仓库,再找到正式配电箱;检查主电路能用之后,切换到正常电源。临时系统替正式系统完成准备,但不负责长期维持整台机器。
switch_root:把正式系统接到 / 上
正式根文件系统准备好后,initramfs 里的 /init 要把它变成系统新的 /。常见工具叫 switch_root。它会处理旧临时根目录、把新根接到正确位置,然后启动正式系统里的 init 程序。
这里有个名字上的小坑:initramfs 使用的是内核提供的 rootfs,不能简单照搬旧 initrd 的 pivot_root 流程。常见 switch_root 会清理临时根里的内容、切换到挂载好的新根,再用 exec 执行新的 init。不同早期用户空间的实现细节不同,但读者只要先记住结果:文件路径从这时起指向硬盘或闪存上的正式系统。
exec 可以理解成“让当前进程换一套程序继续工作”。在常见的交接方式中,临时 /init 用 exec 启动正式 init,因此它的 PID 仍然是 1;不是新开一个 PID 1 和旧 PID 1 并排生活。内核最初交给这个位置的责任,一直留在同一个进程身上。
PID 1 为什么这么特别?
PID 1 是用户空间最早启动、责任也很重的进程。其他程序结束时,它们的退出状态需要被收拾;父进程提前退出后留下的孤儿进程,也需要有人接管和回收。PID 1 还要处理系统关机、重启和服务管理等工作。
如果 PID 1 很早就失败,系统通常没有另一个普通程序可以接手,内核可能报错并停止启动。因此,正式 init 不只是“第一个被打开的软件”,更是整个用户空间的管理者。容器如果使用独立 PID 命名空间,也会在自己的命名空间里有一个 PID 1;这里讨论的是整台电脑主系统里的 PID 1。
正式 init:叫醒服务,让系统能用
正式 init 通常位于 /sbin/init。如今很多发行版里,这个名字会指向 systemd;也有系统使用 BusyBox init、OpenRC、SysV init 等其他实现。名字和内部安排可以不同,它们都要把内核提供的基础能力变成可供人使用的系统。
systemd 作为 PID 1 时,会读取自己的单元配置和依赖关系,安排挂载文件系统、启动设备管理、日志、网络、时间服务、登录终端等工作。相互没有依赖的工作有时可以并行进行,所以现代 Linux 启动更像按依赖清单安排工作,而不是所有服务严格排成一列逐个等候。
服务器可能启动到命令行登录提示;桌面电脑还会启动图形登录管理器;路由器会拉起网络、DHCP 和管理界面。终端里的 getty 或桌面登录程序准备好之后,人就能输入密码登录。到这里,内核的开机接力终于变成了眼前可用的 Linux。
PID 1 的“1”和运行级别 1 不是一回事
PID 1 是进程编号:指出哪个进程承担系统初始化和管理任务。传统 SysV init 的运行级别 1 则是一种系统运行状态,常用于单用户维护模式。一个说的是“谁在做事”,另一个说的是“系统此刻以哪种服务组合运行”,不要因为都带数字 1 就把它们当成同一件事。
使用 systemd 的系统通常用 target 表达一组启动目标,例如 multi-user.target 常代表多用户命令行环境,graphical.target 通常代表带图形登录的环境。systemd 仍能提供对传统运行级别名称的兼容,所以在资料里也可能看到 runlevel 1、3 或 5。
不使用 initramfs 的电脑怎样启动?
initramfs 很有用,但不是每台电脑都必须有。如果内核已经内置了存储控制器和文件系统驱动,可以直接根据 root= 找到根文件系统,再从里面启动 init。固定硬件的嵌入式系统常会尽量走这条更短的路线。
命令行里的 rdinit= 可以指定 initramfs 中要执行的早期程序,默认通常是 /init;init= 则可以要求内核在正式根文件系统上启动另一个程序,代替通常的 /sbin/init。它们属于排查或特殊启动场景会遇到的选项,平时不必改动。
把从电源到登录的接力画完整
一台常见 Linux 开发板固件 → U-Boot
↓ 加载内核、DTB 和可选 initramfs
Linux 内核认识硬件并准备内存
↓ 执行 initramfs 中的 /init(如果有)
加载驱动、解锁磁盘、找到正式根文件系统
↓ switch_root,并 exec 正式 init
PID 1(常见为 systemd)启动系统服务
↓
命令行登录、桌面登录或设备管理界面如果开机提示“找不到根文件系统”,接力可能停在内核或 initramfs 找存储这一步;如果报 /init 找不到,可能是临时系统里缺了早期入口;如果提示没有可用的 init,则可能是正式根文件系统里没有能运行的初始化程序。错误文字不能单独替代检查,但它能告诉我们接力大概停在什么位置。
一路从 U-Boot、设备树、Linux 内核走到 initramfs,再走到 PID 1,我们终于看见了开机为何不是一个按钮按下去就立刻出现桌面,而是一层层把责任交给更完整的程序。PID 1 拉起服务之后,硬件、文件、网络和登录入口才被组织成我们熟悉的操作系统。
至此,这段“从电源到可用系统”的主线先走完了。接下来可以继续往更早处追问:晶体管如何组成逻辑门,逻辑门怎样装进集成电路,微处理器又怎样执行第一条指令。这样我们就能把开机故事和更长的计算机技术史接在一起。