1. 启动过程这东西,值得你专门花一章来啃
RH134的第十章“控制启动过程”,在整门课里的地位有点像驾照考试里的科目二——平时看着不起眼,真到了故障现场,救急全靠它。前面几章教你装系统、管存储、配网络,这章讲的是系统从按下电源键到出现登录提示符之间,到底发生了什么,以及当这个过程出问题时,你能从哪里插手。
我学这一章时最大的感受是:启动过程不是一条直线,而是一条有多个“岔路口”的管道。每个岔路口都留了检修口,也就是各种启动参数和救援模式。你要做的不是背下每个开关的位置,而是搞懂每个检修口什么时候能打开、打开之后能干什么、干完怎么出来。
很多同学容易把这章当成命令背诵题,systemctl isolate rescue.target背得滚瓜烂熟,但真问他rescue模式和emergency模式差在哪、什么时候用哪个,就答不上来了。这恰恰是RHCSA考试和实际工作中最喜欢考察的点。
这一章适合谁看?正在备考RHCSA/RH134的朋友、刚入职需要维护Linux服务器的初级运维、还有那些一直靠重启解决一切问题的“重启侠”——学会控制启动过程之后,很多问题不用重启也能处理,真需要重启的时候,也能在重启过程中做点手脚。
2. 先搞懂启动链条:从按下电源键到登录提示符之间发生了什么
2.1 固件阶段:BIOS和UEFI各自怎么找启动设备
任何Linux系统启动,第一步都不是Linux自己说了算,而是固件先接管。老一点的机器用BIOS,新机器基本都是UEFI。这个阶段的核心任务就一件事:找到一个包含引导加载程序的设备。
BIOS的方式很“机械”——它按照CMOS里设置的启动顺序,逐个检查硬盘、光驱、U盘,在每个设备的前512字节(MBR区域)寻找可用的引导代码。这个区域太小了,装不下复杂逻辑,所以BIOS年代的GRUB会把自身拆成好几段,MBR里只放第一段,剩下的存在后续扇区里,每次启动时现场拼接。这也是为什么BIOS时代引导程序损坏后恢复比较麻烦。
UEFI就聪明得多。它直接读取ESP分区(EFI System Partition,一般格式化为FAT32,挂载在/boot/efi)里的.efi引导文件。系统里装了几个操作系统,ESP分区里就有几个对应的.efi文件,UEFI固件通过NVRAM里的启动项记录来调用它们。这个设计的好处是引导加载程序不再受512字节限制,坏了一个可以通过efibootmgr重建启动项,比BIOS时代灵活很多。
RHEL 9默认使用UEFI启动,但RH134的教材并没有放弃对BIOS/传统模式的讲解,原因很简单:生产环境中老机器存量依然很大,而且考试环境也可能会用传统BIOS模式模拟,所以两种固件的差异你得有概念。
2.2 引导加载程序:GRUB2加载内核和initramfs
固件找到GRUB2之后,控制权交给GRUB。RHEL 9用的是GRUB2,配置主文件是/boot/grub2/grub.cfg(BIOS模式)或/boot/efi/EFI/redhat/grub.cfg(UEFI模式)。注意这个文件是生成出来的,不是手写的,手动改完/etc/default/grub之后要执行grub2-mkconfig -o /boot/grub2/grub.cfg重新生成。
GRUB2做的事情分两步:
- 加载内核
vmlinuz-$(uname -r)到内存 - 加载initramfs(初始RAM磁盘文件系统)到内存
这里我多说两句initramfs。很多初学者不理解:为什么内核不能直接挂载根分区,非得先加载一个临时的RAM文件系统?
原因是内核刚启动时,它还没有能力读取真正的根文件系统。现代服务器用的是各种存储控制器驱动(比如NVMe、SAS HBA卡),这些驱动是编译成模块的,不在内核本体里。内核需要先启动一个迷你的用户空间环境(也就是initramfs),在这个环境里加载必要的存储驱动、识别磁盘、组装LVM或RAID设备,然后才能真正挂载根分区。initramfs本质上是内核和根文件系统之间的桥梁。
还有一个容易忽略的细节:/boot目录下的文件并非全都能直接读。initramfs里包含了/etc/fstab的内容和必要的设备映射信息,所以即使根分区在LVM逻辑卷上,内核也能通过initramfs里的配置找到它。如果你把根文件系统的设备类型改了(比如从LVM改成标准分区),但没有重建initramfs,系统启动时就会卡在“无法找到根设备”的地方——
Warning: /dev/mapper/rhel-root does not exist这时候你知道大概率是initramfs里的设备信息过期了,用安装光盘进救援模式执行dracut --force重建,就比无头苍蝇一样乱试强得多。
2.3 内核初始化与systemd接管
内核把initramfs里的/init跑起来之后,这个程序会做几件事:加载存储驱动、udev创建设备节点、发现根文件系统并挂载、最后把控制权交给真正的/sbin/init——在现代RHEL里,它就是指向systemd的符号链接。
从这一刻起,启动流程从“内核主导”切换为“systemd主导”。systemd的第一个进程PID永远是1,后续所有用户态进程都是它直接或间接拉起来的。你可以观察到,系统里除了内核线程(PID 2的kthreadd及其子孙),其余所有进程的PPID最终都能追溯到1,这正好验证了systemd的进程树根节点地位。
systemd的启动逻辑不是“按数字顺序执行一堆脚本”,而是按依赖关系并行启动单元。它读取默认目标(default target)对应的依赖树,把能同时启动的服务并行拉起,不能并行的就等前面的完成后立刻启动。这也是RHEL 7之后启动速度明显快于SysVInit时代的原因——SysVInit是严格串行的,服务越多,启动越慢。
3. 启动目标与systemd的关系:rescue、emergency、multi-user到底怎么选
3.1 systemd.target是什么:它不是运行级别,但可以看成一个“状态集合”
老Linux用户都习惯说“runlevel 3”“runlevel 5”,到了systemd时代,这个概念被替换成了target。一个target不是一个孤立的“级别”,而是一个依赖集合——它声明了自己需要哪些单元处于活动状态,systemd会根据这些依赖把整棵依赖树拉起来。
RHEL 9里和启动过程强相关的target有这几个:
| Target | 对应旧runlevel | 说明 |
|---|---|---|
| poweroff.target | 0 | 关机 |
| rescue.target | 1 | 单用户模式,挂载所有本地文件系统,启动最小化服务 |
| emergency.target | S | 紧急模式,几乎不启动任何服务,文件系统只读 |
| multi-user.target | 3 | 多用户字符界面 |
| graphical.target | 5 | 多用户图形界面 |
| reboot.target | 6 | 重启 |
这些target之间是可以互相切换的,并不是系统启动那一刻定的就一成不变。系统运行时,你执行systemctl isolate rescue.target就能切到救援模式;从救援模式里执行systemctl isolate multi-user.target就能切回来。
3.2 修改默认启动目标:set-default和get-default
RHEL 9装机时如果选了带GUI的软件包组,默认target就是graphical.target。很多服务器其实不需要图形界面,为了省内存和安全,可以把默认target改成multi-user.target:
# 查看当前默认 systemctl get-default # 设置为多用户模式 systemctl set-default multi-user.target # 验证 systemctl get-default注意set-default只是改默认配置,不会立刻切换当前运行状态。想要立即切换使用systemctl isolate multi-user.target。
这招在日常运维里太常用了。我处理过一台内存只有2G的旧服务器,装了GNOME桌面,平时根本没人登录图形界面,桌面进程却白白吃掉几百MB内存。改成multi-user.target重启之后,内存占用直接降了一大截,ssh登录速度都快了。
3.3 rescue和emergency:哪个更“急救”
这两个模式是启动排障的核心工具,我分开细说。
rescue.target,救援模式。这个模式下systemd会完成基本初始化,挂载所有本地文件系统(你/etc/fstab里配置的挂载点都会挂上),启动网络(如果配置了的话),然后给你一个root shell。它适合的场景是:系统能启动到一定程度,但某个关键服务起不来导致进不了全功能状态,或者需要修改某个配置文件但文件系统是可读写的。
进入rescue模式的方式:
# 运行中直接切换 systemctl isolate rescue.target # 或者重启时在GRUB菜单按e,在linux行末尾添加systemd.unit=rescue.targetemergency.target,紧急模式。它更进一步,只挂载根文件系统(而且默认是只读的),不启动网络,连服务管理都不初始化。适合的场景是:文件系统损坏需要fsck、/etc/fstab写错导致启动卡住、某些关键系统文件被改坏需要修复。
进入emergency模式的方式:
systemctl isolate emergency.target # 或者在GRUB里添加systemd.unit=emergency.target这里有个重要的实操细节:emergency模式下根文件系统是只读的,你需要先重新挂载为读写才能修改文件:
mount -o remount,rw /很多新手进了emergency模式,明明看到了root shell,却执行不了vi /etc/fstab,因为文件系统只读,vi会报错说只读文件系统。先执行上面这条命令,问题就解决了。
两者选哪个?我的经验法则是:如果你怀疑文件系统或基础配置坏了,用emergency;如果只是某个服务或网络的问题,用rescue就够。rescue模式像带着工具箱进了机房,emergency模式像是只带了一把撬棍——越往后者,能干的活越少,但越适合在最恶劣的环境里救命。
4. 系统启动排障:从GRUB菜单到登录提示符,每一环都可能出问题
4.1 内核启动参数:写错一个参数,系统可能直接卡死
控制启动过程的一个重要手段是通过GRUB向内核传递参数。在GRUB菜单界面按e进入编辑模式,找到以linux开头的那一行(RHEL 8/9上可能是linuxefi),在行尾追加参数,然后按Ctrl+x启动。
常用的内核参数我列一下:
systemd.unit=rescue.target:直接启动到救援模式systemd.unit=emergency.target:直接启动到紧急模式rd.break:在initramfs阶段中断,进入dracut shellquiet:内核启动时少打印信息(出问题排查时要删掉这个参数)rhgb:Red Hat图形启动画面(排查问题时建议删除)
举个例子。系统开机卡在黑屏但没完全死机,你按e进GRUB编辑,把quiet rhgb两个参数去掉,按Ctrl+x重新启动。这时你能看到内核启动时一行行刷屏的输出,屏幕卡在哪一行,往往就是问题线索所在。
我在实际工作中遇到过卡在Starting Login Service...的情况,看起来像是systemd的登录服务起不来,去掉quiet rhgb后才发现是磁盘I/O错误反复重试导致的。这个线索如果不看启动日志,光靠猜很难定位。
4.2 查看启动日志:journalctl不是只能看当前系统
排障时需要看启动日志,journalctl是主力工具:
# 查看本次启动日志 journalctl -b # 查看上一次启动日志 journalctl -b -1 # 实时跟踪启动日志 journalctl -f # 查看启动耗时排行 systemd-analyze blamesystemd-analyze blame是一个很容易被忽略的好用命令。它列出每个服务启动花了多少时间,按从长到短排序。排查“开机慢”问题时,用这个命令一眼就能看到是哪个服务在拖后腿,我之前遇到过一个Docker服务每次启动要等60秒超时的案例,就是靠这个命令快速定位的。
4.3 GRUB菜单坏了怎么办:从安装介质引导rescue模式
如果你改坏了GRUB配置,或者写错了内核参数导致系统根本进不了GRUB菜单,这时候还有一个终极入口:用RHEL安装光盘/U盘启动,进入rescue模式。
在安装介质启动菜单里选择“Troubleshooting”→“Rescue a Red Hat Enterprise Linux system”,系统会扫描现有安装,然后给你三个选项:
- Continue:自动探测并把现有系统挂载到
/mnt/sysimage - Read-Only mount:只读挂载
- Skip:不挂载,直接给shell
选1之后执行chroot /mnt/sysimage,你就进入了原系统的环境,此时可以重新生成GRUB配置、修改fstab、重建initramfs,想干嘛就干嘛。
这里有个经验之谈:进入rescue模式后先看/etc/fstab和/boot/grub2/grub.cfg这两个文件的修改时间和内容,十次启动故障有八次是这两个文件被人为改错了。先看它们能省很多排查时间。
5. 单用户模式的实际应用:重置root密码
5.1 rd.break:在initramfs阶段动手
RH134教材里重置root密码的官方推荐方法是rd.break。原理是在内核启动到initramfs阶段时强制中断,跳到一个dracut shell里。这时候真正的根文件系统还没挂载,但你可以在这个阶段做手脚。
具体步骤:
- 重启系统,在GRUB菜单按
e编辑 - 找到
linux那一行,在行尾追加rd.break,按Ctrl+x启动 - 系统会卡在类似
switch_root:/#的提示符 - 执行
mount -o remount,rw /sysroot(把根文件系统以读写方式重新挂载) - 执行
chroot /sysroot切换进真正的根环境 - 执行
passwd root重置密码 - 如果之前启用过SELinux,执行
touch /.autorelabel(让下次启动时自动重新打SELinux标签) - 执行
exit退出chroot,再执行exit或reboot -f重启
注意第7步,RD.break方法会绕过SELinux的进程标签设置,如果直接重启,系统可能因为SELinux上下文不正确而拒绝root登录。加了/.autorelabel之后,重启过程中SELinux会重新标记整个文件系统,虽然耗时稍长,但能避免诡异的权限问题。
5.2 resuce模式重置密码的替代思路
rd.break不是唯一的路子。用前面说的安装介质救援模式也可以,流程类似:进rescue shell、chroot /mnt/sysimage、passwd root、exit、重启。
还有一个思路值得提一下:如果你手上正好有另一台能登录这台机器的方式(比如KVM/vSphere控制台还在,只是忘了SSH密码),其实不用物理重启也能改密码——登录进单用户模式或直接用有sudo权限的账户执行sudo passwd root就行。但真实场景往往是你只有被锁在门外这一个选项,所以rd.break还是必须掌握的。
5.3 重置密码之后:SELinux上下文必须处理
谈到SELinux,我多叮嘱一句。
在很多人的实际测试里,直接rd.break重置密码后重启,系统会提示autorelabel就成功了。但如果你跳过了touch /.autorelabel这步,或者机器上有大量文件需要重打标签(比如数据库数据目录),重启后你可能会被卡在一个SELinux拒绝访问的坑里。
我踩过一次这样的坑:帮客户的物理服务器重设root密码,图省事跳过了autorelabel,结果重启后root登录直接被SELinux拒绝(?)。当时我一脸懵,后来用安装介质进救援模式执行touch /.autorelabel才解决。密码重置本身不难,难的是重置之后系统还能不能正常起来,这一课我一直记到现在。
6. 实际排障复盘:一台RHEL 9机器卡在启动阶段的全过程
理论讲了这么多,我拿一个真实排障案例做复盘,把整个排查链路走一遍,这样知识才能串起来。
6.1 故障现象
一台RHEL 9虚拟机,配置了LVM根分区,某天管理员在/etc/fstab里加了一个额外的XFS磁盘挂载条目,结果重启时系统卡在类似这样的位置:
[ OK ] Found device /dev/mapper/rhel-root. [ OK ] Reached target Initrd Root File System. [ OK ] Reached target Initrd File Systems. Mounting /sysroot...屏幕停在这里,不再往下走,但也不是完全死机,键盘还能响应。
6.2 排查思路
这种卡在挂载根文件系统前后的情况,八成是initramfs阶段就出问题了。我先在GRUB菜单按e,删掉quiet rhgb重启,观察详细输出,果然看到内核在等待某个设备超时:
Job dev-mapper-rhel\x2droot.device/start timed out.但我确认过根分区本身没坏。再一想,/etc/fstab里新加的那个XFS挂载条目,可能让systemd误以为根文件系统依赖它——因为fstab里的挂载顺序和依赖关系错误会导致启动任务无限等待。
6.3 解决过程
- 重启进入GRUB编辑,添加
systemd.unit=emergency.target,跳过所有fstab挂载,直接进emergency模式 - 进emergency模式后,先
mount -o remount,rw /把根文件系统改为可写 - 用
vi打开/etc/fstab,注释掉新加的那行 mount -a测试所有挂载点能否正常挂载- 确认无误后执行
reboot
系统恢复正常。整个过程没有用到安装介质,靠的就是在GRUB阶段向内核传递systemd.unit=emergency.target这个参数。
这个案例的教训是:改fstab之前一定要用mount -a做dry-run测试,或者至少确认挂载点目录已存在。我当时是没先建挂载点目录,导致systemd认为该挂载永远失败,从而阻塞了依赖它的所有后续单元。
7. 把这章内容真正变成自己的:几条实操建议
控制启动过程这章,光看书做题是学不踏实的,它是一门“动手技能”。我建议你有条件的话,在虚拟机里把这些实验做一遍:
最基础的一组实验是启动目标切换——把一个运行中的系统从multi-user切到graphical,再切回来,体会一下isolate和set-default的差异。
第二组实验是启动参数传递。在GRUB菜单里删掉quiet rhgb,观察启动输出的详细程度差异;追加systemd.unit=rescue.target和emergency.target,分别体验两种模式里的环境差异——文件系统是否只读、网络是否可用、哪些服务在跑。
第三组实验是密码重置。在虚拟机里按rd.break的流程完整走一遍,包括touch /.autorelabel的步骤,然后把重置后的系统重启观察SELinux的relabel过程。做过一遍之后,你就知道为什么这个步骤不能省。
第四组实验是破坏和修复。故意改坏一个服务的配置(比如把nginx的配置改成语法错误),或者往fstab里加一个错误的条目,然后重启,体会启动卡住时排查的完整过程。修复之后再想想:如果连这个服务都起不来,我还能用什么方式进入系统?
这组实验做完,你对启动过程的理解会有一个质的飞跃。到时候再回头看systemctl list-units --type=target的输出,你看到的就不是一串名字,而是一张张可以按需切换的状态网络图。
启动过程就像一栋楼的消防通道——平时没人走,但真出事的时候能不能快速找到入口、顺利到达想去的地方,全靠平时对路线的熟悉程度。RH134这章教的就是这条消防通道的每一道门、每一个转弯、每一个应急开关。把这张地图刻在脑子里,比多记几条命令值钱得多。