引用:《鸟哥的Linux私房菜基础篇第三版》
启动过程一览
既然启动是很严肃的一件事,那我们就来了解一下整个启动的过程吧!好让大家比较容易发现启动过程里面可能会发生问题的地方,以及出现问题后的解决之道! 不过,由于启动的过程中,那个启动管理程序(Boot Loader) 使用的软件可能不一样,例如目前各大 Linux distributions 的主流为 grub2,但早期 Linux 默认是使用 grub1或LILO。但无论如何,我们总是得要了解整个 boot loader 的工作情况,才能了解为何进行多重启动的配置时,老是听人家讲要先安装 Windows 再安装 Linux 的原因。
假设以个人计算机架设的 Linux 主机为例,当你按下电源按键后计算机硬件会主动的读取 BIOS或UEFI BIOS 来加载硬件信息及进行硬件系统的自我测试,之后系统会主动的去读取第一个可启动的设备 (由 BIOS 配置的) ,此时就可以读入启动管理程序了。
启动管理程序可以指定使用哪个核心文件来启动,并实际加载核心到内存当中解压缩与运行,此时核心就能够开始在内存内活动,并检测所有硬件信息与加载适当的驱动程序来使整个主机开始运行,等到核心检测硬件与加载驱动程序完毕后,一个操作系统就开始在你的 PC 上面运行了。
主机系统开始运行后,此时 Linux 才会调用外部程序开始准备软件运行的环境, 并且实际的加载所有系统运行所需要的软件程序哩!最后系统就会开始等待你的登陆与操作啦!简单来说,系统启动的经过可以总结成以下的过程:
大概的过程就是上面写的那个样子啦,你会发现systemd这个家伙占的比重非常重!那每一个程序的内容主要是在干嘛呢?下面就分别来谈一谈吧!
BIOS, boot loader 与 kernel 加载
我们这里为了讲解方便,将后续会用到的专有名词先做个综合解释:
在个人计算机架构下,你想要启动系统首先就得要让系统去加载 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:
我们知道每颗硬盘的最前面区域含有MBR或GPT分区表提供loader的区域,那么如果我的主机上面有两颗硬盘的话,系统会去哪颗硬盘的最前面区域读取boot loader 呢?这个就得要看 BIOS 的配置了。基本上,我们常常讲的『系统的MBR』其实指的是第一个启动设备的MBR 才对!所以,改天如果你要将启动管理程序安装到某颗硬盘的MBR时,要特别注意当时系统的『第一个启动设备』是哪个,否则会安装到错误的硬盘上面的MBR 喔!重要重要!
刚刚说到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 主要的功能如下:
由于具有菜单功能,因此我们可以选择不同的核心来启动。而由于具有控制权转交的功能,因此我们可以加载其他 boot sector 内的 loader 啦!不过 Windows 的 loader 默认不具有控制权转交的功能,因此你不能使用 Windows 的 loader 来加载 Linux 的 loader 喔!这也是为啥MBR与多重启动时,会特别强调先装 Windows 再装 Linux 的缘故。 我们将上述的三个功能用以下的图示来解释你就看的懂了!
如上图所示,我的 MBR 使用 Linux 的 grub2 这个启动管理程序,并且里面假设已经有了三个菜单, 第一个菜单可以直接指向 Linux 的核心文件并且直接加载核心来启动;第二个菜单可以将启动管理程序控制权交给 Windows 来管理,此时 Windows 的 loader 会接管启动流程,这个时候他就能够启动 windows 了。第三个菜单则是使用 Linux 在 boot sector 内的启动管理程序,此时就会跳出另一个 grub2 的菜单啦!了解了吗?
而最终 boot loader 的功能就是『加载 kernel文件』啦!
当我们通过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
在核心加载完毕、进行完硬件检测与驱动程序加载后,此时你的主机硬件应该已经准备就绪了(ready) ,此时核心会主动的调用第一个程序,那就是 systemd。你会发现systemd的PID号码是1。systemd最主要的功能就是准备软件运行的环境,包括系统的主机名称、网络配置、语言处理、文件系统格式及其他服务的启动等。而所有的动作都会通过 systemd的默认启动服务集合,亦即是 /etc/system/system/default.target来规划。另外,systemd已经不再使用沿用多年的system V的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 |
如前所述,当我们取得了/etc/systemd/system/default.target这一个默认操作界面的配置之后,接下来系统帮我们做了什么呢? 首先,它会链接到 /usr/lib/systemd/system/这个目录下去取得 multi-user.target 或 graphical.target这两个其中的一 (当然,鸟哥说的是正常的进入Linux 操作环境的情況下!),假设我们是使用 graphical.target 好了,接下来 systemd会去找两个地方的配置, 就是如下的目录:
然后再由 /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 启动过程大概是这样:
除了第一步骤local-fs.target, swap.target 是通过/etc/fstab 来进行挂载的动作之外,那其他的 target 有做啥动作呢?简单得来说说!
如果你自己使用『 systemctl list-dependencies sysinit.target 』来瞧瞧的话,那就会看到许多相依赖的服务,这些服务你应该要一个一个去查看配置脚本的内容,就能够大致理解每个服务的意义。基本上,我们可以将这些服务归类成为几个大项就是了:
不论你即将使用哪种操作环境来使用系统,这个sysinit.target几乎都是必要的工作,从上面你也可以看的出来,基本的核心功能、文件系统、文件系统设备的驱动等等,都在这个时刻处理完毕。所以,这个sysinit.target的阶段是挺重要的。
执行完sysinit.target之后,再来则是basic.target这个项目了。
sysinit.target在初始化系统,而这个basic.target则是一个最基本的作业系统了,这个basic.target的阶段主要启动的服务大概有这些:
在这个阶段完成之后,你的系统已经可以顺利的运行,就差一堆你需要的登陆服务、网络服务、本机认证服务等等的service,于是就可以进入下个服务启动的阶段了。
systemd 启动multi-user.target 下的服务
在加载核心驱动硬件后,经过sysinit.target的初始化过程让系统可以存取之后,加上basic.target让系统成为操作系统的基础,之后就是服务器顺利运行时,需要的各种主机服务以及提供服务器功能的网络服务的启动了。这些服务的启动则大多是在multi-user.target这个操作环境下面,你可以到/etc/systemd/system/multi-user.target.wants/里面去瞧瞧默认要被启动的服务。
也就是说,一般来说服务的启动脚本配置都是放在下面的目录中:
而使用者针对主机的本机服务与服务器网络服务的各项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的功能。
另外,过去用过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这个文件中,系统在每次启动都会去执行这个文件内的指令喔!非常简单吧!
在 multi-user.target下面还有个getty.target的操作界面项目,这个项目就是我们tty终端机界面的项目。能不能提供适当的登陆服务也是multi-user.target下面的内容!包括systemd-logind.service,system-user-sessions.service等服务。
比较有趣的地方是,由于服务都是同步运行,不一定哪个服务先启动完毕。如果getty服务先启动完毕时,你会发现到有可用的终端机尝试让你登陆系统了。问题是,如果systemd-logind.service或systemd-user-sessions.service服务还没有执行完毕的话,那么你还是无法登陆系统的。
如果你的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
有两个地方可以处理模块加载的问题,包括:
基本上 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 |
通过上述的方式,你就可以在启动的时候将你所需要的驱动程序加载或者是调整这些模块的外加参数。
还有哪些常见的环境配置文件呢?我们找几个比较重要的来谈谈: