Linux 开机启动流程
1、Linux启动过程概述
1.1、启动过程
上图中,有 GRUB、GRUB2、init、systemd 等几个概念,下面做一下简单的说明。
GRUB2 相较于 GRUB 的提升:更健壮、可移植、更强大。支持 Legacy BIOS、EFI 和 OpenFirmware,支持 GPT 和 MBR 分区表。支持非 Linux 系统,如苹果 HFS 文件系统和 Windows 的 NTFS 文件系统。
systemd 被设计用来改进 sysvinit 的缺点,它和 Ubuntu 的 upstart 是竞争对手,然而从 Ubuntu 15.04 开始,Ubuntu 开始逐步使用 systemd 替代 Upstart 初始化系统。
systemd 的目标是:尽可能启动更少进程;尽可能将更多进程并行启动。systemd 尽可能减少对 shell 脚本的依赖。传统 sysvinit 使用 inittab 来决定运行哪些 shell 脚本,大量使用 shell 脚本被认为是效率低下无法并行的原因。systemd 使用了 Linux 专属技术,不再顾及 POSIX 兼容。
1.2、init 和 systemd 的区别
init
- init 是串行启动,只有前一个进程启动完,才会启动下一个进程,因此启动速度慢
- 由于是串行启动,当有某一个 shell 脚本被堵塞了,系统将无法正常启动
- init 启动脚本采用 shell 脚本编写,具有编写脚本复杂、执行效率低等缺点
- 由 Linux 内核加载运行,位于 /sbin/init ,是系统中第一个进程,PID 永远为 1
- Init 进程的配置文件
参数 | 说明 |
---|---|
/etc/init.d/ | 服务启动脚本配置文件存放目录 |
/etc/inittab | 默认运行级别配置文件 |
/etc/init/rcS.conf | 系统初始化配置文件 |
/etc/init/rc.conf | 各运行级别初始化的配置文件 |
/etc/init/rcS-sulogin.conf | 单用户模式启动 /sbin/sushell 环境的配置文件 |
/etc/init/control-alt-delete.conf | 终端下的 ctrl+alt+del 热键操作的配置文件 |
/etc/sysconfig/init | tty终端的配置文件 |
/etc/init/start-ttys.conf | 配置tty终端的开启数量、设备文件 |
/etc/init/tty.conf 或 /etc/init/serial.conf | 控制tty终端的开启 |
systemd
- 并行启动,缩短启动时间
- 无启动脚本,改成配置文件的方式定义服务的启动
- 由Linx内核加载运行,位于 /usr/lib/systemd/systemd,是系统中第一个进程,PID 永远为 1
- systemd 进程的配置文件
参数 | 说明 |
---|---|
/etc/systemd/system/default.target | 取代/etc/inittab文件配置,通常符号链接到 /lib/systemd/system/graphical.target |
/run/systemd/system/ | 系统执行过程中所产生的服务脚本所在目录 |
/etc/systemd/system/ | 里面存放着不同级别的开启自启服务 |
/usr/lib/systemd/system/ | 每个服务最主要的启动脚本设置,类似于之前的 /etc/init.d/ |
1.3、运行级别概念
Linux 有 7 个运行级别,以数字 0-6 来表示。
运行级别 | 说明 | RHEL6命令 | RHEL7命令 |
---|---|---|---|
0 | 关机状态,使用该级别将会关机 | init 0 | poweroff |
1 | 系统救援模式,多用于系统维护 | init 1 | systemctl isolate rescue.target |
2 | 字符界面的多用户模式(不可访问网络) | init 2 | systemctl isolate mutil-user.target |
3 | 字符界面的完整多用户模式,大多数服务器主机运行此级别 | init 3 | systemctl isolate mutil-user.target |
4 | 未分配使用 | init 4 | systemctl isolate mutil-user.target |
5 | 图形界面的多用户模式,提供了图形桌面操作环境 | init 5 | systemctl isolate graphical.target |
6 | 重新启动主机 | init 6 | reboot |
2、深入了解Linux引导过程
2.1、第一步:开机自检
2.2、第二步:BIOS 查找引导设备
2.2.1 Legacy BIOS & Legacy GRUB
对于 Legacy BIOS 来说,一般而言只能引导 MBR (Main Boot Record)分区表,在 MBR 分区中,磁盘前 512 字节(即0柱面,0磁头,1扇区)记录着引导记录和分区表信息,数据结构如下。
- MBR:主引导记录,可以安装开机管理程序的地方,占 446 字节
- DPT:磁盘分区表,占 64 字节
- 结束标志:55 AA,占 2 字节
GRUB 安装过程(grub-install /dev/sda):
- 把
/boot/grub/stage1
的前 446 字节写入磁盘的 1 扇区 - 把
/boot/grub/e2fs_stage1_5
从磁盘的 2 扇区开始写入,共 13380 字节,占 27 个扇区 - 生成
/boot/grub/grub.conf
补充 Linux 分区知识:
- 在 MBR 分区表中,Linux 会把磁盘的前 2047 个扇区的空间预留出来,也就是说第一个分区是从 2048 扇区开始的
- 第一个扇区写入 MBR + DPT
- 2 ~ 2047 个扇区,预留给
GRUB
等引导器写入引导代码
因此,BIOS 读取了第一个扇区的数据之后,就知道了如何引导系统了。
- 读取第一个扇区,作用只有一个,就是将磁盘第二个扇区的内容加载的到内存
- 第二个扇区的作用就是加载磁盘的第三个扇区到第N个扇区到内存,N取几,取决于文件系统的支撑代码的大小
- 此时,已经加载了文件系统的驱动了,读取
/boot/grub/stage2
、/boot/grub/grub.conf
等文件,将引导菜单展示到屏幕
2.2.2 Legacy BIOS & GRUB2
前面说到,GRUB2 是 GRUB 的增强版,现在各发行版都已经迁移至 GRUB2 了。因此,了解 GRUB2 也是必须的了。
GRUB2 相比 GRUB 具有如下一些新特征:
- 无 stage1 stage1.5 stage2
- 配置文件采用新语法,支持脚本,加入新的命令,配置文件名为 grub.cfg
- 配置文件 grub.cfg不可写,由 grub2-mkconfig 自动产生,由 update2-grub 维护。
- 分区号不再从 0开始,而是从 1开始
- 支持更多到文件系统,如:ext4、hfs、ntfs,并可以直接从 lvm和raid中读取文件/li>
- grub2有更可靠的方法在磁盘上有多系统时发现文件和目标内核,可以用命令发现系统设备号或者UUID。
- 引入了设备模块,使得 core 镜像保持更小到尺寸
- 在启动时没有选择菜单的话,按住shift即可强制显示菜单
GRUB2 安装过程(grub2-install,grub2-mkconfig):
- 安装 GRUB 文件到
/boot/grub2
- 复制
/usr/lib/grub/i386-pc/
目录下的.mod
、.lst
、.img
到/boot/grub
目录下 /boot/grub2/i386-pc/boot.img
相当与 GRUB 的 stage1 被写入 MBRgrub-mkimage
程序将/usr/lib/grub/i386-pc/kernel.img
和一些模块动态编译生成/boot/grub2/i386-pc/core.img
,它包含了文件系统等重要驱动,并写入到磁盘的第二个扇区,相当于 GRUB 的 stage1.5grub2-mkconfig
生成/boot/grub2/grub.cfg
/boot/grub2/device.map
等文件
引导过程与 GRUB 同理:
- 读取第一个扇区,作用只有一个,就是将磁盘第二个扇区的内容加载的到内存
- 第二个扇区的作用就是加载磁盘的第三个扇区到第N个扇区到内存,N取几,取决于
core.img
的大小 - 此时,已经加载了文件系统的驱动了,读取
/boot/grub/stage2
、/boot/grub/grub.conf
等文件,将引导菜单展示到屏幕
2.2.3 UEFI BIOS 的启动过程
UEFI 全称“统一的可扩展固件接口”(Unified Extensible Firmware Interface),目前已取代传统 BIOS 成为事实上的标准了。在个人PC市场,尤其是笔记本市场,已经是清一色的使用了 UEFI 固件。
UEFI 与 Legacy BIOS 在引导上是完全不同的两个东西,上面已经介绍了 Legacy BIOS 是如何引导 GRUB 运行操作系统的,这一小节我们来了解一下 UEFI 是如何引导 GRUB 的。
UEFI 固件内置了 FAT 格式的驱动程序,默认情况下 UEFI 会搜索所有磁盘的 FAT 格式分区,并读取 FAT 分区的 /EFI/BOOT/BOOTx64.EFI
文件,此文件就是 UEFI 的系统引导文件,UEFI 在 BOOTx64.EFI 的指引下引导操作系统。
当然了,UEFI 也允许操作系统厂家不遵守这样的目录结构。操作系统厂家可以根据自己的需求来组织 EFI 文件,但是需要将可引导写到 UEFI 固件里。因为是以配置文件的方式保存至 UEFI 的可写 ROM 里的,因此恢复出厂或人为删除都会导致引导项丢失。
以 CentOS 7 为例,安装完系统后,会在 UEFI 引导项新增一条记录“CentOS”,CentOS 指向 /EFI/BOOT/centos/shimx64.efi,shimx64.efi 指挥着 UEFI 读取 grubx64.efi,grubx64.efi 读取相关配置文件,显示 CentOS 的启动菜单。总结一下,CentOS 7 的启动顺序就如下图所示。
3、操作系统初始化流程
上面花了很大篇幅去讲解计算机是如何引导操作系统的,目的是给大家补充计算机的一些了冷门知识。接下来讲解操作系统是如何进行初始化工作的。以 CentOS 6 为例讲解。
3.1、内核初始化
在 GRUB 菜单中,按 e 键进去可以看到 GRUB 的引导参数:
root (hd0,0)
kernel /vmlinuz-2.6.32-696.20.1.el6.x86_64
initrd /initramfs-2.6.32-696.20.1.el6.x86_64.img
root (hd0,0)
第一个磁盘的第一个分区kernel
内核文件initrd
为内核提供额外文件的 ramdisk,是一个简装版的根文件系统,解开之后,可以看到完整的 Linux 目录结构。其的作用是为内核提供必须的驱动、切换新根等
附解开 vmlinux 和 initramfs 的命令:
# 解开 vmlinux
od -t x1 -A d ../vmlinuz-2.6.32-696.20.1.el6.x86_64 | grep "1f 8b 08"
# 输出结果:0014432 48 8d 83 60 e3 40 00 ff e0 1f 8b 08 00 c3 6a 6b
# 我们要拿 14432 这个数值,加 9 得到最终的位置 = 14441
dd if=vmlinuz-2.6.32-696.20.1.el6.x86_64 bs=1 skip=14441 | zcat > vmlinux
# 得到最终的可执行文件 vmlinux
# 解开 initramfs
gunzip -c /boot/initramfs-2.6.32-696.20.1.el6.x86_64.img | cpio -id
补充细节:
- initramfs 会被 kernel 加载至内存
- 接着执行 initramfs 的 init 脚本
- init 脚本做的主要的事情就是做一些初始化工作,然后执行 switch_root 命令
- switch_root 命令的主要作用是把 initramfs 的内容全部删除以释放内存,挂载根并切换根,执行 /sbin/init
3.2、init 运行过程
/etc/init/rcS.conf
exec /etc/rc.d/rc.sysinit
- 打印 Welcome to SentOS ...
- 初始化硬件
- 启动 udev
- 设置主机名
- 配置网络参数
- 挂载 proc、sys
- 配置内核参数
- 挂载 /etc/fstab 定义的挂载点
- ...
确认运行级别 /etc/inittab
- exec telinit $runlevel
/etc/init/rc.conf
exec /etc/rc.d/rc $RUNLEVEL
- /etc/profile.d/lang.sh 设置语言环境
- 执行 /etc/rc$runlevel.d/ 下以 K 开头的脚本文件
- 执行 /etc/rc$runlevel.d/ 下以 S 开头的脚本文件
- /etc/rc.d/rc.local
/etc/init/start-ttys.conf
- 启动tty1-tty6设备
3.3、用户登录
/sbin/mingetty 运行mingetty程序,出现字符登录界面
- /etc/issue 在登录界面上显示发行版信息
exec("/bin/login",...) 运行/bin/login程序,验证用户名和口令
- /etc/passwd 读取passwd文件核对用户名和口令
- 登录成功,切换到工作目录
- 初始化环境变量
$HOME
,$PATH
等 - /etc/motd 显示这个文件的内容
- 检查新邮件
exec("/bin/bash",...) 运行bash程序
/etc/profile 执行这个脚本中
- /etc/profile.d/*.sh 执行这些脚本
执行.bash_profile
执行~/.bashrc
- 执行/etc/bashrc
4、补充 CentOS 7 的系统初始化流程
4.1、systemd 运行过程
操作系统在 POST 到加载系统初始化程序的过程和 CentOS 6 基本一致,这里就不再赘述了,主要是补充一下 systemd 初始化程序的执行流程。
systemd 程序执行后,首先会执行默认目标 default.target
(/etc/systemd/system/default.target),这个文件是个软链接,一般默认会指向 /lib/systemd/system/multi-user.target。
.target 的含义,我理解为是一组 Unit 的集合,systemd 会执行 /etc/systemd/system/ 和 /usr/lib/systemd/system/ 下 同名 + .wants 路径下的所有单元。
systemd 会自动处理依赖关系,我们通过查看 default.target 文件得知其依赖路径大致可简化为:
default.target(multi-user.target) -> basic.target -> sysinit.target -> local-fs.target swap.target
那么启动顺序就是反过来的:
local-fs.target swap.target -> sysinit.target -> basic.target -> default.target(multi-user.target)
4.2、systemd 如何兼容 sysv 启动脚本
systemd 中依然保留了兼容 sysv 风格的启动脚本特性,使用传统的 chkconfig 命令可以查看并改变服务的开机启动。
当你执行 chkconfig --add xxx 时,系统会在 /run/systemd/generator.late 路径生成一个 xxx.service 文件,以此来兼容 sysv 启动脚本。
5、后记
以上就是 Linux 启动的全过程了,对于其他操作系统如 Windows 系统来说,其引导部分原理是一样的,只是说引导菜单的展示使用了不同的技术罢了。后半部分的系统初始化流程则是各操作系统的实现都不一样,在 Linux 界,其主要分为 init 和 systemd,其代表就是 CentOS 6 和 CentOS 7 。好了,到目前为止,Linux 的启动就完成了。