Linux 之旅 19:启动流程、模块管理与loader

Linux 之旅 19:启动流程、模块管理与loader

image-20210907201413018

图源:pexels

Linux启动流程分析

启动流程一览

通常操作系统的启动过程可以分为以下几个步骤:

  1. 加载BIOS以获取硬件信息并进行自检,这样就可以获取到第一个可启动设备。
  2. 读取并执行第一个可启动设备的MBR内的启动引导程序(grub2spfdisk等)。
  3. 根据启动引导程序的设置加载Kernel,Kernel进行硬件检测并加载需要的驱动和模块。
  4. Kernel启动Systemd进程,并且用default.target模式准备操作系统环境:
    1. systemd执行sysinit.target初始化系统
    2. systemd执行basic.target准备基础的操作系统环境
    3. systemd启动multi-user.target下的本机与服务器服务
    4. systemd执行multi-user.target下的/etc/rc.d/rc.local文件
    5. systemd执行multi-user.target下的getty.target及登录服务
    6. systemd执行graphical所需的服务

BIOS、boot loader与Kernel加载

BIOS、启动自我检测与MBR/GPT

操作系统启动后第一件事情就是加载BIOS(Basic Input Output System),并通过BIOS加载CMOS信息,由CMOS内设置的信息读取主机的各种硬件信息。

关于BIOS和CMOS的详细信息可以阅读BIOS和CMOS是什么?教大家认识BIOS和CMOS!

从CMOS中可以获取到第一个可用的启动设备(就是我们出重装系统的时候会在BIOS中修改的那个启动顺序列表),然后会从第一个启动设备的MBR分区中获取 boot loader(启动引导程序),如果是多系统,还可能由MBR中的boot loader加载其它分区中的boot loader,再启动具体的操作系统。

boot loader

如果一个磁盘上只装了一个操作系统,其boot loader就在磁盘的MBR中,也就是整个磁盘的第一块扇区中。如果是多系统,每个系统的boot loader会保存在系统所在的分区的第一块扇区(启动扇区,boot sector)中,而MBR中的boot loader仅起一个转交启动工作的用途,会在用户选择启动哪个系统之后,将系统启动工作转移给对应系统的boot loader来执行。整个过程可以用下图表示:

img

图源:鸟哥的私房菜

所以boot loader具有以下功能:

  • 提供菜单:用户可以选择启动哪一个选项启动(对于单系统,也可以提供救援模式之类的启动项)
  • 加载内核文件:直接加载内核的相关文件到内存
  • 转交启动工作给其它loader:如果是多系统,MBR中的boot loader会将具体的启动工作转交给对应的操作系统的boot loader。

不同的操作系统因为使用的文件系统不同,比如Linux使用xfs而Windows使用fat,所以使用的boot loader也会有所不同,而Windows的boot loader行为比较简单,不具备转交启动工作给非Windows系统的boot loader的功能,而且安装Windows时不会提供选项,必定会将boot loader写入MBR中。这样就产生了一个结果,只要安装了Windows,无论其它分区有没有安装别的操作系统,都无法再由boot loader启动,只能启动Windows。所以如果要安装多系统,比如Windows和Linux,最好先安装Windows,再安装Linux。使用Linux的boot loader来提供多个操作系统的加载选项即可。

加载内核检测硬件与initramfs的功能

Linux内核的相关文件放在/boot中,并且一般会保存在/boot/vmlinuz

[icexmoon@xyz ~]$  ls --format=single-column -F /boot
config-3.10.0-1160.el7.x86_64 # 编译内核时的相关配置
efi/ 
grub/
grub2/ # boot loader grub2的相关文件
initramfs-0-rescue-f5a85e92d51e4d40975fd956fd775f9c.img # 救援时使用的虚拟文件系统
initramfs-3.10.0-1160.el7.x86_64.img # 一般开机会使用的虚拟文件系统
symvers-3.10.0-1160.el7.x86_64.gz
System.map-3.10.0-1160.el7.x86_64
testing.img
vmlinuz-0-rescue-f5a85e92d51e4d40975fd956fd775f9c* # 救援用的内核文件
vmlinuz-3.10.0-1160.el7.x86_64* # 内核文件

内核文件被加载以后,仅能直接使用内核中的相关功能,内核中没有的功能就要以“内核模块”的方式加载,这些模块保存在/lib/modules目录中。

要直接给对内核中的功能增加或修改相当麻烦,需要重新编译内核,所以一般来说,能做成“内核模块”的功能都会做成模块,这样无论是增加还是修改就会简单的多,只要内核挂载相应的模块即可,无需重新编译内核。但是这样会有一个新的问题,即内核模块所在的目录/lib/modules是和根目录/在一个分区的,而Linux加载内核后仅会挂载内核所在的这个分区,更要命的是,xfs之类的当前主要使用的文件系统驱动是以模块的方式提供的,并非直接编译在内核中,而内核需要先加载使用xfs文件系统的根目录分区才能读取到模块,这就变成了一个鸡生蛋蛋生鸡的问题。

万幸的是Linux提供了一个叫做虚拟文件系统(Initial RAM Disk 或 Initial RAM Filesystem)的东西,这是一个保存在/boot中的压缩文件,其中包含了一些内核必须的模块,比如文件系统或者磁盘驱动等。内核会先解压这个虚拟文件系统到内存中,并用它作为根目录,从而加载必须的模块,然后就可以加载“真正的”根目录所在的文件系统了,加载后会取代虚拟文件系统。

下面实际解压系统中的虚拟文件系统查看其组成,因为CentOS 7的虚拟文件系统压缩包格式比较特殊,所以这里参考centos7 initramfs解包 打包编写了一个解包的脚本:

initramfs_file=$(ls -a /boot/initramfs-$(uname -r).img)
tmp_home='/tmp/initramfs'
if [ ! -d $tmp_home ]
then
        mkdir $tmp_home
fi
initramfs_tmp="${tmp_home}/initramfs_file"
if [ ! -f $initramfs_tmp ]
then
        cp $initramfs_file ${initramfs_tmp}
fi
/usr/lib/dracut/skipcpio $initramfs_tmp | zcat | cpio -id

运行这个脚本解包即可:

[root@xyz initramfs]# sh unzip_initramfs.sh
128811[root@xyz initramfs]# ll
总用量 31352
lrwxrwxrwx.  1 root root        7 97 22:00 bin -> usr/bin
drwxr-xr-x.  2 root root       45 97 22:00 dev
drwxr-xr-x. 12 root root     4096 97 22:00 etc
lrwxrwxrwx.  1 root root       23 97 22:00 init -> usr/lib/systemd/systemd
-rw-------.  1 root root 32089078 97 22:00 initramfs_file
lrwxrwxrwx.  1 root root        7 97 22:00 lib -> usr/lib
lrwxrwxrwx.  1 root root        9 97 22:00 lib64 -> usr/lib64
drwxr-xr-x.  2 root root        6 97 22:00 proc
drwxr-xr-x.  2 root root        6 97 22:00 root
drwxr-xr-x.  2 root root        6 97 22:00 run
lrwxrwxrwx.  1 root root        8 97 22:00 sbin -> usr/sbin
-rwxr-xr-x.  1 root root     3117 97 22:00 shutdown
drwxr-xr-x.  2 root root        6 97 22:00 sys
drwxr-xr-x.  2 root root        6 97 22:00 sysroot
drwxr-xr-x.  2 root root        6 97 22:00 tmp
-rw-r--r--.  1 root root      302 97 21:59 unzip_initramfs.sh
drwxr-xr-x.  7 root root       66 97 22:00 usr
drwxr-xr-x.  2 root root       29 97 22:00 var

systemd

systemd是系统内核启动后开启的第一个服务进程,其功能是准备一个基础的操作系统运行环境,包括主机名、网络设置、语言设置、文件系统格式等,所有的这些都是通过一个默认的启动服务集合default.target实现的。

target与runlevel

之前我们说过,现在的服务管理机制systemd与以前的system V是有所不同的,以前用于区分系统模式的是runlevel,而现在则使用的是target,为了兼容性方面的考虑,systemd将部分以前的runlevel映射到了某些target上:

[icexmoon@xyz ~]$ ll -d /usr/lib/systemd/system/runlevel*.target | cut -c 41-
/usr/lib/systemd/system/runlevel0.target -> poweroff.target
/usr/lib/systemd/system/runlevel1.target -> rescue.target
/usr/lib/systemd/system/runlevel2.target -> multi-user.target
/usr/lib/systemd/system/runlevel3.target -> multi-user.target
/usr/lib/systemd/system/runlevel4.target -> multi-user.target
/usr/lib/systemd/system/runlevel5.target -> graphical.target
/usr/lib/systemd/system/runlevel6.target -> reboot.target

因此,以前用于操作系统模式转换的命令init x是依然可以使用的,其和systemctl命令的对应关系是:

SystemV systemd
init 0 systemctl poweroff
init 1 systemctl rescue
init [234] systemctl isolate multi-user.target
init 5 systemctl isolate graphical.target
init 6 systemctl reboot
systemd的处理流程

systemd会通过启动default.target的方式准备一个基本的操作系统环境,从Linux 之旅 17:系统服务(daemons)中我们已经知道,default.target可以有两个选项:graphical.targetmulti-user.target,实际上default.target就是一个到这两者之一的一个alias

比较常用的是graphical.target,并且其本身包含multi-user.target,所以这里假设当前Linux主机使用的default.targetgraphical.target

我们来看graphical.target具体会干些什么:

[root@xyz ~]# cat /usr/lib/systemd/system/graphical.target | grep -v '^#'

[Unit]
Description=Graphical Interface
Documentation=man:systemd.special(7)
Requires=multi-user.target
Wants=display-manager.service
Conflicts=rescue.service rescue.target
After=multi-user.target rescue.service rescue.target display-manager.service
AllowIsolate=yes

比较重要的设置是RequiresWants,前者是说启动graphical.target之前要先启动哪些服务,后者是说启动graphical.target之后要启动哪些服务。

从这里可以看出,相关服务的启动顺序是multi-user.target->graphical.target->display-manager.service。当然,前两个是target,是服务集,最后一个是单纯的服务。

现在我们看multi-user.target会干些什么:

[root@xyz ~]# cat /usr/lib/systemd/system/multi-user.target | grep -v '^#'

[Unit]
Description=Multi-User System
Documentation=man:systemd.special(7)
Requires=basic.target
Conflicts=rescue.service rescue.target
After=basic.target rescue.service rescue.target
AllowIsolate=yes

可以看到,要启动multi-user.target就要先启动basic.target

最后再看一下默认安装的需要被multi-user.target加载的服务有哪些:

[root@xyz ~]# ls /usr/lib/systemd/system/multi-user.target.wants/
dbus.service  plymouth-quit.service       systemd-ask-password-wall.path  systemd-update-utmp-runlevel.service
getty.target  plymouth-quit-wait.service  systemd-logind.service          systemd-user-sessions.service

用户自定义的需要被multi-user.target加载的服务有:

[root@xyz ~]# ls /etc/systemd/system/multi-user.target.wants/
abrt-ccpp.service
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值