Linux 系统启动原理
CentOS 7 启动过程
现代计算机系统是硬件与软件的复杂组合。从断电状态开始,到拥有登录提示符的运行中系统,这需要大量的硬件和软件配合工作。
以下列表从较高层面概述了启动CentOS 7的物理X86_64系统所涉及的任务。


-
计算机接通电源。系统固件(现代UEFI或更旧的BIOS)运行开机自检(POST),并开始初始化硬件。
配置: 在系统启动早期,通过按特定的组合键,例如F2,配置系统固件。
-
系统固件搜索启动设备,根据固件配置的顺序搜索启动磁盘上的主启动记录(MBR)。系统固件从磁盘读取boot loader,然后将系统控制权交给boot loader,boot loader是GRand UnifiedBootloader version 2(GRUB2)。
配置: 使用 grub2-install 命令进行配置,它将安装 GRUB2 作为磁盘上的启动加载器。
-
GRUB2从/boot/grub2/grub.cfg文件加载配置并显示一个操作系统菜单,可以从中选择要启动的系统。
配置: 使用 /etc/grub.d/ 目录、/etc/default/grub 文件和 grub2-mkconfig 命令进行配置,以生成/boot/grub2/grub.cfg 文件。
-
boot loader根据选定条目的配置,从磁盘中加载kernel和initramfs,并将它们放入内存中。initramfs是一个存档,其中包含启动时所有必要硬件的内核模块、初始化脚本等等。
boot loader将控制权交给kernel,并同时将启动项的内核参数、initramfs在内存中的位置传递给kernel。内核在initramfs中找到所 有硬件驱动程序,并初始化这些硬件。
配置: 使用 /etc/dracut.conf.d/ 用录、dracut 命令和 lsinitrd 命令进行配置,以检查 initramfs 文件。
- initramfs执行/sbin/init,作为PID 1。在CentOS 7中,/sbin/init是一个指向systemd的链接。
配置: 使用内核参数 init=command 配置系统初始化程序。
-
随后,systemd会加载从内核命令行传递的target或者加载系统配置的default.target,该目标通常启动一个基于文本的登录或图形登录屏幕。
**配置:**使用systemctl设置默认target。
-
default.target依赖sysinit.target,sysinit.target用于初始化系统,例如读取/etc/fstab挂载文件系统,激活systemd-journald等。
**配置:**使用/etc/fstab配置文件系统开机自动挂载。
- default.target还会激活开机启动的单元。
**配置:**使用systemctl设置开机启用服务。
- default.target还会激活getty.target,该target将打开tty1终端用于用户登录。
系统target**
systemd使用类型为target的单元来分组不同单元,例如multi-user.target包涵chronyd.service、
crond.service、firewalld.service等服务。
target还可以包涵其他target,例如graphical.target包涵multi-user.target,multi-user.target包涵
basic.target,basic.target包涵sysinit.target。
使用以下命令查看target之间依赖关系:
[root@localhost ~]# systemctl list-dependencies graphical.target
graphical.target
○ ├─display-manager.service
○ ├─systemd-update-utmp-runlevel.service
○ ├─udisks2.service
● └─multi-user.target
● ├─auditd.service
● ├─crond.service
● ├─dbus.service
● ├─irqbalance.service
● ├─kdump.service
○ ├─mdmonitor.service
● ├─NetworkManager.service
○ ├─restorecond.service
● ├─rngd.service
● ├─rsyslog.service
● ├─sshd.service
● ├─systemd-ask-password-wall.path
● ├─systemd-logind.service
○ ├─systemd-update-utmp-runlevel.service
● ├─systemd-user-sessions.service
● ├─systemtap.service
● ├─tuned.service
● ├─basic.target
● │ ├─-.mount
● │ ├─tmp.mount
● │ ├─paths.target
● │ ├─slices.target
● │ │ ├─-.slice
● │ │ └─system.slice
● │ ├─sockets.target
● │ │ ├─dbus.socket
● │ │ ├─dm-event.socket
● │ │ ├─pcscd.socket
● │ │ ├─systemd-coredump.socket
● │ │ ├─systemd-initctl.socket
● │ │ ├─systemd-journald-dev-log.socket
● │ │ ├─systemd-journald.socket
● │ │ ├─systemd-udevd-control.socket
● │ │ └─systemd-udevd-kernel.socket
● │ ├─sysinit.target
● │ │ ├─dev-hugepages.mount
● │ │ ├─dev-mqueue.mount
● │ │ ├─dracut-shutdown.service
● │ │ ├─hwclock-save.service
● │ │ ├─kmod-static-nodes.service
○ │ │ ├─ldconfig.service
● │ │ ├─lvm2-lvmpolld.socket
● │ │ ├─lvm2-monitor.service
● │ │ ├─proc-sys-fs-binfmt_misc.automount
● │ │ ├─sys-fs-fuse-connections.mount
● │ │ ├─sys-kernel-config.mount
● │ │ ├─sys-kernel-debug.mount
● │ │ ├─sys-kernel-tracing.mount
● │ │ ├─systemd-ask-password-console.path
○ │ │ ├─systemd-binfmt.service
○ │ │ ├─systemd-hwdb-update.service
○ │ │ ├─systemd-journal-catalog-update.service
● │ │ ├─systemd-journal-flush.service
● │ │ ├─systemd-journald.service
○ │ │ ├─systemd-machine-id-commit.service
● │ │ ├─systemd-modules-load.service
● │ │ ├─systemd-network-generator.service
● │ │ ├─systemd-random-seed.service
● │ │ ├─systemd-sysctl.service
○ │ │ ├─systemd-sysusers.service
● │ │ ├─systemd-tmpfiles-setup-dev-early.service
● │ │ ├─systemd-tmpfiles-setup-dev.service
● │ │ ├─systemd-tmpfiles-setup.service
● │ │ ├─systemd-udev-trigger.service
● │ │ ├─systemd-udevd.service
○ │ │ ├─systemd-update-done.service
● │ │ ├─systemd-update-utmp.service
● │ │ ├─cryptsetup.target
● │ │ ├─integritysetup.target
● │ │ ├─local-fs.target
● │ │ │ ├─-.mount
● │ │ │ ├─boot-efi.mount
● │ │ │ ├─boot.mount
● │ │ │ ├─systemd-fsck-root.service
● │ │ │ ├─systemd-remount-fs.service
● │ │ │ └─tmp.mount
● │ │ ├─swap.target
● │ │ │ └─dev-mapper-openeuler\x2dswap.swap
● │ │ └─veritysetup.target
● │ └─timers.target
● │ ├─dnf-makecache.timer
● │ └─systemd-tmpfiles-clean.timer
● ├─getty.target
● │ └─[email protected]
● └─remote-fs.target
[root@localhost ~]#
# 查看反向依赖
[root@localhost ~]# systemctl list-dependencies sshd.service --reverse
sshd.service
● └─multi-user.target
○ └─graphical.target
[root@localhost ~]#
系统启动级别
CentOS 6 之前使用启动级别控制系统开机激活哪些服务。
CentOS 7 之后使用target控制系统开机激活哪些服务。
| runlevel | target | 作用 |
|---|---|---|
| 0 | 关机。init 0 同 poweroff 和 systemctl poweroff | |
| 1 | emergency.target、rescue.target | 单用户模式、救援模式和紧急模式用于修复系统 |
| 2 | 多用户文本界面,不具备 NFS 功能 | |
| 3 | multi‑user.target | 多用户文本界面 |
| 4 | 未使用 | |
| 5 | graphical.target | 多用户图形界面 |
| 6 | 重启,init 6 同 reboot 和 systemctl reboot |
设置系统运行目标
/etc/inittab文件是rhel6之前版本初始化系统使用的配置文件。
# 部分内容如下
# Default runlevel. The runlevels used by RHS are:
# 0 - halt (Do NOT set initdefault to this)
# 1 - Single user mode
# 2 - Multiuser, without NFS (The same as 3, if you do not have
networking)
# 3 - Full multiuser mode
# 4 - unused
# 5 - X11
# 6 - reboot (Do NOT set initdefault to this)
#
# 设置运行级别为5,也就是图形化方式启动。
id:5:initdefault:
设置系统当前运行target
# 设置系统当前运行target为multi-user.target
[root@localhost ~]# systemctl isolate multi-user.target
# 设置系统当前运行target为graphical.target
[root@localhost ~]# systemctl isolate graphical.target
设置系统开机默认运行target
# 查看系统开机默认运行target
[root@localhost ~]# systemctl get-default
multi-user.target
# 设置系统开机默认运行target
[root@localhost ~]# systemctl set-default multi-user.target
# 重启验证
在系统启动时选择其它目标
要在启动时选择其他目标,将systemd.unit=target.target参数附加到内核命令行。该配置是临时生效的,经常用于系统启动过程中故障排除。
要使用这种选择其他目标的方法,请执行以下步骤:
- 启动或重新启动系统。
- 按任意键(Enter除外,它用于执行正常启动)中断启动加载器菜单倒计时。
- 将光标移至要启动的内核条目。
- 按e编辑当前条目。
- 将光标移至以linux开头的行。此为内核命令行。
- 附加systemd.unit=target.target。例如,systemd.unit=emergency.target。
- 按Ctrl+x使用这些更改进行启动。
重置ROOT密码
如果管理员仍处于登录状态,不管是作为拥有完全sudo访问权限的非特权用户,还是作为root用户,这个任务都很简单,直接执行passwd命令设置即可。如果管理员未登录,则这个任务变得略微复杂。
在CentOS6及更早的版本中,管理员可以将系统启动到runlevel 1以获得root提示符。在CentOS7之后系统上与runlevel 1最接近的模式是紧急目标和救援目标,但是这两个目标都需要root密码才能登录。
以下几种方法可用于设置新的root密码。例如:
系统管理员可以使用Live CD启动系统,挂载根文件系统,然后编辑/etc/shadow:
删除root账户密码字段
使用已知密码字段替换root密码字段
方法1:rd.break(官方推荐,优先用)
中断点:initramfs 阶段,还没有切换真实根,提示符 switch_root:/#
-
重新启动系统。
-
按任意键(Enter除外)中断启动加载器倒计时。
-
将光标移至要启动的内核条目。
-
按e编辑选定的条目。

-
将光标移到内核命令行(以linux开头的行)。
-
附加rd.break。利用该选项,在系统从initramfs向实际系统移交控制权前,系统将会中断。kernel行可能有多个console参数,以最后一个为准,有些时候需要设console=tty0。

-
按Ctrl+x使用这些更改进行启动。
-
此时,系统会显示root shell,且磁盘上的实际根文件系统以只读方式挂载在/sysroot。
-
以读/写形式重新挂载/sysroot。
switch_root:/# mount -o remount,rw /sysroot
- 切换root位置,把/sysroot做为文件系统树的根。
switch_root:/# chroot /sysroot
- 设置新root密码。
sh-4.2# passwd root
- 如果系统开启了SELinux功能,则需要确保所有未标记的文件(包括此时的/etc/shadow)在启动过程中都会重新获得标记。
sh-4.2# touch /.autorelabel
-
键入exit两次。第一个命令将退出chroot存放位置,第二个命令将退出initramfs调试shell。此时,系统将继续进行启动,执行完整的SELinux重新标记,然后再次重新启动。
-
登录验证。
方法2:init=/bin/bash
作用:内核启动完,不启动 systemd (PID1),直接把 /bin/bash 作为 PID=1,直接拿到系统 shell。
此时根文件系统默认只读挂载
- 重新启动系统。
- 按任意键(Enter除外)中断启动加载器倒计时。
- 将光标移至要启动的内核条目。
- 按e编辑选定的条目。

- 将光标移到内核命令行(以linux开头的行)。
- 附加init=/bin/bash。利用该选项,在系统从initramfs向实际系统移交控制权前,系统将会中断,请开启一个root shell。
kernel行可能有多个console参数,以最后一个为准,有些时候需要设置console=tty0。

- 按Ctrl+x使用这些更改进行启动。
- 此时,系统会显示root shell,且磁盘上的实际根文件系统以只读方式挂载在/。
- 以读/写形式重新挂载/。
bash-4.2# mount -o remount,rw /
-
设置新root密码
sh-4.2# passwd -
如果系统开启了SELinux功能,则需要确保所有未标记的文件(包括此时的/etc/shadow)在启动过程中都会重新获得标记。
sh-4.2# touch /.autorelabel
- 执行exec /sbin/init,拉起systemd正常走完启动流程
sh-4.2# exec /sbin/init
- 登录验证
两种重置root密码方式对比:
| 对比项 | rd.break | init=/bin/bash |
|---|---|---|
| 中断阶段 | initramfs,切换真实根之前 | 已经切换到真实根,替代 systemd (PID1) |
| 根文件系统位置 | 真实根在 /sysroot,需要 chroot | 直接就是真实系统 / |
| SELinux | 依然生效,必须写 /.autorelabel | SELinux 依然生效,也要 /.autorelabel |
| 磁盘支持 | LVM、RAID、iSCSI 都正常识别,initramfs 驱动完整 | 同样加载 initramfs,磁盘驱动正常 |
| 退出方式 | 两次 exit 正常继续启动 | 不能直接断电 / 重启,必须执行 exec /sbin/init |
| 风险点 | 几乎无风险,官方标准方式 | 忘记 exec /sbin/init 直接重启,极易损坏文件系统 |
| 官方态度 | RHEL/CentOS 官方文档标准方案 | 兼容旧方式,不推荐生产使用 |
/etc/fstab引起的系统启动问题
环境准备
# 设置磁盘分区管理方案
[root@centos79 ~]# parted /dev/sdb mklabel msdos
# 创建分区
[root@centos79 ~]# parted /dev/sdb unit MiB mkpart primary 1 10241
# 格式化为xfs文件系统
[root@centos79 ~]# mkfs.xfs /dev/sdb1
# 创建挂载点
[root@centos79 ~]# mkdir /data01
# 设置持久化挂载
[root@centos79 ~]# echo '/dev/sdb1 /data01 xfs defaults 0 0' >> /etc/fstab
# 挂载并验证
[root@centos79 ~]# mount -a
[root@centos79 ~]# df -h /data01
文件系统 容量 已用 可用 已用% 挂载点
/dev/sdb1 10G 33M 10G 1% /data01
故障1:挂载点不存在
环境准备
[root@centos79 ~]# umount /data01
[root@centos79 ~]# rmdir /data01
重启系统验证
# 可以正常进入系统,挂载点会被自动创建
[root@centos79 ~]# df -h /data01
文件系统 容量 已用 可用 已用% 挂载点
/dev/sdb1 10G 33M 10G 1% /data01
故障2:设备名称写错或者找不到设备
环境准备
[root@centos79 ~]# vim /etc/fstab
# 将原先的sdb1修改为sdb2
/dev/sdb2 /data01 xfs defaults 0 0
重启系统验证
-
启动过程中找不到该设备

-
1分30秒超时后,进入emergency模式,输入密码,进行修复。

-
修改为正确的值或者注释该条目,确保系统正常启动。

-
然后输入reboot,重新启动系统。如果系统没有继续启动,则可以强制关机重启。
故障3:破坏文件系统
环境准备
[root@centos79 ~]# dd if=/dev/zero of=/dev/sdb1 bs=1M count=1
重启系统验证
- 系统启动过程中尝试修复文件系统,修复失败后提示进入emergency模式修复。

- 进入emergency模式修复
# 执行以下命令修复
[root@centos79 ~]# xfs_repair /dev/sdb1

修复完成后,输入exit继续启动系统,并可以正常启动系统。
grub2配置
grub2配置
grub2的主要配置文件如下:
-
/boot/grub2,grub2的主要配置文件所在目录,例如grub.cfg、grubenv、user.cfg
-
/etc/grub2.cfg
- /etc/grub2.cf是指向/boot/grub2/grub.cfg的软连接。
- 不要直接修改/etc/grub2.cfg文件,该文件由系统自动生成。如果需要自定义这个文件,可以通过修改/etc/grub.d/中脚本和/etc/default/grub文件,然后通过grub2-mkconfig命令生成。
使用efi启动方式的grub2配置文件是/etc/grub2-efi.cfg。
-
/etc/grub.d/,该目录下存放多个数字开头的脚本,按照从小到大的顺序执行。例如00_header会调用/etc/default/grub配置文件来实现最基本的开机界面配置。
-
/etc/default/grub,定义grub启动部分变量信息,例如grub启动菜单选择条目的超时时间,内核参数等。
-
/boot/grub2/grubenv,设定默认启动条目。
grub2菜单超时时间
[root@centos79 ~]# vim /etc/default/grub
# 修改GRUB_TIMEOUT为10
GRUB_TIMEOUT=10
# 生效grub2.cfg配置文件
[root@centos79 ~]# grub2-mkconfig -o /etc/grub2.cfg
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-3.10.0-1160.el7.x86_64
Found initrd image: /boot/initramfs-3.10.0-1160.el7.x86_64.img
Found linux image: /boot/vmlinuz-0-rescue-c6e36d35c9a846d7b64d9fdf929b09f6
Found initrd image: /boot/initramfs-0-rescue-c6e36d35c9a846d7b64d9fdf929b09f6.img
done
# 重启
[root@centos79 ~]# reboot
效果如下:

转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/qq_52580914/article/details/164075756




