文章目录
Linux启动流程
系统启动流程
- 按下电源键,硬件主动加载BIOS硬件信息与进行自我检测,并依据设定取得第一个可开机装置;
- 读取并执行第一个可开机装置内MBR的Boot Loader(如grub2);
- 依据Boot Loader的设定加载内核,内核会开始侦测硬件与加载驱动程序;
- 在硬件驱动成功后,内核呼叫第一个进程(init或systemd),而后将控制权交给第一个进程。
BIOS/Boot Loader/Kernel
BIOS
按下电源后,启动系统首先要加载BIOS(Basic Input Output System):
- BIOS程序首先加载CMOS芯片(CMOS芯片保存了系统引导最基本的资料)的信息,并依据CMOS内的设定值取得主机的各项硬件配置;
- 从CMOS处取得信息后,BIOS开始进行开机自我测试(Power-on Selt Test,POST),之后开始执行硬件侦测的初始化,并设定PnP(即插即用装置)。做完以上工作,BIOS可以定义出可开机装置的顺序,接下来开始开机装置的数据读取;
- BIOS通过硬件的INT 13中断功能读取MBR,只要BIOS能够侦测到磁盘,BIOS就可以通过INT 13信道读取磁盘的第一个扇区MBR,即可执行Boot Loader。
总结:按下电源键后首先加载BIOS,BIOS负责自我检测以及硬件侦测,并找到第一个可开机装置,加载Boot Loader。
Boot Loader
Boot Loader(开机管理程序):因为不同的操作系统的文件系统格式不同,所以需要一个Boot Loader处理核心文件加载的问题,不同的操作系统有不同的Boot Loader。Boot Loader的功能如下:
- 最主要的功能是识别操作系统的文件系统格式并据以加载核心到内存中执行;
- 提供选单,用户可以选择不同的开机项目;
- 转交给其他Boot Loader,将开机管理功能转交给其他Loader负责。
扩展:如何在同一台主机安装两个操作系统?
如上图所示一块硬盘,每个文件系统或分区都会保留启动块Boot Block来提供操作系统安装Boot Loader。
MBR常常被不同操作系统的Boot Loader覆盖:
- Linux在安装时,可以选择将boot loader安装到MBR中,也可以选择不安装(如果将boot loader安装到MBR中,那么在MBR和Linux系统都有一个boot loader)。
- Windows在安装时,会主动将MBR和boot block都装上一份boot loader。
虽然MBR只有一个,但是Boot Loader具有选单功能,可以将控制权转交给其他boot loader,因此可以加载其他boot block中的boot loader。
要额外注意的是:安装Windows和Linux双系统时,由于Windows的Boot Loader预设不具有控制权转交功能,因此要先安装Linux再安装Windows。
总结:Boot Loader负责识别操作系统文件系统类型并加载内核到内存中(提供选单和转交控制权功能)。
Kernel
Boot Loader将内核加载到内存中后,内核会自己重新检测硬件(不会使用BIOS侦测到的硬件信息),并尝试挂载根目录以加载驱动程序。
Linux核心文件会被放置在/boot目录中,并取名为/boot/vmlinuz。
[root@rhel7 ~]# ll --format=single-column -F /boot
config-3.10.0-1160.el7.x86_64 # 此版本核心被编译时选择的功能与模块配置文件
grub/ # 旧版本grub目录
grub2/ # 开机管理程序grub2相关数据目录
initramfs-0-rescue-ddfe10d662ea41efaa2953aec05e3f58.img # 救援使用的虚拟文件系统
initramfs-3.10.0-1160.el7.x86_64.img # 正常开机使用的虚拟文件系统
System.map-3.10.0-1160.el7.x86_64 # 核心功能放置到内存地址的对应表
vmlinuz-0-rescue-ddfe10d662ea41efaa2953aec05e3f58* # 救援用到的核心文件
vmlinuz-3.10.0-1160.el7.x86_64* # 开机用到的核心文件
# -F或--classify 在执行文件、目录、Socket、符号连接、管道名称后面,各自加上识别符
执行文件 *
目录 /
Socket =
符号链接 @
管道 |
Linux核心可以动态加载核心模块(驱动程序),核心模块放置在/lib/modules/目录中(SATA、SCSI等磁盘装置的驱动程序通常都是以模块的形式存在)。
[root@rhel7 ~]# ls /lib/modules/3.10.0-1160.el7.x86_64/
build modules.builtin modules.modesetting source
extra modules.builtin.bin modules.networking updates
kernel modules.dep modules.order vdso
modules.alias modules.dep.bin modules.softdep weak-updates
modules.alias.bin modules.devname modules.symbols
modules.block modules.drm modules.symbols.bin
出现问题:驱动程序作为核心模块放置在磁盘根目录中,所以开机过程中,核心必须挂载根目录才能够读取核心模块,从而加载驱动程序。但是此时核心并不认识磁盘,需要加载磁盘驱动程序后才能挂载根目录。由此出现矛盾。
解决措施:通过虚拟文件系统(Initial RAM Disk)处理该问题。正常开机时使用的虚拟文件系统的文件名一般是/boot/initramfs或/boot/initrd。
- 虚拟文件系统可以通过Boot Loader和内核一起被加载到内存中,虚拟文件系统在内存中被解压缩并仿真成一个根目录。
- 该仿真根目录在内存中的文件系统能够提供一支可执行的程序,内核可以通过该程序来加载开机过程中所需要的核心模块(通常为文件系统和磁盘的驱动程序)。
- 加载驱动程序完成后,将根文件系统以只读方式挂载,并挂载到真正的根目录文件系统,而后释放该虚拟文件系统,同时调用第一个进程,进入系统初始化阶段。
注意:RHEL 6 虚拟文件系统为initrd,呼叫第一个进程init;RHEL 7 虚拟文件系统initramfs,呼叫第一个进程systemd。
总结:内核自行检测所有硬件信息,通过搜索虚拟文件系统挂载的仿真根目录来加载必要的驱动程序和核心模块,主机硬件准备就绪后,核心呼叫第一个进程,并转移控制权。
System V / Systemd
System V 启动流程
通过执行系统初始化脚本/etc/rc.d/rc.sysinit初始化用户环境。
- 设置主机名;
- 设置欢迎信息;
- 激活UDEV和SeLinux;
- 挂载/etc/fstab文件定义的所有文件系统;
- 检测根文件系统,并以读写方式重新挂载根文件系统(内核加载阶段只读挂载);
- 设置系统时钟;
- 根据/etc/sysctl.conf文件设置内核参数;
- 激活LVM及RAID设备;
- 激活SWAP设备;
- 加载额外设备的驱动程序(内核只加载根文件系统的驱动程序);
- 清理操作;
通过/etc/inittab文件确定运行级别,之后将运行级别传入/etc/init/rc.conf文件中,/etc/init/rc.conf调用/etc/rc.d/rc $RUNLEVEL。
[root@rhel6 ~]# cat /etc/inittab
...
id:5:initdefault:
[root@rhel6 ~]# cat /etc/init/rc.conf
...
export RUNLEVEL
console output
exec /etc/rc.d/rc $RUNLEVEL
/etc/rc.d/rc 接受传递的参数$runlevel,之后读取/etc/rc$runlevel.d/K*和/etc/rc$runlevel.d/S*所有文件(这些文件都是链接文件,它们链接到了/etc/init.d/*目录下的各个程序),按照优先级启动和关闭相应脚本。
[root@rhel6 ~]# ls /etc/rc.d/
init.d rc rc0.d rc1.d rc2.d rc3.d rc4.d rc5.d rc6.d rc.local rc.sysinit
[root@rhel6 ~]# ls /etc/rc.d/rc5.d/
K01smartd K60nfs S01sysstat S20kdump S50bluetooth
K02oddjobd K69rpcsvcgssd S02lvm2-monitor S22messagebus S55sshd
K05wdaemon K73winbind S08ip6tables S23NetworkManager S58ntpd
...
[root@rhel6 ~]# ll /etc/rc.d/rc5.d/
total 0
lrwxrwxrwx. 1 root root 16 May 1 19:08 K01smartd -> ../init.d/smartd
- K*:要停止的服务,K##*,优先级,数字越小,越优先关闭,依赖的服务先关闭,然后再关闭被依赖的。
- S*:要启动的服务,S##*,优先级,数字越小,越是优先启动,被依赖的服务先启动,而依赖的服务后启动。
启动和关闭/etc/rc.d/rc.local中的程序。因此有什么希望系统开机就做的动作,可以写入/etc/rc.d/rc.local文件中。
[root@rhel6 ~]# cat /etc/rc.local
#!/bin/sh
#
# This script will be executed *after* all the other init scripts.
# You can put your own initialization stuff in here if you don't
# want to do the full Sys V style init stuff.
touch /var/lock/subsys/local
Systemd启动流程
在正常进入Linux操作环境的情况下,取得/etc/systemd/system/default.target 的预设操作界面设定为graphical.target。
[root@rhel7 ~]# ll /etc/systemd/system/default.target
lrwxrwxrwx. 1 root root 36 Sep 11 17:23 /etc/systemd/system/default.target -> /lib/systemd/system/graphical.target
通过/usr/lib/systemd/system/graphical.target配置文件发现Requires和Wants信息
systemd会去找两个地方的设定:
- /etc/systemd/system/graphical.target.wants/ :使用者设定加载的unit
- /usr/lib/systemd/system/graphical.target.wants/:系统默认加载的unit
[root@rhel7 ~]# cat /usr/lib/systemd/system/graphical.target
[Unit]
Description=Graphical Interface
Documentation=man:systemd.special(7)
Requires=multi-user.target # graphical.target必须在multi-user.target完成后才能够进行
Wants=display-manager.service # 进行完graphical.target之后,还得启动display-manager.service才行
Conflicts=rescue.service rescue.target
After=multi-user.target rescue.service rescue.target display-manager.service
AllowIsolate=yes
通过/usr/lib/systemd/system/multi-user.target配置文件查看multi-user.target执行完毕所需操作
systemd会去找两个地方的设定:
- /etc/systemd/system/multi-user.target.wants/:使用者设定加载的unit
- /usr/lib/systemd/system/multi-user.target.wants/:系统默认加载的unit
[root@rhel7 ~]# cat /usr/lib/systemd/system/multi-user.target
[Unit]
Description=Multi-User System
Documentation=man:systemd.special(7)
Requires=basic.target # multi-user.target必须在=basic.target完成后才能够进行
Conflicts=rescue.service rescue.target
After=basic.target rescue.service rescue.target
AllowIsolate=yes
通过/usr/lib/systemd/system/basic.target配置文件查看basic.target执行完毕操作
systemd会去找两个地方的设定:
- /etc/systemd/system/basic.target.wants/:使用者设定加载的unit
- /usr/lib/systemd/system/basic.target.wants/:系统默认加载的unit
[root@rhel7 ~]# cat /usr/lib/systemd/system/basic.target
[Unit]
Description=Basic System
Documentation=man:systemd.special(7)
Requires=sysinit.target # basic.target必须在sysinit.target执行之后才能进行
After=sysinit.target
Wants=sockets.target timers.target paths.target slices.target
After=sockets.target paths.target slices.target
…
可以通过systemctl list-dependencies graphical.target查看系统服务启动的流程,这就是systemd呼叫所需要的服务的流程。
[root@rhel7 basic.target.wants]# systemctl list-dependencies graphical.target
graphical.target
● ├─accounts-daemon.service
● ├─gdm.service
● ├─initial-setup-reconfiguration.service
● ├─network.service
● ├─rtkit-daemon.service
● ├─systemd-readahead-collect.service
● ├─systemd-readahead-replay.service
● ├─systemd-update-utmp-runlevel.service
● ├─udisks2.service
● └─multi-user.target
● ├─abrt-ccpp.service
● ├─abrt-oops.service
● ├─abrt-vmcore.service
...
简略分析 systemctl list-dependencies graphical.target所输出的相依性服务,基本CentOS 7.x的systemd开机流程大约是:
- local-fs.target+swap.target:主要在挂载本机/etc/fstab里所规范的文件系统与相关的内存置换空间
- sysinit.target:主要侦测硬件,加载所需要的核心模块等动作
- basic.target:加载主要的外围硬件驱动程序与防火墙相关任务
- multi-user.target:其他一般系统或网络服务的加载
- graphical.target:图形界面相关等其他服务的加载
SystemV 和 Systemd