简介:这是一份讲解Linux系统启动流程的PPT课件,适合Linux初学者、运维人员和系统管理员学习使用。课件从Linux内核与Linux系统的概念区别、常见发行版引入,逐步展开PC架构主机从BIOS/UEFI固件到GRUB引导加载器,再到内核加载initramfs临时文件系统、挂载真实根文件系统的全过程。内容还专门剖析了initramfs的产生原因、dracut脚本自动生成方式以及手动制作方法,并对比传统SysV init与现代systemd的设计差异,附带systemctl、journalctl、hostnamectl等命令的实际用法。资源为单个PPT文件,整体大小约365KB,结构紧凑、图文并茂,适合课堂演示或自主复盘。已有283人学习。通过这套课件,读者能形成完整的启动流程知识框架,理解内核与根文件系统之间的依赖关系,并掌握使用systemd高效管理服务的方法,为后续系统调试和性能优化打下基础。
1. 为什么说 Linux 启动流程的 PPT 课件值得用启动故障来检验
《Linux系统启动流程PPT课件》这份资料,最直接的价值在于它把 CentOS 7 从按下电源键到进入登录界面的整条链路拆成了三段:BIOS/UEFI 到 GRUB、GRUB 到内核与 initramfs、initramfs 到 systemd。拿到一台启动异常的主机时,很多人第一反应是“系统崩了”,但实际上只要按这条链路逐层排查,大多数问题都能定位到某个具体环节。这份课件适合刚接触 Linux 运维的开发者,也适合做嵌入式网关、需要手工制造临时文件系统的工程师。它讲得不算深,但结构非常适合作为一张排错地图,你可以把每条知识点对应到实际命令,在虚拟机上重新复现一遍。只要有一台 CentOS 7 环境,就能跟着验证整个启动过程。
2. CentOS7 启动流程:从固件到 initramfs 的完整链路
课件第一部分就是 CentOS 7 的 PC 架构主机启动流程。这部分内容如果只看概念会觉得简单,但真实排障时,固件、引导器、内核三个层次必须分开对待,每一层都有完全不同的检查方式。
2.1 BIOS 与 UEFI 的引导差异:第一个程序是谁
按下电源键后,CPU 执行的第一段代码不是 Linux,而是主板固件。老式 BIOS 负责自检并读取磁盘 MBR,UEFI 则从 ESP 分区读取引导文件。两者不仅加载路径不同,还直接影响 GRUB 配置文件的位置。CentOS 7 的 GRUB2 在 BIOS 模式下读取/boot/grub2/grub.cfg,在 UEFI 模式下则读取/boot/efi/EFI/centos/grub.cfg。这个区别很关键,否则你辛苦修改配置后,重启依然走旧参数。
| 对比项 | BIOS 引导 | UEFI 引导 |
|---|---|---|
| 固件入口 | 扫描磁盘 MBR | 从 ESP 分区读取 .efi 文件 |
| GRUB 安装位置 | MBR 或 /boot | ESP 分区 |
| 配置文件路径 | /boot/grub2/grub.cfg | /boot/efi/EFI/centos/grub.cfg |
| 多盘场景 | 容易读错启动盘 | 相对稳定,但 ESP 损坏风险更高 |
检查当前系统是哪种引导方式,通常看/sys/firmware/efi目录是否存在。存在就是 UEFI,不存在就是 BIOS。这个判断决定了后续所有修改操作的对象。我在拿到一台故障机器时,第一件事就是执行这条命令,因为后面是要改/boot/grub2/grub.cfg还是/boot/efi/EFI/centos/grub.cfg,完全是两条路径。如果搞错,grub2-mkconfig 生成了文件,但启动固件根本不读那个位置,等于白改。
2.2 GRUB 加载内核与 initramfs:先有鸡还是先有蛋
GRUB2 本身不启动 Linux,它只负责把两个关键文件载入内存:以vmlinuz-*命名的内核镜像,和以initramfs-*.img命名的临时根文件系统。为什么不能只加载内核?因为内核为了控制体积,只内置最核心的代码,比如进程调度、内存管理、基础设备接口。磁盘控制驱动、文件系统驱动都以模块形式放在/lib/modules/里。但模块放在根文件系统中,如果根文件系统所在磁盘的驱动不在内核里,内核连根文件系统都读不到,更别提加载模块了。
initramfs 就是用来打破这个死循环的。GRUB 把 initramfs 载入内存后,内核将其解压为一块临时 rootfs,里面预置了启动阶段需要的驱动和工具。内核先运行 initramfs 中的/init,由它加载驱动、识别根设备,再挂载真实根文件系统。课件里那句“先有鸡还是先有蛋”说的就是这个场景:模块在文件系统里,文件系统又需要模块才能读取。
实际操作中,最常用的检查命令是:
# 查看内核启动参数,确认 root 设备和 init 路径 cat /proc/cmdline # 查看当前 grub.cfg 中包含的内核菜单项 grep "menuentry " /boot/grub2/grub.cfg # 修改 /etc/default/grub 后需要重新生成 grub.cfg grub2-mkconfig -o /boot/grub2/grub.cfgcat /proc/cmdline的输出通常类似BOOT_IMAGE=/vmlinuz-3.10.0-327.el7.x86_64 root=/dev/mapper/centos-root。如果 root 后面是 LVM 逻辑卷路径,说明 initramfs 阶段必须包含 LVM 相关模块;如果是UUID=...,则根设备识别依赖 UUID 匹配。grub2-mkconfig的-o参数指定输出路径,UEFI 机器上必须输出到 EFI 分区对应的 grub.cfg,否则不生效。
2.3 从 initramfs 切换到真实根:switch_root 的隐藏步骤
initramfs 中/init脚本的工作流程,本质上就是把“临时根”让位给“真实根”。具体步骤包括:挂载/proc、/sys、/dev,创建设备节点,识别根设备,加载磁盘和文件系统驱动,如果是 LVM 或加密卷还要激活卷组或解锁设备,最后用switch_root切换根目录。任何一步失败,都会出现类似Failed to mount /sysroot或者Cannot open root device的报错。
排查这种报错,我一般会在 GRUB 菜单按e编辑启动项,在内核命令行末尾加rd.break,让内核进入 initramfs 的调试 shell。在这个 shell 里可以手动执行:
# 查看内核是否识别了根磁盘 lsblk # 激活 LVM 卷组 lvm vgchange -ay # 手动挂载真实根,验证文件系统是否完整 mount /dev/mapper/centos-root /sysrootrd.break是 dracut 提供的调试开关,它会在 initramfs 完成驱动加载、准备切换到真实根之前停下来。这个状态下能直接看到磁盘设备、LVM 卷组和文件系统状态。如果lvm vgchange -ay成功,说明卷本身没问题,问题多半是 initramfs 没把 LVM 模块打进去;如果 mount 失败,那就要考虑根文件系统损坏或驱动不匹配。退出这个 shell 用exit继续启动流程,改过配置之后我会再执行一次reboot验证效果。
2.4 模块依赖:一个容易被忽略的 initramfs 细节
在课件对 initramfs 的介绍里,最容易忽略的是“驱动模块”不是单个文件,而是有依赖关系的。一个磁盘驱动可能需要先加载多个公共模块,比如 SCSI 通用层、PCI 总线驱动。dracut 生成 initramfs 时会解析modprobe的依赖关系,把整棵依赖树都打进去。但手动用 cpio 打包时,如果只拷贝了某一个.ko文件,启动时modprobe会报unknown symbol或module not found。
查看模块依赖关系用:
# 查询某个内核模块的依赖 modprobe --show-depends ext4 # 查看 initramfs 里是否包含了该依赖 lsinitrd -m /boot/initramfs-$(uname -r).img | grep "drivers/md/dm-mod"这一节延伸出来的结论是:除非你非常清楚目标硬件需要的完整驱动链,否则生产环境尽量用 dracut,而不是手工 cpio。课件里提到手动制作 initramfs 的例子,更多是用于说明 initramfs 本质是什么,而不是推荐你在服务器上手工造镜像。
3. initramfs 制作:dracut 自动生成与 cpio 手动打包
课件里明确说,initramfs 安装完系统后由 dracut 脚本自动生成,也可以使用 cpio 命令手动制作。这一章讲清楚两种方式的适用场景和实践参数。
3.1 dracut 的自动生成逻辑与常用参数
CentOS 7 默认使用 dracut 生成 initramfs。它扫描当前内核目录/lib/modules/$(uname -r),结合机器实际硬件信息,生成一份包含必要驱动和工具的最小镜像。默认文件位置是/boot/initramfs-<内核版本>.img。这份镜像和当前机器绑定,换到另一台机器不一定能启动,因为驱动集合完全不同。
重建 initramfs 的标准命令:
# 用当前内核版本重新生成,-f 覆盖已有文件 dracut -f /boot/initramfs-$(uname -r).img $(uname -r) # 指定内核版本生成,适合为其他内核构建镜像 dracut --kver 3.10.0-1160.el7.x86_64 -f /boot/initramfs-3.10.0-1160.el7.x86_64.img第一个命令中,$(uname -r)返回当前运行的内核版本,例如3.10.0-327.el7.x86_64。第二个参数指定从哪个内核版本的模块目录生成。如果不指定,dracut 默认用当前内核。--kver参数适用于你刚安装了新内核、但还没有重启进新内核的场景。此时uname -r还是旧版本,必须手动指定新内核的版本号,否则生成出来的镜像和实际启动的内核不匹配。
如果要在 initramfs 中强制追加某些驱动,用--add-drivers:
dracut -f --add-drivers "virtio_net" /boot/initramfs-$(uname -r).img $(uname -r)这条命令适合虚拟化平台中网卡没有被自动识别的情况。注意,--add-drivers只是在 initramfs 启动阶段预先加载驱动,并不改变内核模块本身。如果你希望某些模块永远不被打进去,可以用--omit-drivers排除。
3.2 验证 initramfs 内容:用 lsinitrd 而不是猜
生成或重建之后,不要急着重启。先用lsinitrd检查 initramfs 里到底有什么。这个命令会解压 initramfs 镜像并列出文件结构,比直接解包到临时目录快得多。
# 列出所有文件 lsinitrd /boot/initramfs-$(uname -r).img # 只看内核模块部分,并过滤磁盘相关驱动 lsinitrd -m /boot/initramfs-$(uname -r).img | grep -E "drivers/scsi|drivers/block|lvm"-m只显示模块相关路径。我每次重建完都会执行最后一条,确认根文件系统驱动在。比如 xfs 文件系统就要看有没有xfs.ko,LVM 根分区就要看有没有dm-mod.ko。宁可多花一分钟做验证,也不要重启后才发现 initramfs 里缺了关键驱动。
3.3 用 cpio 手动制作最小 initramfs
课件里提到可以用 cpio 命令手动制作,这个场景在嵌入式设备或者特殊网关项目里很常见。手动制作的核心有两点:一是目录结构,二是 init 脚本。内核解压 initramfs 后,如果没有指定rdinit,默认会查找根目录下的init作为第一个用户态程序。
下面是一个最小示例,用静态编译的 busybox 提供基础命令:
# 1. 准备目录结构 mkdir -p /tmp/myinitramfs/{bin,dev,proc,sys} # 2. 复制静态编译的 busybox cp /bin/busybox /tmp/myinitramfs/bin/ # 3. 创建 init 脚本 cat > /tmp/myinitramfs/init <<'EOF' #!/bin/busybox sh mount -t proc proc /proc mount -t sysfs sys /sys echo "initramfs is up" exec /bin/busybox sh EOF chmod +x /tmp/myinitramfs/init # 4. 打包成 cpio 并压缩 cd /tmp/myinitramfs find . -print0 | cpio --null -H newc -o --owner root:root 2>/dev/null | gzip -9 > /tmp/myinitramfs.img逻辑说明:find . -print0将当前目录下所有文件以 null 分隔输出,避免文件名包含空格时出错。cpio -H newc指定 newc 格式,这是内核唯一能识别的传统 cpio 格式。--owner root:root将所有文件所有者设置为 root,否则内核在加载时可能因权限问题拒绝执行 init。最后通过管道交给 gzip 压缩,生成/tmp/myinitramfs.img。
这个示例只能验证 initramfs 能启动到 shell,不能真正挂载真实根文件系统,因为它没有设备驱动、没有分区逻辑、也没有 switch_root。但作为理解 initramfs 原理的实验,已经完全够用。如果你想在真实机器上测试,可以在 GRUB 命令行中临时指定 initrd 文件,并加上rdinit=/init参数,确认它能进到 init 脚本。
3.4 手动制作与 dracut 的边界
手动 cpio 的优势是可控,缺点是模块依赖很难手工维护。一个驱动背后往往跟着一串依赖项,手工拷贝非常容易漏。课件里的例子更适合教学,用来解释 initramfs “本质上就是一块内存中的小型文件系统”。在生产环境,特别是 RHEL/CentOS 系列,永远优先用 dracut。如果确实需要定制,也应该在 dracut 的配置目录下做增量修改,而不是推翻重建。
dracut 的配置目录在/etc/dracut.conf.d/,可以写配置文件来增加默认参数,比如:
cat > /etc/dracut.conf.d/custom-drivers.conf <<'EOF' add_drivers+=" nvme vmxnet3 " omit_drivers+=" floppy " EOFadd_drivers和omit_drivers会作为默认参数合并到每次 dracut 执行中。这种方式比每次手工敲--add-drivers更可靠,也方便团队多台机器保持一致。
4. systemd 功能介绍:从 SysV init 到并发启动
课件最后一部分是 systemd。它拿 SysV init 做对比,解释了为什么要用 systemd 替代传统 init。理解这段内容,对排查启动后服务不起来的场景特别有用。
4.1 SysV init 的串行启动瓶颈
老版本 CentOS 的 PID 1 是init进程,依赖/etc/inittab和/etc/init.d/下的大量 shell 脚本。启动服务用service network start或/etc/init.d/network start。这种方式看起来简单,但命令本质是按脚本顺序一条条执行,前一个服务没启动完,后一个只能等待。课件直言它的缺点是“服务顺序启动,过程较慢,不能根据需要来启动服务”。
更麻烦的是,SysV init 的依赖关系靠脚本名的数字前缀控制,比如S55sshd、S80postfix。一旦有人调整了编号,依赖关系就乱了。只要有一个脚本阻塞,后面全卡住。这种调度方式在物理机时代还能接受,到了云主机和容器时代,启动速度的劣势就非常明显。
4.2 systemd 的并发启动与按需启动
systemd 的守护目标是整个系统,d代表 daemon。它把所有可管理对象抽象成 Unit,包括 service、socket、target、device 等。Unit 文件通过After=、Requires=、Wants=声明依赖,systemd 先构建依赖图,再把没有相互依赖的服务并行拉起。这就是并发启动提速的根本原因。
按需启动是另一个关键能力。传统 init 会一次性把所有服务都启动,systemd 则支持 socket 激活。比如一个服务平时不运行,只有收到外部请求时才被拉起,或者某个单元被另一个单元引用时才开始加载。这能减少系统空转的资源消耗。
分析启动瓶颈可以用:
# 查看启动最慢的 10 个服务 systemd-analyze blame | head -10 # 查看某个服务的启动依赖链 systemd-analyze critical-chain sshd.servicecritical-chain会输出该服务启动前必须等待的单元列表,这些等待项往往是启动耗时的来源。比如网络未就绪、磁盘挂载延迟、或者某个 socket 服务超时。
4.3 用 systemctl 管理服务:一个最小 Unit 示例
课件给出了systemctl start apache.service与/etc/init.d/apache start的对照。这里用一个虚构的 demo 服务来说明 Unit 文件的写法。在/etc/systemd/system/demo.service写入:
cat > /etc/systemd/system/demo.service <<'EOF' [Unit] Description=Demo background service After=network.target [Service] Type=simple ExecStart=/usr/local/bin/demo --foreground Restart=on-failure RestartSec=3 [Install] WantedBy=multi-user.target EOF参数说明:Type=simple表示 systemd 认为该进程启动后就进入运行状态,进程本身必须在前台运行,不能自己 fork 到后台。ExecStart必须写绝对路径和完整参数。Restart=on-failure在进程异常退出时自动拉起,RestartSec=3是重启前的等待秒数。WantedBy=multi-user.target表示在进入多用户模式时启用该服务。
写完文件后执行:
systemctl daemon-reload systemctl enable demo.service systemctl start demo.service systemctl status demo.servicedaemon-reload是必须的。systemd 在启动时读取了旧的 Unit 配置,不重载就不会感知新文件。enable只是在 target 下创建软链接,不会立刻启动。start才真正拉起进程。这里最常见的翻车点是:服务程序是 daemon 类型,父进程启动后会 fork 子进程然后退出,而Type=simple要求 ExecStart 进程一直活着,结果 systemd 认为启动失败,反复重启。
4.4 journalctl 与 hostnamectl:排错和系统信息查看
systemd 把日志集中到了 journald,查看日志不再需要去翻/var/log/messages。常用命令:
# 查看本次启动的所有日志 journalctl -b # 只看某个服务单元的日志,并带时间戳 journalctl -b -u demo.service -o short-precise # 实时跟踪日志 journalctl -f-b表示本次启动,-u过滤指定 Unit,-o short-precise输出微秒级时间戳。服务启动失败时,优先执行第二条命令看日志,比盲猜原因高效得多。
主机名管理也归 systemd 管:
# 查看当前主机名和系统信息 hostnamectl # 设置主机名 hostnamectl set-hostname node01设置后立即生效,不需要重启。
| 操作 | SysV init | systemd |
|---|---|---|
| 启动服务 | service httpd start | systemctl start httpd.service |
| 停止服务 | service httpd stop | systemctl stop httpd.service |
| 查看日志 | tail -f /var/log/messages | journalctl -f |
| 开机自启 | chkconfig httpd on | systemctl enable httpd.service |
这张对照表是课件没有写但实际迁移时一定会用到的。CentOS 7 上很多老脚本还在用service,它会被兼容层转发到 systemctl,但依赖旧版/var/log/messages的日志排查方法已经过时了。
5. 避坑:启动流程调试中的五个常见问题与排查记录
以下五条都是实际排查中踩过的坑,按“现象 → 原因 → 解决”记录,可以收藏备用。
5.1 改了 /etc/default/grub 的启动参数,重启后不生效
现象:在/etc/default/grub的GRUB_CMDLINE_LINUX里加了console=ttyS0,执行reboot后/proc/cmdline看不到这个参数。
原因:GRUB2 启动时读取的是/boot/grub2/grub.cfg,/etc/default/grub只是配置模板。没有执行grub2-mkconfig,改动的参数永远不会进入实际启动项。UEFI 模式下还可能因为输出路径写错,生成的文件没有被固件读取。
解决:先确认引导模式,再执行grub2-mkconfig -o /boot/grub2/grub.cfg,UEFI 环境输出到/boot/efi/EFI/centos/grub.cfg。执行后立即用grep console /boot/grub2/grub.cfg验证参数确实写入,不要相信reboot后才发现。
5.2 dracut 重建 initramfs 后,启动时仍报找不到根设备
现象:内核启动后卡在Failed to mount /sysroot,但用系统安装光盘进入救援模式可以正常挂载根分区。
原因:initramfs 里缺少根文件系统驱动,或 LVM 模块没有被打进去。常见诱因是根分区从/dev/sda1变成了/dev/nvme0n1p1,设备名变化,而 GRUB 命令行的 root 参数还写死了旧设备名;也可能是 dracut 生成时使用了--omit-drivers排除了必需驱动。
解决:先用lsinitrd -m /boot/initramfs-$(uname -r).img | grep lvm检查 LVM 模块,缺少就用dracut -f --add-drivers "lvm2"重新生成。同时把 GRUB 的 root 参数和/etc/fstab都改成 UUID 或/dev/mapper/xxx,避免设备名漂移。
5.3 手动制作的 initramfs 无法进入 init shell
现象:用 cpio 打包的 initramfs 启动后,内核报Kernel panic - not syncing: Attempted to kill init!,或者直接黑屏。
原因:init 脚本没有可执行权限,或者脚本第一行解释器路径写错。另一个常见问题是 cpio 打包时文件所有者不是 root,内核拒绝执行。如果二进制依赖动态库而动态库没有一起打包,也会出现类似现象。
解决:检查ls -l /tmp/myinitramfs/init,确认有x权限。脚本第一行必须指向真实的解释器,比如#!/bin/busybox sh。拷贝的二进制尽量用静态编译版本,或者把动态库一起放进对应目录。打包命令必须带--owner root:root,并用-H newc指定格式。
5.4 systemd 服务启动 90 秒超时
现象:systemctl start demo.service执行后长时间不返回,最终报Start request repeated too quickly或 Timed out,但查看进程时服务实际在运行。
原因:Unit 里的Type写错。服务是 daemon 型,启动后父进程退出、子进程驻留,却写了Type=simple,systemd 认为 ExecStart 进程退出就是服务结束,于是反复拉起并等待超时。
解决:如果服务进程会 fork 到后台,用Type=forking,并配置PIDFile=/run/xxx.pid指向子进程 PID;如果保持前台运行,用Type=simple。服务需要等网络就绪时,加After=network-online.target和Wants=network-online.target,不要只写After=network.target。
5.5 启动后直接进入 emergency mode
现象:开机字符界面显示Welcome to emergency mode!,提示某个文件系统无法挂载,输入 Ctrl+D 后能继续启动,但每次重启都会出现。
原因:/etc/fstab中存在一个挂载项,对应的设备在启动阶段找不到,systemd 为保护系统进入应急模式。常见于挂载了临时数据盘或 U 盘,后来设备拔掉了但 fstab 没有更新;也有可能 UUID 写错。
解决:查看 emergency 提示中的设备或 UUID,用blkid对比修正。如果确实是临时设备,直接注释掉对应 fstab 行,执行systemctl daemon-reload后重启。遇到这种情况不要急着重装系统,先怀疑 fstab。
6. 让启动流程课件变成排查手册:内核升级后的 initramfs 重建与验证
最后分享一个我每次都会执行的固定流程:内核升级后重建并验证 initramfs。很多人升级完内核就重启,翻车后才想起来/boot目录下没有对应版本的 initramfs。虽然内核 RPM 安装时通常会触发 dracut 自动生成,但默认生成的镜像不一定适配你的存储环境,尤其是使用 LVM、软件 RAID 或特殊网卡的机器。
我的习惯是,在重启之前走完下面四步:
# 1. 列出所有已安装内核,确认要启动的新版本号 rpm -q kernel # 2. 为指定内核版本重建 initramfs,注意不要用 uname -r dracut -f /boot/initramfs-3.10.0-1160.el7.x86_64.img 3.10.0-1160.el7.x86_64 # 3. 验证关键驱动是否在镜像内 lsinitrd -m /boot/initramfs-3.10.0-1160.el7.x86_64.img | grep -E "xfs|lvm|nvme" # 4. 检查 GRUB 默认启动项是否正确 grub2-editenv listrpm -q kernel的输出会包含所有已安装内核版本。如果你刚安装新内核但还没有重启,uname -r返回的仍然是旧版本号,这时候用uname -r去重建,生成的就是旧内核的 initramfs,新内核启动时自然找不到。我曾踩过一次这个坑,当时在新机器上装完内核,直接用uname -r生成 initramfs,重启后卡在 grub 提示符下,后来才明白第二个参数一定要显式指定新内核版本。
grub2-editenv list可以查看当前 GRUB 默认启动项。如果新内核版本没有排在第一,重启后还是会进旧内核。这步容易被忽略,但它决定你到底能不能验证刚才生成的 initramfs。
从那以后,我每次升级内核都强制走一遍:先rpm -q kernel确认版本,再dracut -f重建,然后用lsinitrd -m检查磁盘驱动,最后看一眼grub2-editenv list,确认默认启动项是新的内核。这套流程同样适用于把这份启动流程课件里的知识点落地的过程,课件负责讲链路,命令负责验证链路。希望帮到你。
本文还有配套的精品资源,点击获取