Linux学习:Linux的系统启动过程

引用:《鸟哥的Linux私房菜基础篇第三版》

启动过程一览

既然启动是很严肃的一件事,那我们就来了解一下整个启动的过程吧!好让大家比较容易发现启动过程里面可能会发生问题的地方,以及出现问题后的解决之道! 不过,由于启动的过程中,那个启动管理程序(Boot Loader) 使用的软件可能不一样,例如目前各大 Linux distributions 的主流为 grub2,但早期 Linux 默认是使用 grub1或LILO。但无论如何,我们总是得要了解整个 boot loader 的工作情况,才能了解为何进行多重启动的配置时,老是听人家讲要先安装 Windows 再安装 Linux 的原因。

假设以个人计算机架设的 Linux 主机为例,当你按下电源按键后计算机硬件会主动的读取 BIOS或UEFI BIOS 来加载硬件信息及进行硬件系统的自我测试,之后系统会主动的去读取第一个可启动的设备 (由 BIOS 配置的) ,此时就可以读入启动管理程序了。

启动管理程序可以指定使用哪个核心文件来启动,并实际加载核心到内存当中解压缩与运行,此时核心就能够开始在内存内活动,并检测所有硬件信息与加载适当的驱动程序来使整个主机开始运行,等到核心检测硬件与加载驱动程序完毕后,一个操作系统就开始在你的 PC 上面运行了。

主机系统开始运行后,此时 Linux 才会调用外部程序开始准备软件运行的环境, 并且实际的加载所有系统运行所需要的软件程序哩!最后系统就会开始等待你的登陆与操作啦!简单来说,系统启动的经过可以总结成以下的过程:

  1. 加载 BIOS 的硬件信息与进行自我测试,并依据配置取得第一个可启动的设备;
  2. 读取并运行第一个启动设备内 MBR的boot Loader(亦即是 grub2, lilo等程序);
  3. 依据 boot loader的配置加载Kernel,Kernel会开始检测硬件与加载驱动程序;
  4. 在硬件驱动成功后,Kernel 会主动调用systemd程序,并以default.target流程启动;
    1. systemd执行sysinit.target 初始化系统及 basic.target准备运行环境;
    2. systemd启动multi-user.target下的服务;
    3. systemd执行multi-user.target下的 /etc/rc.d/rc.local脚本;
    4. systemd执行multi-user.target 下的 getty.target 及登陆服务;
    5. systemd执行graphical 需要的服务

大概的过程就是上面写的那个样子啦,你会发现systemd这个家伙占的比重非常重!那每一个程序的内容主要是在干嘛呢?下面就分别来谈一谈吧!

 

BIOS, boot loader 与 kernel 加载

我们这里为了讲解方便,将后续会用到的专有名词先做个综合解释:

  • BIOS:不论传统BIOS还是UEFI BIOS都会被统称为BIOS;
  • MBR:虽然分区表有传统MBR以及新的GPT,不过GPT也有保留一块一样MBR的区域,因此,下面的说明在安装boot loader的部分,鸟哥还是统称为MBR喔!总之,MBR 就代表该磁盘的最前面可安装boot loader 的那个区域就对了!
  • BIOS, 启动自我测试与 MBR/GPT

在个人计算机架构下,你想要启动系统首先就得要让系统去加载 BIOS (Basic Input Output System),并通过BIOS程序去加载 CMOS 的信息,并且通过 CMOS 内的配置值取得主机的各项硬件配置,例如CPU与周边设备的通讯时钟啊、启动设备的搜寻顺序啊、硬盘的大小与类型啊、系统时间啊、各周边总线的是否启动 Plug and Play (PnP, 随插即用设备) 啊、各周边设备的I/O位址啊、以及与CPU通讯的IRQ中断等等的信息。

在取得这些信息后,BIOS 还会进行启动自我测试 (Power-on Self Test, POST) 。 然后开始运行硬件检测的初始化,并配置PnP设备,之后再定义出可启动的设备顺序,接下来就会开始进行启动设备的数据读取了。

由于我们的系统软件大多放置到硬盘中嘛!所以BIOS会指定启动的设备好让我们可以读取磁盘中的操作系统核心文件。但由于不同的操作系统他的文件系统格式不相同,因此我们必须要以一个启动管理程序来处理核心文件加载 (load) 的问题,因此这个启动管理程序就被称为Boot Loader了。那这个Boot Loader程序安装在哪里呢?就在启动设备的第一个扇区(sector)内,也就是我们一直谈到的MBR(Master Boot Record, 主要启动记录区)。

那你会不会觉得很奇怪啊?既然核心文件需要 loader 来读取,那每个操作系统的 loader 都不相同, 这样的话BIOS又是如何读取MBR内的loader呢?很有趣的问题吧!其实BIOS是通过硬件的INT 13中断功能来读取MBR的,也就是说,只要 BIOS 能够检测的到你的磁盘 (不论该磁盘是SATA还是IDE接口),那他就有办法通过INT 13这条通道来读取该磁盘的第一个扇区内的MBR,这样 boot loader 也就能够被运行。

Tips:
我们知道每颗硬盘的最前面区域含有MBRGPT分区表提供loader的区域,那么如果我的主机上面有两颗硬盘的话,系统会去哪颗硬盘的最前面区域读取boot loader 呢?这个就得要看 BIOS 的配置了。基本上,我们常常讲的『系统的MBR』其实指的是第一个启动设备的MBR 才对!所以,改天如果你要将启动管理程序安装到某颗硬盘的MBR时,要特别注意当时系统的『第一个启动设备』是哪个,否则会安装到错误的硬盘上面的MBR 喔!重要重要!

  • Boot Loader 的功能

刚刚说到Loader的最主要功能是要认识操作系统的文件格式并据以加载核心到主内存中去运行。由于不同操作系统的文件格式不一致,因此每种操作系统都有自己的boot loader。用自己的loader才有办法加载核心文件。那问题就来啦,你应该有听说过多重操作系统吧?也就是在一部主机上面安装多种不同的操作系统。既然你 (1)必须要使用自己的loader才能够加载属于自己的操作系统核心,而 (2)系统的 MBR 只有一个,那你怎么会有办法同时在一部主机上面安装 Windows 与 Linux 呢?

其实每个文件系统 (filesystem或者是 partition) 都会保留一块启动扇区 (boot sector) 提供操作系统安装boot loader ,而通常操作系统默认都会安装一份 loader 到他根目录所在的文件系统的 boot sector 上。如果我们在一部主机上面安装 Windows 与 Linux 后,该 boot sector, boot loader 与 MBR 的相关性会有点像下图:

如上图所示,每个操作系统默认是会安装一套boot loader到他自己的文件系统中 (就是每个 filesystem 左下角的方框),而在Linux系统安装时,你可以选择将boot loader安装到MBR去,也可以选择不安装。如果选择安装到 MBR 的话,那理论上你在MBR与 boot sector 都会保有一份boot loader程序的。至于Windows 安装时,他默认会主动的将 MBR 与 boot sector 都装上一份 boot loader!所以啦,你会发现安装多重操作系统时,你的 MBR 常常会被不同的操作系统的 boot loader 所覆盖啦!

我们刚刚提到的两个问题还是没有解决啊!虽然各个操作系统都可以安装一份 boot loader到他们的boot sector中,这样操作系统可以通过自己的 boot loader来加载核心了。问题是系统的MBR 只有一个哩!你要怎么运行 boot sector 里面的 loader 啊?boot loader 主要的功能如下:

  • 提供菜单:使用者可以选择不同的启动项目,这也是多重启动的重要功能!
  • 加载核心文件:直接指向可启动的程序区段来开始操作系统;
  • 转交其他 loader:将启动管理功能转交给其他 loader 负责。

由于具有菜单功能,因此我们可以选择不同的核心来启动。而由于具有控制权转交的功能,因此我们可以加载其他 boot sector 内的 loader 啦!不过 Windows 的 loader 默认不具有控制权转交的功能,因此你不能使用 Windows 的 loader 来加载 Linux 的 loader 喔!这也是为啥MBR与多重启动时,会特别强调先装 Windows 再装 Linux 的缘故。 我们将上述的三个功能用以下的图示来解释你就看的懂了!

如上图所示,我的 MBR 使用 Linux 的 grub2 这个启动管理程序,并且里面假设已经有了三个菜单, 第一个菜单可以直接指向 Linux 的核心文件并且直接加载核心来启动;第二个菜单可以将启动管理程序控制权交给 Windows 来管理,此时 Windows 的 loader 会接管启动流程,这个时候他就能够启动 windows 了。第三个菜单则是使用 Linux 在 boot sector 内的启动管理程序,此时就会跳出另一个 grub2 的菜单啦!了解了吗?

  • 菜单一:MBR(grub2) --> kernel file --> booting
  • 菜单二:MBR(grub2) --> boot sector(Windows loader) --> Windows kernel --> booting
  • 菜单三:MBR(grub2) --> boot sector(grub2) --> kernel file --> booting

而最终 boot loader 的功能就是『加载 kernel文件』啦!

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

当我们通过boot loader 的管理而开始读取核心文件后,接下来,Linux 就会将核心解压缩到主内存当中,并且利用核心的功能,开始测试与驱动各个周边设备,包括储存设备、CPU、网卡、声卡等等。此时 Linux 核心会以自己的功能重新检测一次硬件,而不一定会使用 BIOS 检测到的硬件信息,也就是说,核心此时才开始接管 BIOS 后的工作了。那么核心文件在哪里啊?一般来说,他会被放置到/boot里面,并且取名为/boot/vmlinuz 才对!

[root@study ~]# ls --format=single-column -F /boot

config-3.10.0-229.el7.x86_64                <==此版本核心被编译时选择的功能与模块配置文件

grub/                                       <==旧版grub1,不需要理会

grub2/                                      <==就是启动加载器grub2 相关资料目录

initramfs-0-rescue-309eb890d3d95ec7a.img    <==下面几个就是虚拟文件系统文件,这一个是用来救援的!

initramfs-3.10.0-229.el7.x86_64.img         <==正常开机会用到的虚拟文件系统

initramfs-3.10.0-229.el7.x86_64kdump.img    <==核心出问题时会用到的虚拟文件系统

System.map-3.10.0-229.el7.x86_64            <==核心功能放置到内存地址对应表

vmlinuz-0-rescue-309eb890d09543d95ec7a*     <==救援用的核心文件

vmlinuz-3.10.0-229.el7.x86_64*              <==就是核心文件啦!最重要者!

从上表我们也可以知道CentOS 7.x的Linux 核心为 3.10.0-229.el7.x86_64这个版本!为了硬件开发商与其他核心功能开发者的便利, 因此 Linux 核心是可以通过动态加载核心模块的 (就请想成驱动程序即可),这些核心模块就放置在 /lib/modules/目录内。由于模块放置到磁盘根目录内 (要记得 /lib 不可以与 /分别放在不同的 partition !),因此在启动的过程中核心必须要挂载根目录,这样才能够读取核心模块提供加载驱动程序的功能。而且为了担心影响到磁盘内的文件系统,因此启动过程中根目录是以只读的方式来挂载的。

一般来说,非必要的功能并且可以编译成为模块的核心功能,目前的Linux distributions 都会将他编译成为模块。因此 USB, SATA, SCSI...等磁盘设备的驱动程序通常都是以模块的方式来存在的。现在来思考一种情况,假设你的 linux 是安装在 SATA 磁盘上面的,你可以通过 BIOS 的 INT 13 取得 boot loader 与 kernel 文件来启动,然后 kernel 会开始接管系统并且检测硬件及尝试挂载根目录来取得额外的驱动程序。

问题是,核心根本不认识 SATA 磁盘,所以需要加载 SATA 磁盘的驱动程序, 否则根本就无法挂载根目录。但是 SATA 的驱动程序在 /lib/modules 内,你根本无法挂载根目录又怎么读取到 /lib/modules/ 内的驱动程序?是吧!非常的两难吧!在这个情况之下,你的 Linux 是无法顺利启动的! 那怎办?没关系,我们可以通过虚拟文件系统来处理这个问题。

虚拟文件系统 (Initial RAM Disk或Initial RAM Filesystem) 一般使用的文件名为/boot/initrd或/boot/initramfs ,这个文件的特色是,他也能够通过boot loader来加载到内存中,然后这个文件会被解压缩并且在内存当中模拟成一个根目录,而且此模拟在内存当中的文件系统能够提供一个可运行的程序,通过该程序来加载启动过程中所最需要的核心模块,通常这些模块就是 USB, RAID, LVM, SCSI 等文件系统与磁盘接口的驱动程序啦!等加载完成后,会帮助核心重新调用systemd来开始后续的正常启动流程。

如上图所示,boot loader可以加载kernel与initramfs ,然后在内存中让initramfs 解压缩成为根目录,kernel就能够借此加载适当的驱动程序,最终释放虚拟文件系统,并挂载实际的根目录文件系统,就能够开始后续的正常启动流程。更详细的 initramfs 说明,你可以自行使用 man initrd去查阅看看。下面让我们来了解一下 CentOS 7.x 的 initramfs 文件内容有什么吧!

# 1. 先来直接看一下 initramfs里面的内容有些啥文件?

[root@study ~]# lsinitrd /boot/initramfs-3.10.0-229.el7.x86_64.img

# 首先会显示出 initramfs 最前面文件头的资料介绍,这部分会占用一些容量!

Image: /boot/initramfs-3.10.0-229.el7.x86_64.img: 18M

=====================================================================

Early CPIO image

=====================================================================

drwxr-xr-x   3 root     root            0 May  4 17:56 .

-rw-r--r--   1 root     root            2 May  4 17:56 early_cpio

drwxr-xr-x   3 root     root            0 May  4 17:56 kernel

drwxr-xr-x   3 root     root            0 May  4 17:56 kernel/x86

drwxr-xr-x   2 root     root            0 May  4 17:56 kernel/x86/microcode

-rw-r--r--   1 root     root        10240 May  4 17:56 kernel/x86/microcode/GenuineIntel.bin

=====================================================================

Version: dracut-033-240.el7

 

Arguments: -f

 

dracut modules:  # 开始一堆模块的加载动作

bash

nss-softokn

.....(中间省略).....

=====================================================================

drwxr-xr-x  12 root     root            0 May  4 17:56 .

crw-r--r--   1 root     root       5,   1 May  4 17:56 dev/console

crw-r--r--   1 root     root       1,  11 May  4 17:56 dev/kmsg

crw-r--r--   1 root     root       1,   3 May  4 17:56 dev/null

.....(中间省略).....

lrwxrwxrwx   1 root     root           23 May  4 17:56 init -> usr/lib/systemd/systemd

.....(中间省略).....

drwxr-xr-x   2 root     root            0 May  4 17:56 var/lib/lldpad

lrwxrwxrwx   1 root     root           11 May  4 17:56 var/lock -> ../run/lock

lrwxrwxrwx   1 root     root           10 May  4 17:56 var/log -> ../run/log

lrwxrwxrwx   1 root     root            6 May  4 17:56 var/run -> ../run

=====================================================================

# 最后则会列出这个 initramfs里面的所有文件!也就是说,这个initramfs文件大概分为两部分,

# 先是文件头说明的资料部分,再才是真的会被核心读取的全部文件!

从上面我们大概知道了这个initramfs里面包含两大区域,一个是事先说明的一些资料,包括kernel/x86/microcode/GenuineIntel.bin这些东西。在这些资料后面,才是真的我们的核心会去读取的重要文件,如果看一下文件的内容,你会发现到init这个程序已经被systemd所取代,如果你想要进一步将这个文件解开的话,那得要先将前面的kernel/x86/microcode/GenuineIntel.bin之前的文件先去除掉,这样才能够顺利的解开。因此,得要这样操作:

# 1. 先取得 initramfs 前面应该要去除的容量有多少才对!使用下列方式取得

[root@study ~]# mkdir /dev/shm/initramfs

[root@study ~]# cd /dev/shm/initramfs

[root@study initramfs]# cpio -i -d --no-absolute-filenames  \

> -I /boot/initramfs-3.10.0-229.el7.x86_64.img

22 blocks

# 这个重点就是在前面的字节占了 block 容量,每个block 容量为512bytes,

# 因此,前面的部分就总共占了:22 * 512 = 11264 个 bytes 的意思!

# 每一个initramfs 文件的前置字节容量都不相同,所以需要先找出来去除才行!

 

# 2.将/boot/initramfs-XX 下的文件进行去除前面不需要的文件资料部分。

[root@study initramfs]# dd if=/boot/initramfs-3.10.0-229.el7.x86_64.img of=initramfs.gz \

>  bs=11264 skip=1

[root@study initramfs]# ll initramfs.gz; file initramfs.gz

-rw-r--r--. 1 root root 18558166 Aug 24 19:38 initramfs.gz

initramfs.gz: gzip compressed data, from Unix, last modified: Mon May  4 17:56:47 2015,

 max compression

 

# 3. 从上面看到文件是 gzip 压缩文件,所以将它解压缩后,再查看一下文件的类型!

[root@study initramfs]# gzip -d initramfs.gz

[root@study initramfs]# file initramfs

initramfs: ASCII cpio archive (SVR4 with no CRC)

 

# 4. 解开后又产生一个cpio文件,得要将它用 cpio 的方法解开!加上不要绝对路径的参数较保险!

[root@study initramfs]# cpio -i -d -H newc --no-absolute-filenames < initramfs

[root@study initramfs]# ll

lrwxrwxrwx.  1 root root        7 Aug 24 19:40 bin -> usr/bin

drwxr-xr-x.  2 root root       42 Aug 24 19:40 dev

drwxr-xr-x. 12 root root     4096 Aug 24 19:40 etc

lrwxrwxrwx.  1 root root       23 Aug 24 19:40 init -> usr/lib/systemd/systemd

-rw-r--r--.  1 root root 42263552 Aug 24 19:38 initramfs

lrwxrwxrwx.  1 root root        7 Aug 24 19:40 lib -> usr/lib

lrwxrwxrwx.  1 root root        9 Aug 24 19:40 lib64 -> usr/lib64

drwxr-xr-x.  2 root root        6 Aug 24 19:40 proc

drwxr-xr-x.  2 root root        6 Aug 24 19:40 root

drwxr-xr-x.  2 root root        6 Aug 24 19:40 run

lrwxrwxrwx.  1 root root        8 Aug 24 19:40 sbin -> usr/sbin

-rwxr-xr-x.  1 root root     3041 Aug 24 19:40 shutdown

drwxr-xr-x.  2 root root        6 Aug 24 19:40 sys

drwxr-xr-x.  2 root root        6 Aug 24 19:40 sysroot

drwxr-xr-x.  2 root root        6 Aug 24 19:40 tmp

drwxr-xr-x.  7 root root       61 Aug 24 19:40 usr

drwxr-xr-x.  3 root root       47 Aug 24 19:40 var

# 看吧!上面几乎就像是一个小型的文件系统根目录,这样就能让 kernel去加载了!

 

# 4. 接下来瞧一瞧到底这个小型的文件系统中,systemd 是要以哪个target来执行启动呢?

[root@study initramfs]# ll usr/lib/systemd/system/default.target

lrwxrwxrwx. 1 root root 13 Aug 24 19:40 usr/lib/systemd/system/default.target -> initrd.target

 

# 5. 最终,让我们瞧一瞧系统内默认的 initrd.target 依赖的所有服务文件吧!

[root@study initramfs]# systemctl list-dependencies initrd.target

initrd.target

├─dracut-cmdline.service

.....(中间省略).....

├─basic.target

│ ├─alsa-restore.service

.....(中间省略).....

│ ├─slices.target

│ │ ├─-.slice

│ │ └─system.slice

│ ├─sockets.target

│ │ ├─dbus.socket

.....(中间省略).....

│ │ └─systemd-udevd-kernel.socket

│ ├─sysinit.target

│ │ ├─dev-hugepages.mount

.....(中间省略).....

│ │ ├─local-fs.target

│ │ │ ├─-.mount

│ │ │ ├─boot.mount

.....(中间省略).....

│ │ └─swap.target

│ │   ├─dev-centos-swap.swap

.....(中间省略).....

│ │   └─dev-mapper-centos\x2dswap.swap

│ └─timers.target

│   └─systemd-tmpfiles-clean.timer

├─initrd-fs.target

└─initrd-root-fs.target

# 通过 systemd 的方式,一个一个的将所有的检测与服务加载到系统中!

通过上面解开initramfs的结果,你会知道其实initramfs就是一个小型的根目录,这个小型根目录里面也是通过systemd来进行管理,同时查看default.target的链接,会发现其实这个小型系统就是通过initrd.target来启动,而initrd.target也是需要读入一堆例如basic.target,sysinit.target等等的硬件检测、核心功能启用的过程,然后开始让系统顺利运行。最终才卸载initramfs的小型文件系统,挂载实际的系统根目录。

此外,initramfs并没有包含所有,它仅是包含启动过程会用到的核心模块而已。所以如果你在initramfs里面去找modules这个关键字的话,就可以发现主要的核心模块大概就是SCSI、VIRTIO、RAID等等跟磁盘相关性比较高的模块就是了。现在由于磁盘大部分就是使用STAT这玩意儿,并没有IDE的接口。所以,没有initramfs的话,你的Linux几乎就是不能顺利启动的啦!除非你将SATA的模块直接编译到核心去了!

在核心完整的加载后,您的主机应该就开始正常的运行了,接下来,就是开始执行系统的第一个程序:systemd

第一个程序 systemd 及使用 default.target 进入启动分析

在核心加载完毕、进行完硬件检测与驱动程序加载后,此时你的主机硬件应该已经准备就绪了(ready) ,此时核心会主动的调用第一个程序,那就是 systemd。你会发现systemd的PID号码是1。systemd最主要的功能就是准备软件运行的环境,包括系统的主机名称、网络配置、语言处理、文件系统格式及其他服务的启动等。而所有的动作都会通过 systemd的默认启动服务集合,亦即是 /etc/system/system/default.target来规划。另外,systemd已经不再使用沿用多年的system V的runlevel了。

  • 常见的操作环境target 与兼容 runlevel 的等级

可以作为默认的操作环境(default.target)的主要项目有:

multi-user.target 以及 graphical.target这两个。当然还有某些比较特殊的操作环境,包括rescue.target,emergency.target,shutdown.target等等,以及initrd.target。

但是过去的system V使用的是一个称为runlevel(执行等级)的概念来启动系统的,systemd为了兼容老的system V,所以也将runlevel与操作环境相结合,你可以使用下面的方式来查询两者间的对应关系:

[root@study ~]# ll -d /usr/lib/systemd/system/runlevel*.target | cut -c 28-

May  4 17:52 /usr/lib/systemd/system/runlevel0.target -> poweroff.target

May  4 17:52 /usr/lib/systemd/system/runlevel1.target -> rescue.target

May  4 17:52 /usr/lib/systemd/system/runlevel2.target -> multi-user.target

May  4 17:52 /usr/lib/systemd/system/runlevel3.target -> multi-user.target

May  4 17:52 /usr/lib/systemd/system/runlevel4.target -> multi-user.target

May  4 17:52 /usr/lib/systemd/system/runlevel5.target -> graphical.target

May  4 17:52 /usr/lib/systemd/system/runlevel6.target -> reboot.target

如果你之前已经使用过system V的方式来管理系统的话,那应该会知道切换执行等级可以使用『 init 3 』转成文字界面,『 init 5 』转成图形界面吧?这个init程序依然是保留下来的,只是init 3会相当于systemctl isolate multi-user.target就是了,如果做个完整的对比,这两个东西的对应为:

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 的处理过程

如前所述,当我们取得了/etc/systemd/system/default.target这一个默认操作界面的配置之后,接下来系统帮我们做了什么呢? 首先,它会链接到 /usr/lib/systemd/system/这个目录下去取得 multi-user.target 或 graphical.target这两个其中的一 (当然,鸟哥说的是正常的进入Linux 操作环境的情況下!),假设我们是使用 graphical.target 好了,接下来 systemd会去找两个地方的配置, 就是如下的目录:

  • /etc/systemd/system/graphical.target.wants/:使用者配置加载的 unit
  • /usr/lib/systemd/system/graphical.target.wants/:系统默认加载的 unit

然后再由 /usr/lib/systemd/system/graphical.target 这个配置文件内发现如下的资料:

[root@study ~]# cat /usr/lib/systemd/system/graphical.target

[Unit]

Description=Graphical Interface

Documentation=man:systemd.special(7)

Requires=multi-user.target

After=multi-user.target

Conflicts=rescue.target

Wants=display-manager.service

AllowIsolate=yes

 

[Install]

Alias=default.target

这表示graphical.target必须要完成multi-user.target之后才能够执行,而执行完graphical.target之后,还得要启动display-manager.service 才行的意思。好了!那么通过同样的方式,我们来找找multi-user.target要执行完毕得要加载的项目有哪些呢?

# 先来看看 multi-user.target配置文件内定义了相依赖的操作环境有哪些呢?

[root@study ~]# cat /usr/lib/systemd/system/multi-user.target

[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

 

[Install]

Alias=default.target

 

# 然后看看系统默认要加载的 unit 有哪些?

[root@study ~]# ls /usr/lib/systemd/system/multi-user.target.wants

brandbot.path  plymouth-quit.service           systemd-logind.service

dbus.service   plymouth-quit-wait.service      systemd-user-sessions.service

getty.target   systemd-ask-password-wall.path

 

# 使用者自定义要加载的 unit 又有哪些呢?

[root@study ~]# ls /etc/systemd/system/multi-user.target.wants

abrt-ccpp.service    crond.service           mdmonitor.service       sshd.service

abrtd.service        hypervkvpd.service      ModemManager.service    sysstat.service

abrt-oops.service    hypervvssd.service      NetworkManager.service  tuned.service

abrt-vmcore.service  irqbalance.service      postfix.service         vmtoolsd.service

abrt-xorg.service    kdump.service           remote-fs.target        vsftpd2.service

atd.service          ksm.service             rngd.service            vsftpd.service

auditd.service       ksmtuned.service        rsyslog.service

backup2.timer        libstoragemgmt.service  smartd.service

backup.timer         libvirtd.service        sshd2.service

通过上面的结果,我们又能知道multi-user.target需要在basic.target执行完毕才能够加载上述的许多unit,然后再去basic.target里面找资料等等,最终这些资料就可以通过systemctl list-dependencies graphical.target这个命令来列出所有的依赖的服务,这就是systemd的调用所需要的服务的过程。

Tips:
要知道系统的服务启动的过程,最简单的方法就是systemctl list-dependencies graphical.target这个命令,只是,如果你想要知道背后的配置文件意义,那就是分别去找出/etc/usr/lib下面的graphical.target.wants/目录下的文件就对了。当然,配置文件脚本里面的Requires这个配置项所代表的服务,也是需要先加载的。

 

大概分析一下『 systemctl list-dependencies graphical.target 』所输出的依赖性服务,基本上我们 CentOS 7.x 的 systemd 启动过程大概是这样:

  1. local-fs.target + swap.target:这两个target主要在挂载本机 /etc/fstab 里面所定义的文件系统与相关的内存交换空间。
  2. sysinit.target:这个target 主要在检测硬件,加载所需要的核心模块等动作。
  3. basic.target:加载主要的周边硬件驱动程序与防火墙相关任务
  4. multi-user.target下的其它一般系统或网络服务的加载
  5. 图形界面相关服务如 gdm.service 等其他服务的加载

除了第一步骤local-fs.target, swap.target 是通过/etc/fstab 来进行挂载的动作之外,那其他的 target 有做啥动作呢?简单得来说说!

systemd 执行sysinit.target 初始化系统、basic.target 准备系统

如果你自己使用『 systemctl list-dependencies sysinit.target 』来瞧瞧的话,那就会看到许多相依赖的服务,这些服务你应该要一个一个去查看配置脚本的内容,就能够大致理解每个服务的意义。基本上,我们可以将这些服务归类成为几个大项就是了:

  • 特殊文件系统设备的挂载:包括dev-hugepages.mount,dev-mqueue.mount等挂载服务,主要在挂载跟大容量内存分页使用与信息队列的功能。挂载成功后,会在/dev下面建立/dev/hugepages/,/dev/mqueue等目录;
  • 特殊文件系统的启用:包括磁盘陈列、网络磁盘(iscsi)、LVM文件系统、文件系统多路径服务(multipath)等等,也会在这里被检测与使用到。
  • 启动过程的信息传送与动画执行:使用plymouthd服务搭配plymouth命令来传送动画与信息
  • 日志管理服务的使用:就是systemd-journald这个服务的启用
  • 加载额外的核心模块:通过/etc/modules-load.d/*.conf文件的配置,让核心额外加载管理员所需要的核心模块
  • 加载额外的核心参数配置:包括/etc/sysctl.conf以及/etc/sysctl.d/*.conf内的配置
  • 启动系统的随机数生成器:随机数生成器可以帮助系统进行一些密码加密的功能
  • 配置终端机(console)字符
  • 启动动态设备管理:就是udevd这个家伙,用在动态对应实际设备存取与设备文件名的一个服务,相当重要,也是在这里启动的。

不论你即将使用哪种操作环境来使用系统,这个sysinit.target几乎都是必要的工作,从上面你也可以看的出来,基本的核心功能、文件系统、文件系统设备的驱动等等,都在这个时刻处理完毕。所以,这个sysinit.target的阶段是挺重要的。

执行完sysinit.target之后,再来则是basic.target这个项目了。

sysinit.target在初始化系统,而这个basic.target则是一个最基本的作业系统了,这个basic.target的阶段主要启动的服务大概有这些:

  • 加载alsa音效驱动程序:这个alsa是个音效相关的驱动程序,会让你的系统有音效产生;
  • 加载firewalld防火墙:CentOS 7.x以后使用firewalld取代iptables的防火墙配置,虽然最终都是使用iptables的架构,不过在配置上差很多;
  • 加载CPU的微指令功能;
  • 启动与配置SELinux 的安全上下文:如果由disable的状态改成enable的状态,或者是管理员配置强制重新配置一次SELinux的安全上下文,也在这个阶段处理;
  • 将目前的启动过程所产生的启动信息写入到/var/log/dmesg文件中;
  • 由/etc/sysconfig/modules/*.modules及/etc/rc.modules加载管理员指定的模块;
  • 加载systemd支持的timer功能;

在这个阶段完成之后,你的系统已经可以顺利的运行,就差一堆你需要的登陆服务、网络服务、本机认证服务等等的service,于是就可以进入下个服务启动的阶段了。

systemd 启动multi-user.target 下的服务

在加载核心驱动硬件后,经过sysinit.target的初始化过程让系统可以存取之后,加上basic.target让系统成为操作系统的基础,之后就是服务器顺利运行时,需要的各种主机服务以及提供服务器功能的网络服务的启动了。这些服务的启动则大多是在multi-user.target这个操作环境下面,你可以到/etc/systemd/system/multi-user.target.wants/里面去瞧瞧默认要被启动的服务。

也就是说,一般来说服务的启动脚本配置都是放在下面的目录中:

  • /usr/lib/systemd/system (系統默认的服务启动的脚本配置 )
  • /etc/systemd/system (管理员自己开发与配置的脚本配置)

而使用者针对主机的本机服务与服务器网络服务的各项unit若要enable的话,就是将它放到/etc/system/system/multi-user.target.wants/这个目录下做个链接,这样就可以在启动的时候去启动它。你在使用systemctl enable/disable时,系统的回应是什么呢?

# 将vsftpd.service 先 disable 再 enable 看看输出的信息是什么?

[root@study ~]# systemctl disable vsftpd.service

rm '/etc/systemd/system/multi-user.target.wants/vsftpd.service'

 

[root@study ~]# systemctl enable vsftpd.service

ln -s '/usr/lib/systemd/system/vsftpd.service' '/etc/systemd/system/multi-user.target.

 wants/vsftpd.service'

有没有发现亮点了?不是从/etc/system/system/multi-user.target.wants/里面删除链接文件,就是建立链接文件,这样说,理解吧?你当然不需要手动做这些链接,而是使用systemcal来处理即可!另外,这些程序除非在脚本配置里面原本就有定义服务的依赖,这样才会有顺序的启动之外,大多数的服务都是同时启动的!这就是systemd的功能。

  • 兼容 system V 的 rc-local.service

另外,过去用过Linux的朋友大概都知道,当系统完成启动后,还想要让系统额外执行某些程序的话,可以将该程序指令或脚本的绝对路径写入到/etc/rc.d/rc.local这个文件去!新的systemd机制中,它建议直接写一个systemd的启动脚本配置文件到/etc/system/system/下面,然后使用systemctl enable的方式来配置启用它,而不要直接使用rc.local这个文件啦!

但是像鸟哥这种老人家就是喜欢将启动后要立刻执行的许多管理员自己的脚本,将它写入到/etc/rc.d/rc.local去嘛!那新版的systemd有没有支持呢?当然有!那就是rc-local.server这个服务的功能了!这个服务不需要启动,它会自己判断/etc/rc.d/rc.local是否具有可执行的权限来判断要不要启动这个服务!你可以这样检查看看:

# 1. 先看一下 /etc/rc.d/rc.local 的权限,然后检查multi-user.target有沒有这个服务

[root@study ~]# ll /etc/rc.d/rc.local

-rw-r--r--. 1 root root 473 Mar  6 13:48 /etc/rc.d/rc.local

 

[root@study ~]# systemctl status rc-local.service

rc-local.service - /etc/rc.d/rc.local Compatibility

   Loaded: loaded (/usr/lib/systemd/system/rc-local.service; static)

   Active: inactive (dead)

 

[root@study ~]# systemctl list-dependencies multi-user.target | grep rc-local

# 明明就有这个服务,但是 rc.local 不具有可执行 (x) 的权限,因此这个服务不会被执行

 

# 2. 加入可执行权限后,再看一下 rc-local 是否可被启用!

[root@study ~]# chmod a+x /etc/rc.d/rc.local; ll /etc/rc.d/rc.local

-rwxr-xr-x. 1 root root 473 Mar  6 13:48 /etc/rc.d/rc.local

 

[root@study ~]# systemctl daemon-reload

[root@study ~]# systemctl list-dependencies multi-user.target | grep rc-local

├─rc-local.service   # 这个服务确实被记录到启动的环境下!

通过这个chmod a+x /etc/rc.d/rc.local 的步骤,你的许多脚本就可以放在 /etc/rc.d/rc.local这个文件中,系统在每次启动都会去执行这个文件内的指令喔!非常简单吧!

  • 提供 tty 界面与登陆的服务

在 multi-user.target下面还有个getty.target的操作界面项目,这个项目就是我们tty终端机界面的项目。能不能提供适当的登陆服务也是multi-user.target下面的内容!包括systemd-logind.service,system-user-sessions.service等服务。

比较有趣的地方是,由于服务都是同步运行,不一定哪个服务先启动完毕。如果getty服务先启动完毕时,你会发现到有可用的终端机尝试让你登陆系统了。问题是,如果systemd-logind.service或systemd-user-sessions.service服务还没有执行完毕的话,那么你还是无法登陆系统的。

systemd 启动graphical.target下的服务

如果你的default.target 是 multi-user.target 的话,那么这个步骤就不会运行。反之,如果是graphical.target 的话,那么systemd就会开始加载用户管理服务与图形界面管理(windows display manager,DM)等,启动图形界面来让用户以图形界面登陆系统,如果你对于graphical.target 多了哪些服务有兴趣,那就来检查看看:

[root@study ~]# systemctl list-dependencies graphical.target

graphical.target

├─accounts-daemon.service

├─gdm.service

├─network.service

├─rtkit-daemon.service

├─systemd-update-utmp-runlevel.service

└─multi-user.target

  ├─abrt-ccpp.service

.....(下面省略).....

事实上就是多了上面列出来的这些服务而已~大多数都是图形界面账号管理的功能,至于实际让用户可以登陆的服务,倒是那个 gdm.service 哩! 如果你去瞧瞧 gdm.service 的内容,就会发现最重要的执行文件是 /usr/sbin/gdm 喔!那就是让使用者可以利用图形界面登入的最重要服务啰!

到此为止,systemd 就已经完整的处理完毕,你可以使用图形界面或文字界面的方式来登入系统,系统也顺利的启动完毕,也能够将你写入到 /etc/rc.d/rc.local 的脚本实际执行一次。那如果默认是图形界面 (graphical.target) 但是想要关掉而进入文字界面 (multi-user.target) 呢? 很简单啊!使用『 systemctl isolate multi-user.target 』即可!如果使用『 init 3 』呢?也是可以啦! 只是系统实际执行的还是『 systemctl isolate multi-user.target 』就是了!

启动过程会用到的主要配置文件

基本上, systemd 有自己的配置文件处理方式,不过为了兼容 system V ,其实很多的服务脚本配置还是会读取位于 /etc/sysconfig/ 下的环境配置文件! 下面我们就来谈谈几个常见的比较重要的配置文件。

关于模块: /etc/modprobe.d/*.conf /etc/modules-load.d/*.conf

有两个地方可以处理模块加载的问题,包括:

  • /etc/modules-load.d/*.conf:单纯要核心加载模块的位置;
  • /etc/modprobe.d/*.conf:可以加上模块参数的位置

基本上 systemd 已经帮我们将启动会用到的驱动程序全部加载了,因此这个部分你应该无须修改才对。不过, 如果你有某些特定的参数要处理时,应该就得要在这里进行了。举例来说,我们将vsftp服务的端口更改到 555这个号码上去了!那我们可能需要修改防火墙配置,其中一个针对 FTP 很重要的防火墙模块为 nf_conntrack_ftp, 因此,你可以将这个模块写入到系统启动过程中,例如:

[root@study ~]# vim /etc/modules-load.d/vbird.conf
nf_conntrack_ftp

一个模块(驱动程序)写一行~然后,上述的模块基本上是针对 FTP端口,亦即 port 21 所配置的,如果需要调整到 port 555 的话, 得要外带参数才行!模块外加参数的配置方式得要写入到另一个地方喔!

[root@study ~]# vim /etc/modprobe.d/vbird.conf
options nf_conntrack_ftp ports=555

之后重新启动就能够顺利的加载并且处理好这个模块了。不过,如果你不想要启动测试,想现在处理呢?有个方式可以来进行看看:

[root@study ~]# lsmod | grep nf_conntrack_ftp
# 没东西!因为还没有加载这个模块!所以不会出现任何信息!
 
[root@study ~]# systemctl restart systemd-modules-load.service
[root@study ~]# lsmod | grep nf_conntrack_ftp
nf_conntrack_ftp       18638  0
nf_conntrack          105702  1 nf_conntrack_ftp

通过上述的方式,你就可以在启动的时候将你所需要的驱动程序加载或者是调整这些模块的外加参数。

  • /etc/sysconfig/*

还有哪些常见的环境配置文件呢?我们找几个比较重要的来谈谈:

  • authconfig:
    这个文件主要在定义使用者的身份认证的机制,包括是否使用本机的 /etc/passwd, /etc/shadow 等,以及 /etc/shadow密码记录使用何种加密算法,还有是否使用外部密码服务器提供的账号验证 (NIS, LDAP) 等。系统默认使用 SHA512 加密算法,并且不使用外部的身份验证机制;另外,不建议手动修改这个文件喔!你应该使用『 authconfig-tui 』指令来修改较好!
  • cpupower:
    如果你有启动 cpupower.service 服务时,他就会读取这个配置文件。主要是 Linux 核心如何操作 CPU 的原则。 一般来说,启动 cpupower.service 之后,系统会让CPU 以最大效能的方式来运行,否则默认就是用多少算多少的模式来处理的。
  • firewalld, iptables-config, ip6tables-config, ebtables-config:
    与防火墙服务的启动外带的参数有关。
  • network-scripts/:
    至于network-scripts里面的文件,则是主要用在配置网卡。

 

猜你喜欢

转载自blog.csdn.net/byrgzs/article/details/83471926