又到了RH134的知识点整理时间。第10章控制启动过程这节很多人嫌杂一会儿systemd一会儿GRUB一会儿又是target背完就忘。但说句实在话想搞懂Linux系统到底是怎么从按下电源键走到登录界面的这一章反而是最值得反复琢磨的内容。这篇文章我不打算按教材顺序机械罗列而是用一条完整的启动链条把散落的知识点串起来再加上我自己在真实机器上反复试过、踩过的坑。如果你正在备考相关认证或者刚接触RHEL系系统、遇到启动类故障不知道怎么下手这篇应该对你有用。1. 启动链条拆解固件、GRUB、内核与systemd的交接1.1 POST与引导设备选择从BIOS到UEFI运维做久了总会遇到一次服务器开机黑屏卡在某个logo的场面。要定位这类问题必须先搞清楚启动的最开始几十秒发生了什么。机器通电后的第一阶段是做硬件自检POSTCPU、内存、磁盘控制器这些基础硬件都要过一遍。之后固件会按预设的设备顺序找可引导设备。老一代的BIOS是通过查找硬盘主引导记录MBR末尾的启动标志来定位引导代码现在的主流是UEFI它直接去读取EFI系统分区ESP里的.efi引导文件比如grubx64.efi。UEFI的好处是更规范、支持GPT分区、启动速度也快一些。这里有个很常见的现象一台机器装了双系统结果开机总是直接进另一个其实不一定是系统坏了很可能是固件引导顺序里排在前面的是另一个盘。我之前给一台测试机排障就是插着U盘没拔导致UEFI启动顺序里USB排第一机器每次开机都卡在引导选择界面。拔掉U盘问题立刻消失。所以遇到开机不进系统的第一件事不是重装系统而是先进固件界面看启动顺序。1.2 GRUB2到底加载了什么固件找到引导程序之后控制权交给GRUB2。RH134这章重点就是要理解GRUB2的工作它本身是一个小型操作系统负责呈现启动菜单、选择内核、并把内核和初始RAM磁盘加载进内存。GRUB2的主配置文件在/boot/grub2/grub.cfg。这个文件一般不建议手动改它是由grub2-mkconfig命令根据/etc/default/grub和/etc/grub.d/目录下的脚本自动生成的。如果你直接手改了grub.cfg下次内核更新或者重新生成配置时你的改动很可能被覆盖白忙活一场。grub.cfg里的每个菜单项都带着一组关键信息内核文件vmlinuz-xxx、初始RAM磁盘initramfs-xxx.img以及一串内核命令行参数。其中rootUUIDxxx用来指定根文件系统所在分区。我建议大家在排障时先学会看这个文件它能告诉你系统每次开机到底选了哪个内核、传了什么参数这些信息在后续排查启动问题时非常有用。1.3 initramfs存在的理由很多新手会问内核为什么不直接挂载根文件系统非要先加载一个initramfs这其实是个经典的鸡生蛋、蛋生鸡问题。内核要挂载真正的根文件系统就需要磁盘控制器驱动和文件系统驱动。但这些驱动文件放在哪里通常就在根文件系统里。不加载驱动就找不到根找不到根就加载不了驱动。initramfs就是用来解开这个死结的它是一份压缩的小型根文件系统里面预先塞进了开机挂载真实根所需的驱动模块、udev规则和一些基础工具。启动时内核先解压initramfs到内存以它为临时根跑起来然后在里面加载真正的磁盘驱动和文件系统模块最后把真实的根文件系统挂载到/sysroot再做一次根切换转向真实的根环境继续启动。所以如果系统突然换了新的磁盘控制器或者根文件系统所在的文件系统类型需要特殊模块甚至只是initramfs损坏都可能卡在这一步。重建initramfs的命令其实很简单比如说用dracut -f /boot/initramfs-$(uname -r).img $(uname -r)这类操作在救援场景中经常用到。1.4 systemd接管后的第一个动作根切换完成后内核执行/sbin/init。在现代RHEL系系统上/sbin/init是指向systemd的符号链接所以真正接管用户空间的是systemd它从此成为进程号唯一为1的进程。systemd接管后的工作不再是像SysV时代那样一个脚本一个脚本地顺序执行而是把所有开机任务抽象成单元Unit根据单元之间的依赖关系构建一张启动图然后并行启动互不冲突的任务。这也是为什么现在开机速度普遍比老系统快很多。在这一步判断启动卡在哪里的常用手段有两种一是去掉内核参数里的rhgb和quiet让系统把启动日志直接打到屏幕上二是等系统勉强起来或进入救援环境后用journalctl -b -1查看上一次启动的完整日志。前者适合直观观察卡点后者适合事后复盘分析。两者结合基本能覆盖大部分启动排障场景。2. systemd单元与依赖为什么服务能同时启动2.1 单元文件的结构与优先级把systemd的启动逻辑拆开看核心就在单元文件。单元文件通常放在两个位置/usr/lib/systemd/system/是软件包安装时自带的属于出厂配置/etc/systemd/system/是管理员自己创建的或者用来覆盖出厂配置。RHEL系的规则是/etc下的同名单元优先级更高。那日常维护时想改一个服务的行为应该怎么改我强烈建议不要整体复制单元文件到/etc下而是用override配置目录在/etc/systemd/system/对应服务名.service.d/下创建一个override.conf只写需要覆盖的字段。好处很明显软件包升级后上游单元文件的修复和安全更新仍然能通过/etc下未覆盖的部分生效不会因为本地整体文件把新改动全部挡住。单元文件内部结构分为几段。每个单元通用的是[Unit]段里面写Description描述、After/Before顺序、Wants/Requires依赖服务单元有[Service]段定义ExecStart启动命令、ExecStop停止命令、Restart重启策略[Install]段则决定服务在什么target下被enable最常用的就是WantedBymulti-user.target。2.2 After和Requires顺序与依赖必须分开看单元文件里最容易被搞混的就是After/Before与Wants/Requires这两组字段。我见过不少运维把After当成必须等这个服务启动成功实际上完全不是一回事。After和Before只定义启动顺序不意味着对方一定要启动。比如A设置了AfterB意思是如果B在当前启动集合里那就先启动B再启动A但B哪怕启动失败了A还是会照常启动因为没有硬性依赖关系。Wants和Requires才是定义依赖的Requires是硬依赖被依赖方失败依赖方也会失败Wants是软依赖被依赖方失败依赖方不受影响。所以一个严谨的服务单元既要表达我依赖它又要表达我需要的顺序那就要同时写After和Wants/Requires。拿典型的Web服务来说它需要网络就绪才能正常监听端口单元文件里通常就有Afternetwork-online.target同时network-online.target又依赖网络管理服务systemd靠着这串依赖图就能自动决定谁先谁后。2.3 systemctl常用操作与为什么服务又活了理解单元文件之后systemctl的命令就很好串起来了。start/stop/restart只影响当前运行状态不影响下次开机enable/disable控制的是开机是否自启不影响当前状态还有一个常被忽略但非常实用的mask/unmask会把服务彻底屏蔽相当于把单元文件指向/dev/null就算别的服务声明了Wants依赖它systemd也不会把它拉起来。我调过的一个内部系统案例就是栽在mask没用好上。当时有个服务A在单元里Wants了服务B运维想把B停掉执行了systemctl disable B当时看着是停了结果下次开机B又出现在进程列表里。原因就是disable只去掉了B自身的自启设置但服务A的依赖仍然会把B拉起。后来改成systemctl mask BB才真正死透。所以判断服务为什么复活先看谁在依赖它再看自己的enable状态。2.4 用journalctl回看启动过程排障启动问题时只看当前运行状态的journal是不够的。系统每次启动的日志都按boot分区保存几个实用命令值得记下来journalctl -b 查看本次启动日志。journalctl -b -1 查看上一次启动日志这在系统起不来、只能进救援环境时特别有用。journalctl -u xxx.service 只看某个服务单元的日志。systemctl list-units --failed 直接列出启动失败的服务。有一次我处理一台启动慢且中途卡住的机器就是在rescue环境里用journalctl -b -1找到了一个挂载单元反复超时顺着日志里的设备路径检查才发现是fstab里一个不再存在的旧分区配置没删干净。这种问题如果不看上次启动日志单靠肉眼很难定位。3. target目标单元运行级别的后现代替代品3.1 默认target与运行时切换SysV时代有个运行级别runlevel的概念0到6各有含义。现代RHEL系已经用target取代了这套机制。target本质上是一组单元的集合点类似一个分组标签把开机时该启动的一堆服务聚合在一起。比如multi-user.target会触发网络、SSH、cron等服务graphical.target又在multi-user的基础上追加图形桌面相关的服务。查看系统默认target用systemctl get-default修改默认target用systemctl set-default。比如我想让一台带桌面的服务器开机直接进命令行就执行systemctl set-default multi-user.target下次开机就不会再进入图形界面了。运行时临时切换target用systemctl isolate比如systemctl isolate multi-user.target可以在不重启的情况下直接退出图形界面进入命令行。但注意isolate并不是每个target都能用只有单元文件里声明了AllowIsolateyes的target才支持否则会拒绝执行。这个设计是为了避免误操作把一个正常运行的target切碎。3.2 runlevel N与target的对应关系尽管运行级别已经退出历史舞台系统还是保留了兼容映射方便老脚本和习惯runlevel的运维。映射关系是这样runlevel3.target 对应 multi-user.targetrunlevel5.target 对应 graphical.targetrunlevel1.target 对应 rescue.targetrunlevel0.target 对应 poweroff.targetrunlevel6.target 对应 reboot.target在旧服务脚本里用chkconfig管理开机启动的服务到了systemd时代就把WantedBy写在对应target下。比如一个服务希望开机进入字符界面时启动单元文件里写WantedBymulti-user.target然后执行systemctl enable就会在multi-user.target.wants目录里生成一个软链接。所以搞懂target之间的依赖就搞懂了服务是怎么被拉起来的。3.3 依赖链排查list-dependencies的用法target和target之间、target和服务之间经常有依赖。查询这些关系有一个很顺手的命令systemctl list-dependencies。默认只列出一级依赖想看完整链条就加--all参数。比如systemctl list-dependencies graphical.target输出里就会包括multi-user.target、NetworkManager.service、sshd.service等一大串。反过来当一个服务无法启动时也可以用systemctl list-dependencies xxx.service查看它依赖了谁再逐个检查被依赖单元的状态。我排障遇到过一次诡异现象systemctl status显示sshd没起来但直接执行systemctl start sshd又能成功。最后用list-dependencies一查发现sshd单元里有个Afternetwork.target而network.target在这个系统上被一个自定义服务拖住了导致sshd在启动序列里一直排队等。问题不在sshd本身而在依赖链上游。这类经验说明排障不要只盯着出问题的服务本身往依赖链上下游多看两级往往更快。4. 起不来机器时的求生路线救援模式与重置密码4.1 rescue与emergency什么样的故障选哪个系统启动到一半挂了进不了正常登录界面这时候先别急着重装。RHEL系提供了两个特殊targetrescue.target和emergency.target。rescue.target可以理解为最小可用环境它会尽量挂载根文件系统为可读写尝试挂载其他文件系统并启动少量基础服务。这个模式适合大多数启动故障比如修复fstab写错、重建initramfs、重置root密码。emergency.target则更极端只挂载根文件系统为只读几乎不启动任何服务适合根文件系统本身都报错、基础挂载都无法完成的场景。选择标准其实很简单如果是某个服务坏了导致进不了系统优先rescue因为该有的工具依赖都在如果是根分区挂载失败、文件系统受损才考虑emergency。能进rescue就别先进emergency后者环境太简陋能做的事有限。4.2 从GRUB菜单手动进入救援target实际操作上物理机和虚拟机从GRUB进入救援模式的过程是这样开机出现GRUB菜单时选中默认的内核条目按e进入编辑界面。找到以linux或linuxefi开头的行这是内核启动参数所在行。在该行末尾追加systemd.unitrescue.target或者更简洁的single不过single最终也会指向rescue。按Ctrl-x启动系统会进入带root shell的救援环境。输入root密码后就可以执行修复操作了。这里要提醒一个容易翻车的地方RHEL 8/9在UEFI方式下该行通常显示为linuxefi而不是linux。追加参数要注意位置必须加在这一行的末尾别换成下一行否则GRUB解析配置失败会直接回到菜单什么也进不去。4.3 重置root密码的完整流程与常见翻车点root密码忘了或者root被意外锁定这是每个管理员早晚会遇到的事。RH134这章讲的方法非常实战步骤看起来不多但每一步都有坑。先进入GRUB编辑界面找到linux/linuxefi开头那一行在末尾追加rd.break然后按Ctrl-x启动。rd.break的意思是让内核在initramfs阶段停下这时候根文件系统已经被挂载到/sysroot了但是我们还没进入真正的根环境。接着执行mount -o remount,rw /sysroot把根重新挂载成可写。为什么要这一步因为启动早期根是只读挂载的如果不先改成可写下一步进去之后什么都改不了。然后执行chroot /sysroot进入真实的根文件系统执行passwd root给root设置新密码。这里还有关键一步在SELinux启用的系统上建议在chroot环境里执行touch /.autorelabel。如果不做这一步下次开机时SELinux可能因为/etc/shadow的安全上下文标签不对直接拒绝认证服务读取这个文件结果就是密码改了却仍然无法登录。之所以要touch这个标记文件是为了触发系统在下次启动时对文件系统做一次安全上下文重标。做完密码修改后exit退出chroot再exit退出挂载系统会自动继续启动流程。重启后就可以用新密码登录了。4.4 救援完成后的善后操作救援模式修完别直接重启了事。正确做法是确认修复效果和清理现场。如果是通过systemd.unitrescue.target进入的在救援shell里输入exit系统通常会自动继续完成启动流程。趁这个机会观察启动日志看之前报错的服务是否恢复正常。用journalctl -b -1看一眼上次启动的报错是否还在比什么都靠谱。另外如果之前为了修问题改过GRUB参数比如追加了rd.break或者systemd.unitrescue.target这些只是临时参数重启后就不会再带上了通常不用专门清除。但如果确实把参数写进了/etc/default/grub那一定要记得改回来再重新生成grub.cfg。我见过有人排查完问题忘了清理永久参数导致后面每次开机都进救援模式白白折腾一上午。5. 内核启动参数与GRUB配置想改启动行为先懂这套流程5.1 看懂常用内核启动参数RH134这章有一半内容其实是在教读者认识内核参数。常见参数的用途值得整理一下rhgb和quiet默认会加上的两个参数让开机过程显示图形化的logo并隐藏细节日志。排障时通常要临时去掉才能直观看到启动卡在哪一步。consolettyS0把启动日志输出到串口设备。适合服务器通过串口控制台管理的场景但没配串口的机器不要乱加。systemd.unitrescue.target / emergency.target临时指定启动target。rd.break在initramfs阶段中断进入root shell适合重置密码等操作。selinux0 enforcing0临时关闭SELinux一般用来判断是不是SELinux策略导致服务起不来定位完就要改回去。这些参数本身不复杂但要注意区分临时生效和永久生效。临时生效就是在GRUB编辑界面里改一下只对本次启动有效永久生效就要改配置文件并重新生成grub.cfg。5.2 修改GRUB配置的正确姿势永久修改内核启动参数的标准流程是这样的编辑/etc/default/grub找到GRUB_CMDLINE_LINUX...这一行。在双引号内加入或删除需要的参数。执行grub2-mkconfig -o /boot/grub2/grub.cfg重新生成主配置。如果是UEFI引导部分RHEL版本还要执行同样的命令输出到/boot/efi/EFI/redhat/grub.cfg。这里最大的坑是漏掉第3步。很多人改了/etc/default/grub就觉得完事了重启后参数没变就开始怀疑GRUB坏了其实只是没有重新生成配置。grub.cfg是个静态文件不会自动感知default/grub的修改。这个套路要记牢任何GRUB配置变更最终都要落到grub.cfg才算数。还有一个小细节生成配置时会扫描系统里的内核版本如果你同时装了多个内核grub.cfg里会列出多个菜单项。想调整开机默认选中哪个可以用grub2-set-default指定或者用grub2-editenv list查看当前的saved_entry。这些零碎命令考试不一定会考但排查时确实常碰到。5.3 实战演练命令行启动与关闭图形界面最后把前面所有知识点串成一个实际场景。假设有一台带图形桌面的测试服务器想改成开机直接进命令行同时排障期间想看到完整启动日志。第一步执行systemctl set-default multi-user.target把默认target从graphical.target改成multi-user.target这样开机就不会再进入图形桌面了。第二步编辑/etc/default/grub在GRUB_CMDLINE_LINUX里去掉rhgb和quiet。如果你的系统当前没加这两个参数那就不用动保证屏幕上能输出详细日志就行。第三步执行grub2-mkconfig -o /boot/grub2/grub.cfg重新生成配置。这一步不能省。第四步重启验证。重启后系统会直接进入命令行登录界面而且开机过程中会一条条打印systemd启动服务的状态。如果某个服务启动失败屏幕上一眼就能看到红字提示。等排障结束想把安静的图形开机体验找回来再把rhgb quiet加回去重新生成grub.cfg即可。整个过程看起来很简单但把target切换、GRUB参数修改、配置重新生成这三个知识点全部串起来了练一次比背十遍命令都管用。