1. 系统启动流程概述

大家好,我是你们的老朋友。今天咱们聊聊系统启动这件事。

说实话,我入行头两年,一直觉得系统启动就是个「按电源键 → 进桌面」的过程。直到有一次线上服务器怎么都起不来,我盯着黑屏的终端,才意识到——不懂启动流程,你连问题出在哪都不知道。

系统启动,说白了就是一段接力赛。从你按下电源键,到看到登录提示符,中间经历了四个关键阶段:BIOS/UEFI → Bootloader → 内核初始化 → init进程。每个阶段都有它的使命,也有它的坑。

核心要点:启动流程的每个阶段都是「前一个阶段加载后一个阶段」的链式关系。任何一个环节出问题,系统都起不来。

BIOS/UEFI 硬件自检 Bootloader GRUB / 引导加载 内核初始化 驱动加载 / 挂载根 init进程 systemd / 服务启动 系统启动四阶段:前一个加载后一个,环环相扣

1.1 BIOS/UEFI:硬件自检与启动设备选择

按下电源键后,CPU 会去执行一段固化在主板上的代码。这就是 BIOS 或 UEFI 的领地。

BIOS(Basic Input/Output System)是老传统了。它做两件事:POST(Power-On Self Test,加电自检)和查找启动设备。POST 检查内存、CPU、显卡这些硬件是否正常。如果听到「嘀」一声短响,说明硬件自检通过。要是长响不停……嗯,你该检查内存条了。

UEFI(Unified Extensible Firmware Interface)是 BIOS 的接班人。它更现代,支持 GPT 分区表、图形界面、网络启动。我个人习惯在服务器上全用 UEFI + GPT 组合,因为支持 2TB 以上的磁盘,启动速度也更快。

我的经验:有一次客户服务器启动卡在 POST 阶段,怎么都过不去。排查了半天,结果是主板纽扣电池没电了,BIOS 设置丢失。换了个电池,世界清净了。所以,别小看那块小电池。

BIOS/UEFI 最后一步,是根据启动顺序(比如硬盘、U盘、网络)找到启动设备,读取它的第一个扇区(MBR 或 GPT 的引导分区),然后把控制权交给 Bootloader。

1.2 Bootloader:引导加载程序

Bootloader 的任务很明确:加载操作系统内核到内存。

Linux 世界最常用的 Bootloader 是 GRUB2(GRand Unified Bootloader 2)。它支持多系统引导、内核参数传递、救援模式。你开机时看到的那个选系统菜单,就是 GRUB 的界面。

GRUB 的工作流程大致是:

  • 读取配置文件 /boot/grub/grub.cfg
  • 显示启动菜单(如果有多个内核或系统)
  • 根据选择加载内核镜像 vmlinuz 和初始内存盘 initramfs
  • 把控制权交给内核

避坑指南:我曾经在升级内核后忘记更新 GRUB 配置,结果重启后系统找不到新内核,直接卡在 GRUB 提示符下。那会儿我冷汗都下来了。后来学乖了——每次装完内核,一定跑一遍 grub2-mkconfig -o /boot/grub2/grub.cfg。

这里有个小细节:initramfs 是干嘛的?说白了,它是一个微型根文件系统,包含内核启动初期需要的驱动(比如磁盘控制器驱动、文件系统驱动)。因为内核本身不认识你的硬盘,得靠 initramfs 里的驱动先挂载真正的根文件系统。

1.3 内核初始化:从裸机到有根

内核被加载到内存后,开始执行初始化。这个过程我把它分成三步:

  1. 解压自身:内核镜像通常是压缩的,先解压到内存中
  2. 初始化硬件:检测 CPU、内存、中断控制器、定时器等
  3. 挂载根文件系统:这是最关键的一步。内核根据启动参数 root= 找到根分区,挂载到 /

你想想看,如果内核找不到根文件系统,会发生什么?它会 panic(内核恐慌),然后系统就卡死了。我在生产环境遇到过这种情况——磁盘的 UUID 变了,但 grub.cfg 里还是旧的 UUID,结果内核挂载根分区失败。那一次我学会了:永远用 UUID 或 LABEL 来指定根分区,别用设备名(比如 /dev/sda1),因为设备名可能会变。

关键命令:查看当前根分区的 UUID 用 blkid /dev/sda1,或者 lsblk -f。启动参数里写 root=UUID=xxxx-xxxx-xxxx 最稳妥。

内核初始化完成后,会在根文件系统上启动第一个用户空间进程——init 进程。

1.4 init进程:用户空间的起点

init 进程是 Linux 系统中所有进程的祖先。它的 PID 永远是 1。如果 init 进程挂了,整个系统就崩溃了——因为内核会 panic。

传统上,Linux 使用 SysV init,通过 /etc/inittab 和一系列启动脚本(/etc/rc.d/)来启动服务。但 SysV 是串行启动的,速度慢,依赖关系也不好管理。

现在的主流是 systemd。它并行启动服务,速度快得多。systemd 的配置文件是 .service 文件,放在 /usr/lib/systemd/system/ 和 /etc/systemd/system/ 下。

特性 SysV init systemd
启动方式 串行 并行
配置文件 Shell 脚本 .service 单元文件
依赖管理 手动排序 自动解析
日志 /var/log/messages journald
速度 慢 快

我个人更偏爱 systemd。它虽然学习曲线陡一点,但用熟了真的很顺手。比如你想看某个服务为什么启动失败,一条 journalctl -u nginx.service 就能看到完整日志,比翻 /var/log/messages 方便多了。

小技巧:如果你怀疑某个服务拖慢了启动速度,可以用 systemd-analyze blame 查看每个服务的启动耗时。我曾经用这个命令发现一个数据库服务的启动脚本里有个 30 秒的 sleep,优化后系统启动时间从 45 秒降到了 15 秒。

init 进程启动后,会依次启动系统服务(网络、SSH、数据库等),最后启动登录管理器或 getty,让你看到登录提示符。到这一步,系统启动就算完成了。

嗯,回顾一下:BIOS/UEFI 做硬件自检 → Bootloader 加载内核 → 内核初始化并挂载根文件系统 → init 进程启动所有服务。这个流程你记住了,以后遇到启动问题,就能快速定位是哪个环节出了岔子。

一句话总结:系统启动就是一场接力赛,每一棒都至关重要。搞懂它,你就能在服务器面前挺直腰杆。