1. 系统启动流程概述:BIOS/UEFI、Bootloader、内核初始化、init进程
大家好,我是你们的老朋友。今天咱们聊聊系统启动这件事。
说实话,我入行头两年,一直觉得系统启动就是个「按电源键 → 进桌面」的过程。直到有一次线上服务器怎么都起不来,我盯着黑屏的终端,才意识到——不懂启动流程,你连问题出在哪都不知道。
系统启动,说白了就是一段接力赛。从你按下电源键,到看到登录提示符,中间经历了四个关键阶段:BIOS/UEFI → Bootloader → 内核初始化 → init进程。每个阶段都有它的使命,也有它的坑。
核心要点:启动流程的每个阶段都是「前一个阶段加载后一个阶段」的链式关系。任何一个环节出问题,系统都起不来。
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 内核初始化:从裸机到有根
内核被加载到内存后,开始执行初始化。这个过程我把它分成三步:
- 解压自身:内核镜像通常是压缩的,先解压到内存中
- 初始化硬件:检测 CPU、内存、中断控制器、定时器等
- 挂载根文件系统:这是最关键的一步。内核根据启动参数
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 进程启动所有服务。这个流程你记住了,以后遇到启动问题,就能快速定位是哪个环节出了岔子。
一句话总结:系统启动就是一场接力赛,每一棒都至关重要。搞懂它,你就能在服务器面前挺直腰杆。