天天看點

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:

我們知道每顆硬碟的最前面區域含有MBR或GPT分區表提供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 的相關性會有點像下圖:

Linux學習:Linux的系統啟動過程
Linux學習:Linux的系統啟動過程

如上圖所示,每個作業系統預設是會安裝一套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 的緣故。 我們将上述的三個功能用以下的圖示來解釋你就看的懂了!

Linux學習:Linux的系統啟動過程
Linux學習: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 才對!

[[email protected] ~]# 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來開始後續的正常啟動流程。

Linux學習:Linux的系統啟動過程
Linux學習:Linux的系統啟動過程

如上圖所示,boot loader可以加載kernel與initramfs ,然後在記憶體中讓initramfs 解壓縮成為根目錄,kernel就能夠借此加載适當的驅動程式,最終釋放虛拟檔案系統,并挂載實際的根目錄檔案系統,就能夠開始後續的正常啟動流程。更詳細的 initramfs 說明,你可以自行使用 man initrd去查閱看看。下面讓我們來了解一下 CentOS 7.x 的 initramfs 檔案内容有什麼吧!

# 1. 先來直接看一下 initramfs裡面的内容有些啥檔案?

[[email protected] ~]# 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 前面應該要去除的容量有多少才對!使用下列方式取得

[[email protected] ~]# mkdir /dev/shm/initramfs

[[email protected] ~]# cd /dev/shm/initramfs

[[email protected] 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 下的檔案進行去除前面不需要的檔案資料部分。

[[email protected] initramfs]# dd if=/boot/initramfs-3.10.0-229.el7.x86_64.img of=initramfs.gz \

>  bs=11264 skip=1

[[email protected] 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 壓縮檔案,是以将它解壓縮後,再檢視一下檔案的類型!

[[email protected] initramfs]# gzip -d initramfs.gz

[[email protected] initramfs]# file initramfs

initramfs: ASCII cpio archive (SVR4 with no CRC)

# 4. 解開後又産生一個cpio檔案,得要将它用 cpio 的方法解開!加上不要絕對路徑的參數較保險!

[[email protected] initramfs]# cpio -i -d -H newc --no-absolute-filenames < initramfs

[[email protected] 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來執行啟動呢?

[[email protected] 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 依賴的所有服務檔案吧!

[[email protected] 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與操作環境相結合,你可以使用下面的方式來查詢兩者間的對應關系:

[[email protected] ~]# 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 這個配置檔案内發現如下的資料:

[[email protected] ~]# 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配置檔案内定義了相依賴的操作環境有哪些呢?

[[email protected] ~]# 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 有哪些?

[[email protected] ~]# 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 又有哪些呢?

[[email protected] ~]# 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 看看輸出的資訊是什麼?

[[email protected] ~]# systemctl disable vsftpd.service

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

[[email protected] ~]# 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有沒有這個服務

[[email protected] ~]# ll /etc/rc.d/rc.local

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

[[email protected] ~]# 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)

[[email protected] ~]# systemctl list-dependencies multi-user.target | grep rc-local

# 明明就有這個服務,但是 rc.local 不具有可執行 (x) 的權限,是以這個服務不會被執行

# 2. 加入可執行權限後,再看一下 rc-local 是否可被啟用!

[[email protected] ~]# 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

[[email protected] ~]# systemctl daemon-reload

[[email protected] ~]# 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 多了哪些服務有興趣,那就來檢檢視看:

[[email protected] ~]# 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, 是以,你可以将這個子產品寫入到系統啟動過程中,例如:

[[email protected] ~]# vim /etc/modules-load.d/vbird.conf      
nf_conntrack_ftp      

一個子產品(驅動程式)寫一行~然後,上述的子產品基本上是針對 FTP端口,亦即 port 21 所配置的,如果需要調整到 port 555 的話, 得要外帶參數才行!子產品外加參數的配置方式得要寫入到另一個地方喔!

[[email protected] ~]# vim /etc/modprobe.d/vbird.conf      
options nf_conntrack_ftp ports=555      

之後重新啟動就能夠順利的加載并且處理好這個子產品了。不過,如果你不想要啟動測試,想現在處理呢?有個方式可以來進行看看:

[[email protected] ~]# lsmod | grep nf_conntrack_ftp      
# 沒東西!因為還沒有加載這個子產品!是以不會出現任何資訊!      
[[email protected] ~]# systemctl restart systemd-modules-load.service      
[[email protected] ~]# 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裡面的檔案,則是主要用在配置網卡。

繼續閱讀