1. 程式人生 > >Linux學習:Linux的系統啟動過程

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 才對!

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

如上圖所示,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裡面的檔案,則是主要用在配置網絡卡。