Systemd 技术原理实践
來源于:干貨分享 | Systemd 技術原理&實踐(上)和 干貨分享 | Systemd 技術原理&實踐(下)
一、systemd 介紹
1 systemd 的起源
關于 systemd 的起源,首先要從 Linux 的 init 程序說起。Linux 系統在啟動過程中,內核完成初始化以后,由內核第一個啟動的程序便是 init 程序,路徑為 /sbin/init(為一個軟連接,鏈接到真實的 init 進程),其 PID 為1,它為系統里所有進程的“祖先”,Linux 中所有的進程都由 init 進程直接或間接進行創建并運行,init 進程以守護進程的方式存在,負責組織與運行系統的相關初始化工作,讓系統進入定義好的運行模式,如命令行模式或圖形界面模式。
init 程序的發展,大體上可分為三個階段:sysvinit->upstart->systemd,根據 init 進程的發展特性,可以簡單理解為如下:
- sysvinit:init 系統通過 shell 腳本以串行的方式啟動系統服務,下一個進程必須等待上一個進程啟動完成后才能開始啟動,因此系統啟動的過程比較慢。
- upstart:在 sysvinit 的基礎上,把一些沒有關聯的程序并行啟動,以提高啟動的速度,但是存在依賴關系的程序仍然為串行啟動。
- systemd:通過套接字激活的機制,讓所有無論有無依賴關系的程序全部并行啟動,并且僅按照系統啟動的需要啟動相應的服務,最大化提高開機啟動速度。
目前優麒麟操作系統使用的就為 systemd。systemd 的意思為 system daemon,意為系統守護進程,由 Lennart Poettering 帶頭開發,采用更加優秀的服務框架,并且與老的 sysvinit 兼容,其設計目的就是克服 sysvinit 與 upstart 的缺點,進一步地提高啟動速度。目前主流的系統中,systemd 的守護進程主要分為系統態(system)與用戶態(user),可以在 ps -ef 中看到 systemd 的守護進程,如下:
$ ps -ef UID PID PPID C STIME TTY TIME CMD root 1 0 0 08:04 ? 00:00:20 /sbin/init splash root 2 0 0 08:04 ? 00:00:00 [kthreadd] root 3 2 0 08:04 ? 00:00:00 [rcu_gp] root 4 2 0 08:04 ? 00:00:00 [rcu_par_gp] root 6 2 0 08:04 ? 00:00:00 [kworker/0:0H-events_highpri] root 9 2 0 08:04 ? 00:00:00 [mm_percpu_wq] root 10 2 0 08:04 ? 00:00:00 [rcu_tasks_kthre]PID 為1的進程/sbin/init 即是 system 態的 systemd,它為一個軟鏈接,指向真實的 systemd 路徑,在優麒麟操作系統中一般放在/lib/systemd/目錄:
$ ll /sbin/init lrwxrwxrwx 1 root root 20 9月 8 02:37 /sbin/init -> /lib/systemd/systemd*systemd 為進程服務集合的總稱,它包含許多的進程,負責控制、管理系統的資源,其中包括 systemd-login,負責用戶登錄相關信息的創建、修改與刪除;systemd-sleep 控制系統的休眠、睡眠狀態切換等等。在優麒麟操作系統下,它們主要集中在/lib/systemd/文件目錄:
$ ls /lib/systemd/ boot systemd-cryptsetup systemd-pstore systemd-udevd catalog systemd-dissect systemd-quotacheck systemd-update-utmp libsystemd-shared-245.so systemd-fsck systemd-random-seed systemd-user-runtime-dir logind.conf.d systemd-fsckd systemd-remount-fs systemd-user-sessions network systemd-growfs systemd-reply-password systemd-veritysetup ntp-units.d systemd-hibernate-resume systemd-resolved systemd-volatile-root resolv.conf systemd-hostnamed systemd-rfkill system-environment-generators set-cpufreq systemd-initctl systemd-shutdown system-generators system systemd-journald systemd-sleep system-preset systemd systemd-localed systemd-socket-proxyd system-shutdown systemd-ac-power systemd-logind systemd-sulogin-shell system-sleep systemd-backlight systemd-makefs systemd-sysctl user systemd-binfmt systemd-modules-load systemd-sysv-install user-environment-generators systemd-bless-boot systemd-networkd systemd-timedated user-generators systemd-boot-check-no-failures systemd-networkd-wait-online systemd-timesyncd user-preset systemd-cgroups-agent systemd-network-generator systemd-time-wait-sync每個進程的主要用途可以閱讀 freedesktop systemd 手冊:https://www.freedesktop.org/software/systemd/man/
目前 systemd 占據 init 程序的主導,有統一天下的趨勢。
2 systemd 的主要功能
systemd 采用并行的啟動方式,并提供按需啟動的方式:systemd 在設計之初最關注兩件事情:更少的開始,更多的并行。更少的開始,意味著在開機啟動階段,systemd 僅啟動系統啟動時必要的一些服務,更多其他的服務推遲啟動,直到真正需要它的時候,例如優麒麟的藍牙 bluetooth 與截圖相關的服務,開機的時候系統不會用到它;優麒麟的 U 盤啟動器相關的服務,可以等到接入 U 盤的時候再啟動;如果系統未連接到網絡,那那些需要用到網絡的相關服務也可以無需啟動,直到網絡連通后的第一次連接再啟動即可。更多的并行,意味著服務的啟動不需要像 sysvinit 一樣序列化啟動,而是同時運行所有需要的服務,以便系統 cpu 資源利用的最大化,因此總的啟動時間最小化,后面會介紹 systemd 是如何實現所有服務并行啟動。
采用 cgroup 跟蹤管理進程的生命周期:cgroup 為控制組,是一個層級結構,類似與文件管理系統的結構。當一個進程創建了子進程,子進程會繼承父進程的 cgroup,就好比子進程創建在父進程的目錄下,當子進程又創建一個子進程時,這個子進程會繼承上一個子進程的 cgroup,也就相當與繼承了父進程的 cgroup,好比這個子進程創建在上一個子進程的目錄下,也就在父進程的目錄下,通過這一機制就可以把父進程與所有的子進程關聯起來并進行跟蹤,當停止父進程時,可以通過查詢 cgroup 找到所有關聯的子進程,從而確保干凈的停止所有相關服務。
啟動掛載與自動掛載:在系統啟動過程中,systemd 在初始化時會自身進行一些掛載,如/sys 目錄與/run 目錄的掛載,這些都是系統啟動時至關重要的文件系統。systemd 還能實現動態掛載點的自動掛載,例如不需要經常使用的光盤、U 盤的掛載,只在這些設備接入時,systemd 啟動相應的服務并對其進行臨時的掛載以便訪問其中的內容,當這些設備拔出時,這些掛載點將被取消以便節約資源。
事務的依賴關系管理:系統有很多的服務存在依賴關系,例如麒麟軟件商店需要等待網絡服務的啟動,lightdm 與 systemd 交互需要等待 D-Bus 的啟動,大多數服務也需要等待 syslog 的完全啟動與初始化。systemd 采用 Unit(配置單元)管理這些服務的依賴關系,維護一個事務的一致性,并保證所有的相關服務不會出現相互依賴而產生死鎖的情況,后面會對 Unit 進行詳細介紹。
日志:systemd 自帶 journalctl 命令來查看系統保存的所有日志信息,并且可以支持通過一些參數來對日志進行過濾,方便用戶進行日志分析。
其他:systemd 經過幾代的更新,實現的功能已經十分的多了,甚至有人覺得 systemd 管得太多了,導致一些服務都沒有了存在的必要。例如 systemd 添加了許多 systemctl 的命令,可以實現系統電源的管理;systemd 還添加了看門狗機制,其他守護進程需要定期 ping systemd 進程,否則會視為失敗而重啟它等等。詳情可以去閱讀設計師的博客 http://0pointer.de/blog/projects。
3 systemd 如何實現服務的并行
systemd 的設計理念就是希望讓所有的服務并行的啟動,以最大化利用硬件資源,提高啟動的時間。但是我們知道服務之間存在依賴關系,客戶端需要等待服務端的啟動才可以建立連接,例如前面提到的,在優麒麟操作系統中,所有的服務都需要等待 syslog 服務的啟動,那 systemd 是如何擺脫這同步和序列化過程的呢?
systemd 的設計師認為,對于傳統的守護進程,他們真正實際等待另一個守護進程提供的是套接字的準備,需要的是一個文件系統的 socket 套接字描述符,這是它們唯一等待的,因此是否可以設法讓這些套接字描述符可以更早的創建用于連接,從而不用等待整個守護進程完整的啟動?答案是可以的。
在 C 語言中,一個進程啟動另一個進程時,一般是執行系統調用 exec(),systemd 在調用 exec()來啟動服務之前,先創建與該服務關聯的監聽套接字并激活,然后在 exec()啟動服務期間把套接字傳遞給它,因此在服務還在啟動的時候,套接字就已經處于可用的狀態。通過這一方式,systemd 可以在第一步中為所有的服務創建套接字,然后第二步一次運行所有的服務,如果一個服務需要依賴于另一個服務,由于套接字已經準備好,服務之間可以直接進行連接并繼續執行啟動,如果遇到了需要同步的請求,不得不等待阻塞的情況,那阻塞的也將只會是一個服務,并且只是一個服務的一個請求,不會影響其他服務的啟動,由此實現服務之間不需要再進行序列化的啟動。Linux 內核提供了套接字緩沖區功能,幫助 systemd 實現了最大的并行化,還是拿 syslog 服務來說,優麒麟操作系統上大多數服務在啟動初期都會先進行日志相關的初始化配置,如果同時啟動 syslog 服務與各種 syslog 的客戶端服務,由于 syslog 相關套接字在 systemd 執行 exec()啟動 syslog 之前已經創建并準備好,客戶端可以直接連接到 syslog 的套接字上,如果遇到 syslog 啟動比較慢,客戶端向 syslog 發送請求消息,syslog 還無法處理的情況,通過內核 socket 緩沖區的機制,請求的消息將會傳到 syslog 套接字的緩沖區之中,只要緩沖區未滿,客戶端就不需要等待并繼續往下執行;一旦 syslog 服務完全啟動,它就會使所有消息出列并處理他們;當出現另一種情況,緩沖區已滿,或者需要同步消息請求的情況,雖然這個時候客戶端不得不阻塞等待,但是也只有一個客戶端的一個請求被阻塞,并且直到服務端趕上并處理為止。
因此 systemd 先進行套接字的激活,然后開始服務的創建,使得所有的服務可以并行啟動,依賴的管理也變得多余,至少可以說是次要的,因為從服務的角度看,只要套接字是激活的,另一個服務有沒有啟動都沒有區別,這樣一種方式也使得程序更加地健壯,因為不論服務可用或不可用,甚至是崩潰,套接字都處于可用的狀態,不會讓客戶端注意到丟失連接。
4 systemd 執行單元–Unit 介紹
Unit 是 systemd 管理服務與資源的基本單元,可以簡單理解為 systemd 啟動后每次需要執行的服務,每次需要處理的資源,都被抽象為一個配置單元 Unit,保存在一個 Unit 文件里面。例如,當用戶登錄到優麒麟操作系統時,systemd 會執行 systemd-login.service 這個 Unit 文件來啟動 login 服務,持續跟蹤用戶的會話、進程、空閑狀態,為用戶分配一個 slice 單元;當用戶執行睡眠操作時,systemd 會執行 systemd-suspend.service 文件的 Unit,來啟動 systemd-sleep 服務執行系統睡眠操作。Unit 文件可以根據其后綴名分為12種不同的類型,systemd 內部給這12種類型的 Unit 定義了不同的全局模板,因此 systemd 的執行流程為:
首先找到對應的 Unit 文件,然后根據 Unit 文件的類型匹配對應的全局模板,再然后根據這個模板解析 Unit 文件,最后執行 Unit 文件里的操作。接下來簡單介紹一下12種 Unit 文件類型:
(1)service:這是最明顯的單元類型,代表一個后臺守護進程,可以啟動、停止、重新啟動、重新加載守護進程,是最常用的一類 Unit 文件。
(2)socket:這個單元在文件系統或互聯網上封裝了一個套接字。目前 systemd 支持流、數據報和順序包類型的 AF_INET、AF_INET6、AF_UNIX 套接字。還支持經典的 FIFO 作為傳輸。每個套接字單元都有一個匹配的服務單元,相應的服務在第一個連接進入套接字時就會啟動,例如:nscd.socket 在傳入連接上啟動 nscd.service。
(3)device:這個單元封裝了 Linux 設備樹中的一個設備。如果設備通過 udev 規則為此標記,它將在 systemd 中作為設備單元公開。使用 udev 設置的屬性可用作配置源來設置設備單元的依賴關系。
(4)mount:這個單元封裝了文件系統層次結構中的一個掛載點。systemd 監控所有掛載點,也可用于掛載或卸載掛載點。systemd 會將/etc/fstab 中的條目都轉換為掛載點,并在開機時處理。
(5)automount:這個單元類型在文件系統層次結構中封裝了一個自動掛載點。每個自動掛載單元都有一個匹配的掛載單元,當該自動掛載點被訪問時,systemd 就會執行掛載點中定義的掛載行為。
(6)target:這種單元類型用于單元的邏輯分組:它本身并不做任何事情,它只是引用其他單元,從而可以一起控制。比如:想讓系統進入圖形化模式,需要運行許多服務和配置命令,這些操作都由一個個的配置單元表示,將所有這些配置單元組合為一個目標(target),就表示需要將這些配置單元全部執行一遍以便進入目標所代表的系統運行狀態。
(7)snapshot:類似于 target 單元,snapshot 本身實際上不做任何事情,它們的唯一目的是引用其他單元。快照可用于保存或回滾 init 系統的所有服務和單元的狀態。比如允許用戶臨時進入特定狀態,例如“緊急外殼”,終止當前服務,并提供一種簡單的方法返回之前的狀態。
(8)swap:和掛載配置單元類似,交換配置單元用來管理交換分區。用戶可以使用交換配置單元來定義系統中的交換分區,可以讓這些交換分區在啟動時被激活。
(9)timer:定時器配置單元用來定時觸發用戶定義的服務操作,是一種基于定時器的服務激活,這類配置單元取代了 atd、crond 等傳統的定時服務。
(10)path:這類配置單元用來監控指定目錄或者文件的變化,根據變化觸發其他配置單元服務的運行。
(11)scope:這個單元主要表示從 systemd 外部創建的進程。
(12)slice:此單元主要用于封裝管理一組進程資源占用的控制組的 slice 單元,也就是主要用于 cgroup,它通過在 cgroup 中創建一個節點實現資源的控制,一般包含 scope 與 service 單元。
下面通過藍牙服務 bluetooth.service 介紹一下 Unit 文件的結構。
$ cat /usr/lib/systemd/system/bluetooth.service [Unit] Description=Bluetooth service Documentation=man:bluetoothd(8) ConditionPathIsDirectory=/sys/class/bluetooth[Service] Type=dbus BusName=org.bluez ExecStart=/usr/lib/bluetooth/bluetoothd NotifyAccess=main #WatchdogSec=10 Restart=on-failure CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE LimitNPROC=1 ProtectHome=true ProtectSystem=full[Install] WantedBy=bluetooth.target Alias=dbus-org.bluez.serviceUnit 文件主要分為以下三個大的部分:
Unit 段: 此部分所有 Unit 文件通用,用來定義 Unit 的元數據、配置以及與其他 Unit 的關系,Description 描述 Unit 文件信息,Documentation 表示指定服務的文檔,Condition 表示服務啟動的條件,有些 Unit 還包含 wants、before、after、require 字段,這些表示服務的一個依賴關系。
service 段: 服務(Service)類型的 Unit 文件特有的字段,用于定義服務的具體管理和執行動作。其中包括 Type 字段,定義進程的行為,例如使用 fork()啟動,使用 dbus 啟動等等;ExecStart、ExecStartPre、ExecStartPos、ExecReload、ExecStop 分別表示啟動當前服務執行的命令、啟動當前服務之前執行的命令、啟動當前服務之后啟動的命令、重啟當前服務時執行的命令、停止當前服務時執行的命令。以上圖為例,啟動藍牙服務所需要執行的命令為/usr/lib/bluetooth/bluetoothd。
Install 段: 此部分所有 Unit 文件通用,通常指定運行目標的 target,使得服務在系統啟動時自動運行。Wantedby、RequiredBy 與 Unit 段 Wants 字段類似,表示依賴關系,Alias 字段表示啟動運行時使用的別名。
相關字段更詳細的描述可以參考 freedesktop 的 man 手冊:https://www.freedesktop.org/software/systemd/man/systemd.unit.html
https://www.freedesktop.org/software/systemd/man/systemd.service.html
以優麒麟操作系統為例,Unit 文件主要的存儲路徑如下:
system:
/etc/systemd/system/* /run/systemd/system/* /lib/systemd/system/*user:
~/.config/systemd/user/* /etc/systemd/user/* $XDG_RUNTIME_DIR/systemd/user/* /run/systemd/user/* ~/.local/share/systemd/user/* /usr/lib/systemd/user/*二、systemd 時代的開機啟動流程
在 systemd 作為系統的 init 程序的時代下,Linux 系統的啟動流程可以大致分為 6 個階段:BIOS 自檢階段、GRUB 引導階段、kernel 內核加載階段、initrd 虛擬根文件系統階段、systemd 初始化階段、終端登錄階段。每個階段都各司其職,為下一個階段的進行做鋪墊,相互聯系,缺一不可。接下來對每個階段做一下介紹:
1 BIOS 自檢階段
從我們啟動計算機從按下電源鍵開始,計算機開始通電,然后系統就開始加載主板內存上的第一段代碼:BIOS,系統進入 BIOS 自檢階段。
BIOS 為基本輸入輸出系統,全稱 Basic Input Output System,它燒錄在主板的內存上,其中的內容只能讀不能改,如果要進行更改只能重新燒錄到主板的內存上。BIOS 在開機階段最主要的功能為上電自檢,它會對主板上接入的硬件設備一個個進行檢查,例如檢查 CPU、主板、內存、軟硬盤系統、鍵盤、光驅等等硬件是否接入正常,有無故障,當某一些主要的硬件(例如 CPU、內存等)出現問題時,BIOS 就會報錯,無法繼續啟動系統。我們在啟動電腦時聽到的滴滴的聲音就是 BIOS 蜂鳴器發出的聲音,當硬件出問題的時候就可以聽到它蜂鳴器響兩到三聲報錯,系統就無法進行下一步的啟動。
BIOS 檢查完所有硬件狀態并狀態無誤的時候,就會按照設置的啟動順序去找相應的啟動盤,然后引導系統進入相應的啟動盤繼續啟動系統。有過刷機經驗的朋友應該知道在系統啟動時按 F12 或者 delete 鍵就會進入 BIOS 界面,然后就會去選擇相應的啟動盤進行刷機,啟動盤可以是裝機 U 盤、光驅,也可以是已經裝了系統的磁盤等等,BIOS 可以設置默認的啟動順序,例如:可以設置 U 盤為第一啟動項,開機啟動時 BIOS 就會引導系統去找 U 盤對應的硬件接口,當找不到 U 盤時,BIOS 會繼續嘗試第二啟動項,當選擇好了啟動項時,系統進入相應的啟動盤,并開始執行啟動盤中第一塊磁盤第一個扇區的代碼,至此 BIOS 自檢階段結束。
2 GRUB 引導階段
GRUB 是 GRand Unified Bootloader 的縮寫,它是一個多重操作系統的管理器,存放在第一個磁盤的第一個扇區的主引導扇區里面,如果你的電腦里面裝了多個系統,例如 Linux 系統和 Windows 系統,那么你可以通過 GRUB 來移動光標選擇自己想要進入的系統,選擇好系統以后 GRUB 就會根據系統分區表里找到對應系統所在的磁盤分區,加載相應的 grub.cfg 配置文件,通過配置文件,加載 /boot 分區的文件系統驅動,然后在文件系統中找到系統內核,把內核加載進來并啟動,最后把系統的控制權交給內核,至此 GRUB 引導階段結束。
GRUB 除了引導系統這一主要功能外,還可以通過 grub.cfg 配置文件來實現其他的一些功能。grub.cfg 配置文件存放在 /boot/grub/目錄下,配置文件中,Linux 參數表示系統啟動時對應加載的內核,當系統里存放了多個內核、或者在你電腦上重新修改編譯了新的內核的時候,可以配置此項來選擇相應的內核進行加載;quiet 參數類似于 loglevel 參數,用來配置日志啟動的等級;splash 參數用來配置相應的啟動動畫等等。
3 Kernel 內核加載階段
在講解內核的啟動之前,先簡單介紹一下 Linux 內核。Linux 內核是一種宏內核,運行在單一地址空間的單一的程序,把系統的進程線程管理、內存管理、文件系統、驅動管理、網絡管理等一些基本功能集中在一起,內核中的每一個函數都可以訪問到其內核的其他部分,不同于微內核(代表:Windows 系統),微內核則是將這些功能獨立的劃分成不同的服務,服務之間通過通訊接口與中心內核通訊。
在結束了 GRUB 引導階段,內核拿到系統的控制權后,首先開始初始化系統中各種設備的相關配置工作,其中包括 CPU、I/O 設備、存儲設備的初始化等等,其次,內核創建內核態的 kernel_init 的進程,然后找到 initrd 文件并解壓,加載 initrd 虛擬根文件系統中的驅動程序完成相關硬件的初始化,最后調用 initrd 虛擬根文件系統的 init 腳本,至此,內核在系統啟動過程中的作用基本上就已經完成,內核開始等待 initrd 執行 init 進程,內核加載階段結束。
4 Initrd 虛擬根文件系統階段
initrd(Initial RAM Disk)它是一個虛擬的根文件系統,在 GRUB 階段被拷貝到內存,在內核中被解壓,是一個臨時的虛擬根文件系統,對其進行解壓后可以看到它的目錄結構和實際的根文件系統類似,并且包含了些驅動程序,可通過lsinitramfs命令查看initrd.img的內容:
$ lsinitramfs initrd.img-5.10.0-1051-oem | head -n 20 . kernel kernel/x86 kernel/x86/microcode kernel/x86/microcode/AuthenticAMD.bin kernel kernel/x86 kernel/x86/microcode kernel/x86/microcode/.enuineIntel.align.0123456789abc kernel/x86/microcode/GenuineIntel.bin . bin conf conf/arch.conf conf/conf.d conf/conf.d/zfs conf/conf.d/zz-resume-auto conf/initramfs.conf conf/modules cryptroot由于內核為了精簡,只保留了最基本的模塊,因此沒有各種設備硬件的驅動程序,這些驅動程序就存放在了 initrd 里面,內核啟動的時候,就從 initrd 中加載必要的驅動模塊,完成硬件的初始化工作,接著,內核就開始執行虛擬根文件系統里的 init 程序,即虛擬根文件系統下的 systemd 程序,systemd 就作為內核的子程序,拿到了系統的控制權,開始做一些系統初始化方面的工作。
通過上面的描述,可以總結一下,虛擬根文件系統的階段可以大致的分為:內核加載 initrd 里面的驅動程序、虛擬根文件系統下的 systemd 程序加載這兩個過程,因此也可把虛擬根文件系統階段分別歸到內核加載階段與 systemd 初始化階段兩個里面,是與上下兩個階段重合的一個階段。此外,initrd 還提供了美化啟動圖形界面的功能,用來遮蓋系統啟動過程中的 log 日志輸出,提升用戶的體驗。當 initrd 下的 systemd 進程完成環境的初始化,系統切換到真正的根文件系統的時候,initrd 階段結束。
5 systemd 初始化階段
systemd 是 system deamon 的簡稱,是一個 Linux 系統基礎組件的集合,提供了系統與服務的管理,是 pid 為 1 的 init 進程,是所有進程的父進程。
通過前面的描述,我們可以把 systemd 分為兩個階段:虛擬根文件系統階段與實根文件系統階段。內核通過解壓 initrd 文件得到虛擬根文件系統,然后執行虛擬根文件系統下的 init 程序來啟動 systemd,systemd 作為內核的子進程在虛擬根文件系統下開始運行。虛擬根文件下的 systemd 首先對目前的系統進行一些檢查,例如判斷系統的運行狀態是 user 態還是 system 態,系統是正常的啟動狀態還是異常出錯后的重啟狀態等等,然后進行一些系統的初始化配置,包括:環境變量的配置、日志的相關配置等,接著對一些關鍵的文件系統進行掛載,主要包括 /proc、/sys、/dev、/var 這些基本的文件系統目錄,到這一步后,systemd 就開始為切換實根文件目錄做準備,保存一些已經配置的項目,并進行一些環境的適配之后,systemd 執行 setsid()系統調用脫離內核控制,成為一個完全獨立的父進程,至此 systemd 的虛擬根文件系統階段結束,systemd 進入到實根文件系統階段。
在實根文件系統階段,systemd 首先進行一些切換后的環境適配,然后開啟日志終端的功能,并把系統啟動時臨時保存在內核中的日志提取出來,整理后存放到相應的日志文件中,下一步,systemd 開始進行一些系統能力的獲取與系統相關的初始化與配置,例如:CPU 親和力的獲取、主機名的配置、系統 ID 的配置,cgroup 控制器的掛載、回環網絡的配置等,完成以上的這些所有初始化工作后,systemd 作為 PID 為 1 的守護進程,開始了各個系統服務的創建與管理工作,根據相應 Unit 配置單元文件執行相應的系統服務,通過各個服務逐步完成系統的啟動工作。systemd 執行 Unit 的順序大致可以分為 sysinit.target->basic.target->default.target,其中 sysinit.target 與 basic.target 主要用來啟動一些系統初始化相關的一些服務與執行一些開機啟動早期的一些任務,default.target 則指向不同的“運行級別”target 文件,如果是進入命令行模式則指向 multi-user.target 文件,如果是進入圖形界面模式則指向 graphical.target 文件。至此,systemd 開機啟動階段的工作完成。
6 終端登錄階段
在完成了 systemd 初始化階段以后,系統根據配置的運行級別,進入不同的登錄界面,下面主要從圖形登錄界面進行介紹。在優麒麟操作系統中,systemd 之后的啟動流程主要如下:systemd->lightdm->Xorg->ukui-session,在優麒麟的終端通過 pstree 命令可以看到如下兩個進程樹:
$ pstree systemd─┬....├─lightdm─┬─Xorg───13*[{Xorg}]│ ├─lightdm─┬─ukui-session─┬─agent───2*[{agent}]│ │ │ ├─applet.py│ │ │ ├─fcitx│ │ │ ├─indicator-china───80*[{indicator-china}]│ │ │ ├─kmdaemon───5*[{kmdaemon}]│ │ │ ├─kylin-nm───10*[{kylin-nm}]│ │ │ ├─kylin-printer───11*[{kylin-printer}]│ │ │ ├─mktip───9*[{mktip}]│ │ │ ├─peony-qt-deskto───10*[{peony-qt-deskto}]│ │ │ ├─polkit-ukui-aut───9*[{polkit-ukui-aut}]│ │ │ ├─ssh-agent│ │ │ ├─ukui-flash-disk───6*[{ukui-flash-disk}]│ │ │ ├─ukui-kwin_x11───11*[{ukui-kwin_x11}]│ │ │ ├─ukui-menu───9*[{ukui-menu}]│ │ │ ├─ukui-panel───10*[{ukui-panel}]│ │ │ ├─ukui-power-mana───9*[{ukui-power-mana}]│ │ │ ├─ukui-power-mana───3*[{ukui-power-mana}]│ │ │ ├─ukui-screensave───4*[{ukui-screensave}]│ │ │ ├─ukui-search───9*[{ukui-search}]│ │ │ ├─ukui-settings-d───10*[{ukui-settings-d}]│ │ │ ├─ukui-sidebar─┬─dbus-monitor│ │ │ │ └─13*[{ukui-sidebar}]│ │ │ ├─ukui-volume-con───11*[{ukui-volume-con}]│ │ │ ├─ukui-window-swi───9*[{ukui-window-swi}]│ │ │ ├─update-notifier───3*[{update-notifier}]│ │ │ ├─user-guide-daem───5*[{user-guide-daem}]│ │ │ └─5*[{ukui-session}]│ │ └─2*[{lightdm}]│ └─2*[{lightdm}]│....lightdm 是一個全新的、輕量的 Linux 桌面的桌面顯示管理器,它首先會拉起 Xorg,Xorg 是一個顯示的后臺,負責屏幕的繪制,然后 lightdm 還會拉起 lightdm-greeter,lightdm-greeter 是 lightdm 的子進程,它會拉起 ukui-greeter 進程,ukui-greeter 是登錄界面進程,因此 ukui-greeter 起來以后,系統進入到登錄界面,當輸入登錄的用戶名與密碼,用戶名與密碼效驗通過以后,lightdm 建立個人的 ukui-session 桌面窗口管理器進程,至此,終端登錄階段結束,系統完成啟動。
三、systemd 相關命令
systemd 提供了 systemctl 相關命令,用于管理系統,下面對一些基礎常用命令進行介紹:
1 系統管理命令,控制系統電源狀態
# 重啟系統 $ sudo systemctl reboot# 關閉系統,切斷電源 $ sudo systemctl poweroff# 暫停系統,使系統進入睡眠狀態 $ sudo systemctl suspend# 讓系統進入冬眠狀態 $ sudo systemctl hibernate# 讓系統進入交互式休眠狀態 $ sudo systemctl hybrid-sleep2 systemd-analyze 命令,用于查看啟動耗時,可用來分析系統的啟動效率
#查看啟動耗時 $ systemd-analyze#查看每個服務的啟動耗時 $ systemd-analyze blame#顯示瀑布狀的啟動過程流 $ systemd-analyze critical-chain#顯示指定服務的啟動流 $ systemd-analyze critical-chain atd.service3 hostnamectl 命令,用于查看當前主機的信息
#顯示當前主機的信息 $ hostnamectl#設置主機名 $ sudo hostnamectl set-hostname rhel74 Unit 相關命令,用來管理 Unit 配置單元
#列出正在運行的 Unit $ systemctl list-units#列出所有 Unit,包括沒有找到配置文件的或者啟動失敗的 $ systemctl list-units --all#列出所有沒有運行的 Unit $ systemctl list-units --all --state=inactive#列出所有加載失敗的 Unit $ systemctl list-units --failed#列出所有正在運行的、類型為 service 的 Unit $ systemctl list-units --type=service#立即啟動一個服務 $ sudo systemctl start apache.service#立即停止一個服務 $ sudo systemctl stop apache.service#重啟一個服務 $ sudo systemctl restart apache.service#殺死一個服務的所有子進程 $ sudo systemctl kill apache.service#重新加載一個服務的配置文件 $ sudo systemctl reload apache.service#重載所有修改過的配置文件 $ sudo systemctl daemon-reload#顯示某個 Unit 的所有底層參數 $ systemctl show httpd.service#顯示某個 Unit 的指定屬性的值 $ systemctl show -p CPUShares httpd.service#設置某個 Unit 的指定屬性 $ sudo systemctl set-property httpd.service CPUShares=5005 日志管理,用來查看和過濾系統日志
#查看所有日志(默認情況下 ,只保存本次啟動的日志) $ sudo journalctl#查看內核日志(不顯示應用日志) $ sudo journalctl -k#查看系統本次啟動的日志 $ sudo journalctl -b $ sudo journalctl -b -0#查看上一次啟動的日志(需更改設置) $ sudo journalctl -b -1#查看指定時間的日志 $ sudo journalctl --since="2012-10-30 18:17: 16" $ sudo journalctl --since "20 min ago" $ sudo journalctl --since yesterday $ sudo journalctl --since "2015-01-10" --until "2015-01-11 03:00" $ sudo journalctl --since 09:00 --until "1 hour ago"#顯示尾部的最新 10 行日志 $ sudo journalctl -n#顯示尾部指定行數的日志 $ sudo journalctl -n 20#實時滾動顯示最新日志 $ sudo journalctl -f#查看指定服務的日志 $ sudo journalctl /usr/lib/systemd/systemd#查看指定進程的日志 $ sudo journalctl _PID=1#查看某個路徑的腳本的日志 $ sudo journalctl /usr/bin/bash#查看指定用戶的日志 $ sudo journalctl _UID=33 --since today#查看某個 Unit 的日志 $ sudo journalctl -u nginx.service $ sudo journalctl -u nginx.service --since today#實時滾動顯示某個 Unit 的最新日志 $ sudo journalctl -u nginx.service -f#查看指定優先級(及其以上級別)的日志,共有8級 # 0: emerg # 1: alert # 2: crit # 3: err # 4: warning # 5: notice # 6: info # 7: debug $ sudo journalctl -p err -b#日志默認分頁輸出,--no-pager 改為正常的標準輸出 $ sudo journalctl --no-pager#顯示日志占據的硬盤空間 $ sudo journalctl --disk-usage#指定日志文件占據的最大空間 $ sudo journalctl --vacuum-size=1G#指定日志文件保存多久 $ sudo journalctl --vacuum-time=1years關于 systemd 的完整介紹可以參考 systemd 官網手冊以及systemd 的官網中文翻譯手冊
systemd 源碼地址:https://launchpad.net/ubuntu/+source/systemd 和 https://github.com/systemd/systemd
總結
以上是生活随笔為你收集整理的Systemd 技术原理实践的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: h5封装去底部_贪婪洞窟H5:也出微信小
- 下一篇: 微信办公时代,企业文化也能撑起企业的半壁