新聞中心
了解 systemd 是怎樣決定服務(wù)啟動(dòng)順序,即使它本質(zhì)上是個(gè)并行系統(tǒng)。

成都創(chuàng)新互聯(lián)專業(yè)為企業(yè)提供孝南網(wǎng)站建設(shè)、孝南做網(wǎng)站、孝南網(wǎng)站設(shè)計(jì)、孝南網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計(jì)與制作、孝南企業(yè)網(wǎng)站模板建站服務(wù),10余年孝南做網(wǎng)站經(jīng)驗(yàn),不只是建網(wǎng)站,更提供有價(jià)值的思路和整體網(wǎng)絡(luò)服務(wù)。
最近在設(shè)置 Linux 系統(tǒng)時(shí),我想知道如何確保服務(wù)和其他單元的依賴關(guān)系在這些依賴于它們的服務(wù)和單元啟動(dòng)之前就已經(jīng)啟動(dòng)并運(yùn)行了。我需要更多 systemd 如何管理啟動(dòng)程序的相關(guān)知識(shí),特別是在本質(zhì)上是一個(gè)并行的系統(tǒng)中如何是決定服務(wù)啟動(dòng)順序的。
你可能知道 SystemV(systemd 的前身,我在這個(gè)系列的 第一篇文章 中解釋過)通過 Sxx 前綴命名啟動(dòng)腳本來決定啟動(dòng)順序,xx 是一個(gè) 00-99 的數(shù)字。然后 SystemV 利用文件名來排序,然后按照所需的運(yùn)行級別執(zhí)行隊(duì)列中每個(gè)啟動(dòng)腳本。
但是 systemd 使用單元文件來定義子程序,單元文件可由系統(tǒng)管理員創(chuàng)建或編輯,這些文件不僅可以用于初始化時(shí)也可以用于常規(guī)操作。在這個(gè)系列的 第三篇文章 中,我解釋了如何創(chuàng)建一個(gè)掛載單元文件。在第五篇文章中,我解釋了如何創(chuàng)建一種不同的單元文件 —— 在啟動(dòng)時(shí)執(zhí)行一個(gè)程序的服務(wù)單元文件。你也可以修改單元文件中某些配置,然后通過 systemd 日志去查看你的修改在啟動(dòng)序列中的位置。
準(zhǔn)備工作
先確認(rèn)你已經(jīng)在 /etc/default/grub 文件中的 GRUB_CMDLINE_LINUX= 這行移除了 rhgb 和 quiet,如同我在這個(gè)系列的 第二篇文章 中展示的那樣。這讓你能夠查看 Linux 啟動(dòng)信息流,你在這篇文章中部分實(shí)驗(yàn)中需要用到。
程序
在本教程中,你會(huì)創(chuàng)建一個(gè)簡單的程序讓你能夠在主控臺(tái)和后續(xù)的 systemd 日志中查看啟動(dòng)時(shí)的信息。
創(chuàng)建一個(gè) shell 程序 /usr/local/bin/hello.sh 然后添加下述內(nèi)容。你要確保執(zhí)行結(jié)果在啟動(dòng)時(shí)是可見的,可以輕松的在 systemd 日志中找到它。你會(huì)使用一版攜帶一些方格的 “Hello world” 程序,這樣它會(huì)非常顯眼。為了確保這個(gè)文件是可執(zhí)行的,且為了安全起見,它需要 root 的用戶和組所有權(quán)和 700 權(quán)限。
#!/usr/bin/bash
# Simple program to use for testing startup configurations
# with systemd.
# By David Both
# Licensed under GPL V2
#
echo "###############################"
echo "######### Hello World! ########"
echo "###############################"
在命令行中執(zhí)行這個(gè)程序來檢查它能否正常運(yùn)行。
[root@testvm1 ~]# hello.sh
###############################
######### Hello World! ########
###############################
[root@testvm1 ~]#
這個(gè)程序可以用任意腳本或編譯語言實(shí)現(xiàn)。hello.sh 程序可以被放在 Linux 文件系統(tǒng)層級結(jié)構(gòu)(FHS)上的任意位置。我把它放在 /usr/local/bin 目錄下,這樣它可以直接在命令行中執(zhí)行而不必在打命令的時(shí)候前面帶上路徑。我發(fā)現(xiàn)我創(chuàng)建的很多 shell 程序需要從命令行和其他工具(如 systemd)運(yùn)行。
服務(wù)單元文件
創(chuàng)建服務(wù)單元文件 /etc/systemd/system/hello.service,寫入下述內(nèi)容。這個(gè)文件不一定是要可執(zhí)行的,但是為了安全起見,它需要 root 的用戶和組所有權(quán)和 644 或 640 權(quán)限。
# Simple service unit file to use for testing
# startup configurations with systemd.
# By David Both
# Licensed under GPL V2
#
[Unit]
Description=My hello shell script
[Service]
Type=oneshot
ExecStart=/usr/local/bin/hello.sh
[Install]
WantedBy=multi-user.target
通過查看服務(wù)狀態(tài)來確認(rèn)服務(wù)單元文件能如期運(yùn)行。如有任何語法問題,這里會(huì)顯示錯(cuò)誤。
[root@testvm1 ~]# systemctl status hello.service
● hello.service - My hello shell script
Loaded: loaded (/etc/systemd/system/hello.service; disabled; vendor preset: disabled)
Active: inactive (dead)
[root@testvm1 ~]#
你可以運(yùn)行這類 “oneshot”(單發(fā))類型的服務(wù)多次而不會(huì)有問題。此類服務(wù)適用于服務(wù)單元文件啟動(dòng)的程序是主進(jìn)程,必須在 systemd 啟動(dòng)任何依賴進(jìn)程之前完成的服務(wù)。
共有 7 種服務(wù)類型,你可以在 systemd.service(5) 的手冊頁上找到每一種(以及服務(wù)單元文件的其他部分)的詳細(xì)解釋。(你也可以在文章末尾的 資料 中找到更多信息。)
出于好奇,我想看看錯(cuò)誤是什么樣子的。所以我從 Type=oneshot 這行刪了字母 “o”,現(xiàn)在它看起來是這樣 Type=neshot,現(xiàn)在再次執(zhí)行命令:
[root@testvm1 ~]# systemctl status hello.service
● hello.service - My hello shell script
Loaded: loaded (/etc/systemd/system/hello.service; disabled; vendor preset: disabled)
Active: inactive (dead)
May 06 08:50:09 testvm1.both.org systemd[1]: /etc/systemd/system/hello.service:12: Failed to parse service type, ignoring: neshot
[root@testvm1 ~]#
執(zhí)行結(jié)果明確地告訴我錯(cuò)誤在哪,這樣解決錯(cuò)誤變得十分容易。
需要注意的是即使在你將 hello.service 文件保存為它原來的形式之后,錯(cuò)誤依然存在。雖然重啟機(jī)器能消除這個(gè)錯(cuò)誤,但你不必這么做,所以我去找了一個(gè)清理這類持久性錯(cuò)誤的方法。我曾遇到有些錯(cuò)誤需要 systemctl daemon-reload 命令來重置錯(cuò)誤狀態(tài),但是在這個(gè)例子里不起作用??梢杂眠@個(gè)命令修復(fù)的錯(cuò)誤似乎總是有一個(gè)這樣的聲明,所以你知道要運(yùn)行它。
然而,每次修改或新建一個(gè)單元文件之后執(zhí)行 systemctl daemon-reload 確實(shí)是值得推薦的做法。它提醒 systemd 有修改發(fā)生,而且它可以防止某些與管理服務(wù)或單元相關(guān)的問題。所以繼續(xù)去執(zhí)行這條命令吧。
在修改完服務(wù)單元文件中的拼寫錯(cuò)誤后,一個(gè)簡單的 systemctl restart hello.service 命令就可以清除錯(cuò)誤。實(shí)驗(yàn)一下,通過添加一些其他的錯(cuò)誤至 hello.service 文件來看看會(huì)得到怎樣的結(jié)果。
啟動(dòng)服務(wù)
現(xiàn)在你已經(jīng)準(zhǔn)備好啟動(dòng)這個(gè)新服務(wù),通過檢查狀態(tài)來查看結(jié)果。盡管你可能之前已經(jīng)重啟過,你仍然可以啟動(dòng)或重啟這個(gè)單發(fā)服務(wù)任意次,因?yàn)樗贿\(yùn)行一次就退出了。
繼續(xù)啟動(dòng)這個(gè)服務(wù)(如下所示),然后檢查狀態(tài)。你的結(jié)果可能和我的有區(qū)別,取決于你做了多少試錯(cuò)實(shí)驗(yàn)。
[root@testvm1 ~]# systemctl start hello.service
[root@testvm1 ~]# systemctl status hello.service
● hello.service - My hello shell script
Loaded: loaded (/etc/systemd/system/hello.service; disabled; vendor preset: disabled)
Active: inactive (dead)
May 10 10:37:49 testvm1.both.org hello.sh[842]: ######### Hello World! ########
May 10 10:37:49 testvm1.both.org hello.sh[842]: ###############################
May 10 10:37:49 testvm1.both.org systemd[1]: hello.service: Succeeded.
May 10 10:37:49 testvm1.both.org systemd[1]: Finished My hello shell script.
May 10 10:54:45 testvm1.both.org systemd[1]: Starting My hello shell script...
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ######### Hello World! ########
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
May 10 10:54:45 testvm1.both.org systemd[1]: hello.service: Succeeded.
May 10 10:54:45 testvm1.both.org systemd[1]: Finished My hello shell script.
[root@testvm1 ~]#
從狀態(tài)檢查命令的輸出中我們可以看到,systemd 日志表明 hello.sh 啟動(dòng)然后服務(wù)結(jié)束了。你也可以看到腳本的輸出。該輸出是根據(jù)服務(wù)的最近調(diào)用的日志記錄生成的,試試看多啟動(dòng)幾次這個(gè)服務(wù),然后再看狀態(tài)命令的輸出就能理解我所說的。
你也應(yīng)該直接查看日志內(nèi)容,有很多種方法可以實(shí)現(xiàn)。一種辦法是指定記錄類型標(biāo)識(shí)符,在這個(gè)例子中就是 shell 腳本的名字。它會(huì)展示前幾次重啟和當(dāng)前會(huì)話的日志記錄。如你所見,我已經(jīng)為這篇文章做了挺長一段時(shí)間的研究測試了。
[root@testvm1 ~]# journalctl -t hello.sh
<剪去>
-- Reboot --
May 08 15:55:47 testvm1.both.org hello.sh[840]: ###############################
May 08 15:55:47 testvm1.both.org hello.sh[840]: ######### Hello World! ########
May 08 15:55:47 testvm1.both.org hello.sh[840]: ###############################
-- Reboot --
May 08 16:01:51 testvm1.both.org hello.sh[840]: ###############################
May 08 16:01:51 testvm1.both.org hello.sh[840]: ######### Hello World! ########
May 08 16:01:51 testvm1.both.org hello.sh[840]: ###############################
-- Reboot --
May 10 10:37:49 testvm1.both.org hello.sh[842]: ###############################
May 10 10:37:49 testvm1.both.org hello.sh[842]: ######### Hello World! ########
May 10 10:37:49 testvm1.both.org hello.sh[842]: ###############################
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ######### Hello World! ########
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
[root@testvm1 ~]#
為了定位 hello.service 單元的 systemd 記錄,你可以在 systemd 中搜索。你可以使用 G+Enter 來翻頁到日志記錄 記錄的末尾,然后用回滾來找到你感興趣的日志。使用 -b 選項(xiàng)僅展示最近啟動(dòng)的記錄。
[root@testvm1 ~]# journalctl -b -t systemd
<剪去>
May 10 10:37:49 testvm1.both.org systemd[1]: Starting SYSV: Late init script for live image....
May 10 10:37:49 testvm1.both.org systemd[1]: Started SYSV: Late init script for live image..
May 10 10:37:49 testvm1.both.org systemd[1]: hello.service: Succeeded.
May 10 10:37:49 testvm1.both.org systemd[1]: Finished My hello shell script.
May 10 10:37:50 testvm1.both.org systemd[1]: Starting D-Bus System Message Bus...
May 10 10:37:50 testvm1.both.org systemd[1]: Started D-Bus System Message Bus.
我拷貝了一些其他的日志記錄,讓你對你可能找到的東西有所了解。這條命令噴出了所有屬于 systemd 的日志內(nèi)容 —— 當(dāng)我寫這篇時(shí)是 109183 行。這是一個(gè)需要整理的大量數(shù)據(jù)。你可以使用頁面的搜索功能,通常是 less 或者你可以使用內(nèi)置的 grep 特性。-g( 或 --grep=)選項(xiàng)可以使用兼容 Perl 的正則表達(dá)式。
[root@testvm1 ~]# journalctl -b -t systemd -g "hello"
[root@testvm1 ~]# journalctl -b -t systemd -g "hello"
-- Logs begin at Tue 2020-05-05 18:11:49 EDT, end at Sun 2020-05-10 11:01:01 EDT. --
May 10 10:37:49 testvm1.both.org systemd[1]: Starting My hello shell script...
May 10 10:37:49 testvm1.both.org systemd[1]: hello.service: Succeeded.
May 10 10:37:49 testvm1.both.org systemd[1]: Finished My hello shell script.
May 10 10:54:45 testvm1.both.org systemd[1]: Starting My hello shell script...
May 10 10:54:45 testvm1.both.org systemd[1]: hello.service: Succeeded.
May 10 10:54:45 testvm1.both.org systemd[1]: Finished My hello shell script.
[root@testvm1 ~]#
你可以使用標(biāo)準(zhǔn)的 GNU grep 命令,但是這不會(huì)展示日志首行的元數(shù)據(jù)。
如果你只想看包含你的 hello 服務(wù)的日志記錄,你可以指定時(shí)間來縮小范圍。舉個(gè)例子,我將在我的測試虛擬機(jī)上以 10:54:00 為開始時(shí)間,這是上述的日志記錄開始的分鐘數(shù)。注意 --since= 的選項(xiàng)必須加引號,這個(gè)選項(xiàng)也可以寫成 -S "某個(gè)時(shí)間"。
日期和時(shí)間可能在你的機(jī)器上有所不同,所以確保使用能匹配你日志中的時(shí)間的時(shí)間戳。
[root@testvm1 ~]# journalctl --since="2020-05-10 10:54:00"
May 10 10:54:35 testvm1.both.org audit: BPF prog-id=54 op=LOAD
May 10 10:54:35 testvm1.both.org audit: BPF prog-id=55 op=LOAD
May 10 10:54:45 testvm1.both.org systemd[1]: Starting My hello shell script...
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ######### Hello World! ########
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
May 10 10:54:45 testvm1.both.org systemd[1]: hello.service: Succeeded.
May 10 10:54:45 testvm1.both.org systemd[1]: Finished My hello shell script.
May 10 10:54:45 testvm1.both.org audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd"'
May 10 10:54:45 testvm1.both.org audit[1]: SERVICE_STOP pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd/"'
May 10 10:56:00 testvm1.both.org NetworkManager[840]: [1589122560.0633] dhcp4 (enp0s3): error -113 dispatching events
May 10 10:56:00 testvm1.both.org NetworkManager[840]: [1589122560.0634] dhcp4 (enp0s3): state changed bound -> fail
<剪去>
since 選項(xiàng)跳過了指定時(shí)間點(diǎn)的所有記錄,但在此時(shí)間點(diǎn)之后仍有大量你不需要的記錄。你也可以使用 until 選項(xiàng)來裁剪掉你感興趣的時(shí)間之后的記錄。我想要事件發(fā)生時(shí)附近的一分鐘,其他的都不用:
[root@testvm1 ~]# journalctl --since="2020-05-10 10:54:35" --until="2020-05-10 10:55:00"
-- Logs begin at Tue 2020-05-05 18:11:49 EDT, end at Sun 2020-05-10 11:04:59 EDT. --
May 10 10:54:35 testvm1.both.org systemd[1]: Reloading.
May 10 10:54:35 testvm1.both.org audit: BPF prog-id=27 op=UNLOAD
May 10 10:54:35 testvm1.both.org audit: BPF prog-id=26 op=UNLOAD
<剪去>
ay 10 10:54:35 testvm1.both.org audit: BPF prog-id=55 op=LOAD
May 10 10:54:45 testvm1.both.org systemd[1]: Starting My hello shell script...
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ######### Hello World! ########
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
May 10 10:54:45 testvm1.both.org systemd[1]: hello.service: Succeeded.
May 10 10:54:45 testvm1.both.org systemd[1]: Finished My hello shell script.
May 10 10:54:45 testvm1.both.org audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd>
May 10 10:54:45 testvm1.both.org audit[1]: SERVICE_STOP pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd/>
lines 1-46/46 (END)
如果在這個(gè)時(shí)間段中仍然有大量的活動(dòng)的話,你可以使用這些選項(xiàng)組合來進(jìn)一步縮小結(jié)果數(shù)據(jù)流:
[root@testvm1 ~]# journalctl --since="2020-05-10 10:54:35" --until="2020-05-10 10:55:00" -t "hello.sh"
-- Logs begin at Tue 2020-05-05 18:11:49 EDT, end at Sun 2020-05-10 11:10:41 EDT. --
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ######### Hello World! ########
May 10 10:54:45 testvm1.both.org hello.sh[1380]: ###############################
[root@testvm1 ~]#
你的結(jié)果應(yīng)該與我的相似。你可以從這一系列的實(shí)驗(yàn)中看出,這個(gè)服務(wù)運(yùn)行的很正常。
重啟 —— 還是走到這一步
到目前為止,你還沒有重啟過安裝了服務(wù)的機(jī)器。所以現(xiàn)在重啟吧,因?yàn)楫吘惯@個(gè)教程是關(guān)于啟動(dòng)階段程序運(yùn)行的情況。首先,你需要在啟動(dòng)序列中啟用這個(gè)服務(wù)。
[root@testvm1 ~]# systemctl enable hello.service
Created symlink /etc/systemd/system/multi-user.target.wants/hello.service → /etc/systemd/system/hello.service.
[root@testvm1 ~]#
注意到這個(gè)軟鏈接是被創(chuàng)建在 /etc/systemd/system/multi-user.target.wants 目錄下的。這是因?yàn)榉?wù)單元文件指定了服務(wù)是被 multi-user.target 所“需要”的。
重啟機(jī)器,確保能在啟動(dòng)階段觀察數(shù)據(jù)流,這樣你能看到 “Hello world” 信息。等等……你看見了么?嗯,我看見了。盡管它很快被刷過去了,但是我確實(shí)看到 systemd 的信息顯示它啟動(dòng)了 hello.service 服務(wù)。
看看上次系統(tǒng)啟動(dòng)后的日志。你可以使用頁面搜索工具 less 來找到 “Hello” 或 “hello”。我裁剪了很多數(shù)據(jù),但是留下了附近的日志記錄,這樣你就能感受到和你服務(wù)有關(guān)的日志記錄在本地是什么樣子的:
[root@testvm1 ~]# journalctl -b
<剪去>
May 10 10:37:49 testvm1.both.org systemd[1]: Listening on SSSD Kerberos Cache Manager responder socket.
May 10 10:37:49 testvm1.both.org systemd[1]: Reached target Sockets.
May 10 10:37:49 testvm1.both.org systemd[1]: Reached target Basic System.
May 10 10:37:49 testvm1.both.org systemd[1]: Starting Modem Manager...
May 10 10:37:49 testvm1.both.org systemd[1]: Starting Network Manager...
May 10 10:37:49 testvm1.both.org systemd[1]: Starting Avahi mDNS/DNS-SD Stack...
May 10 10:37:49 testvm1.both.org systemd[1]: Condition check resulted in Secure Boot DBX (blacklist) updater being skipped.
May 10 10:37:49 testvm1.both.org systemd[1]: Starting My hello shell script...
May 10 10:37:49 testvm1.both.org systemd[1]: Starting IPv4 firewall with iptables...
May 10 10:37:49 testvm1.both.org systemd[1]: Started irqbalance daemon.
May 10 10:37:49 testvm1.both.org audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=irqbalance comm="systemd" exe="/usr/lib/sy>"'
May 10 10:37:49 testvm1.both.org systemd[1]: Starting LSB: Init script for live image....
May 10 10:37:49 testvm1.both.org systemd[1]: Starting Hardware Monitoring Sensors...
<剪去>
May 10 10:37:49 testvm1.both.org systemd[1]: Starting NTP client/server...
May 10 10:37:49 testvm1.both.org systemd[1]: Starting SYSV: Late init script for live image....
May 10 10:37:49 testvm1.both.org systemd[1]: Started SYSV: Late init script for live image..
May 10 10:37:49 testvm1.both.org audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=livesys-late comm="systemd" exe="/usr/lib/>"'
May 10 10:37:49 testvm1.both.org hello.sh[842]: ###############################
May 10 10:37:49 testvm1.both.org hello.sh[842]: ######### Hello World! ########
May 10 10:37:49 testvm1.both.org hello.sh[842]: ###############################
May 10 10:37:49 testvm1.both.org systemd[1]: hello.service: Succeeded.
May 10 10:37:49 testvm1.both.org systemd[1]: Finished My hello shell script.
May 10 10:37:49 testvm1.both.org audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd>"'
May 10 10:37:49 testvm1.both.org audit[1]: SERVICE_STOP pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd/>
May 10 10:37:50 testvm1.both.org audit: BPF prog-id=28 op=LOAD
<剪去>
你可以看到 systemd 啟動(dòng)了 hello.service 單元,它執(zhí)行了 hello.sh 腳本并將輸出記錄在日志中。如果你能在啟動(dòng)階段抓到它,你也應(yīng)該能看見,systemd 信息表明了它正在啟動(dòng)這個(gè)腳本,另外一條信息表明了服務(wù)成功。通過觀察上面數(shù)據(jù)流中第一條 systemd 消息,你會(huì)發(fā)現(xiàn) systemd 在到達(dá)基本的系統(tǒng)目標(biāo)后很快就啟動(dòng)了你的服務(wù)。
但是我想看見信息在啟動(dòng)階段也被打印出來。有一種方法可以做到:在 hello.service 文件的 [Service] 段中加入下述行:
StandardOutput=journal+console
現(xiàn)在 hello.service 文件看起來像這樣:
# Simple service unit file to use for testing
# startup configurations with systemd.
# By David Both
# Licensed under GPL V2
#
[Unit]
Description=My hello shell script
[Service]
Type=oneshot
ExecStart=/usr/local/bin/hello.sh
StandardOutput=journal+console
[Install]
WantedBy=multi-user.target
加上這一行后,重啟系統(tǒng),并在啟動(dòng)過程中觀察顯示屏上滾動(dòng)的數(shù)據(jù)流。你應(yīng)該在它的小方框中看到信息。在啟動(dòng)序列完成后,你可以查看最近的啟動(dòng)日志,然后定位到你新服務(wù)的日志記錄。
修改次序
現(xiàn)在你的服務(wù)已經(jīng)可用了,你可以看看它在啟動(dòng)序列中哪個(gè)位置啟動(dòng)的,嘗試下修改它。需要牢記的是 systemd 傾向于在每個(gè)主要目標(biāo)(basic.target、multi-user.target 和 graphical.**target)中并行啟動(dòng)盡可能多的服務(wù)和其他的單元類型。你應(yīng)該剛剛看過最近一次開機(jī)的日志記錄,它應(yīng)該和上面我的日志看上去類似。
注意,systemd 在它到達(dá)到基本系統(tǒng)目標(biāo)(basic.target)后不久就啟動(dòng)了你的測試服務(wù)。這正是你在在服務(wù)單元文件的 WantedBy 行中指定的,所以它是對的。在你做出修改之前,列出 /etc/systemd/system/multi-user.target.wants 目錄下的內(nèi)容,你會(huì)看到一個(gè)指向服務(wù)單元文件的軟鏈接。服務(wù)單元文件的 [Install] 段指定了哪一個(gè)目標(biāo)會(huì)啟動(dòng)這個(gè)服務(wù),執(zhí)行 systemctl enable hello.service 命令會(huì)在適當(dāng)?shù)?nbsp;targets.wants 路徑下創(chuàng)建軟鏈接。
hello.service -> /etc/systemd/system/hello.service
某些服務(wù)需要在 basic.target 階段啟動(dòng),其他則沒這個(gè)必要,除非系統(tǒng)正在啟動(dòng) graphical.target。這個(gè)實(shí)驗(yàn)中的服務(wù)不會(huì)在 basic.target 期間啟動(dòng) —— 假設(shè)你直到 graphical.target 階段才需要它啟動(dòng)。那么修改 WantedBy 這一行:
WantedBy=graphical.target
一定要先禁用 hello.service 再重新啟用它,這樣可以刪除舊鏈接并且在 graphical.targets.wants 目錄下創(chuàng)建一個(gè)新的鏈接。我注意到如果我在修改服務(wù)需要的目標(biāo)之前忘記禁用該服務(wù),我可以運(yùn)行 systemctl disable 命令,鏈接將從兩個(gè) targets.wants 目錄中刪除。之后我只需要重新啟用這個(gè)服務(wù)然后重啟電腦。
啟動(dòng) graphical.target 下的服務(wù)有個(gè)需要注意的地方,如果電腦啟動(dòng)到 multi-user.target 階段,這個(gè)服務(wù)不會(huì)自動(dòng)啟動(dòng)。如果這個(gè)服務(wù)需要 GUI 桌面接口,這或許是你想要的,但是它同樣可能不是你想要的。
用 -o short-monotonic 選項(xiàng)來查看 graphical.target 和 multi-user.target 的日志,展示內(nèi)核啟動(dòng)幾秒后的日志,精度為微秒級別:
[root@testvm1 ~]# journalctl -b -o short-monotonic
multi-user.target 的部分日志:
[ 17.264730] testvm1.both.org systemd[1]: Starting My hello shell script...
[ 17.265561] testvm1.both.org systemd[1]: Starting IPv4 firewall with iptables...
<剪去>
[ 19.478468] testvm1.both.org systemd[1]: Starting LSB: Init script for live image....
[ 19.507359] testvm1.both.org iptables.init[844]: iptables: Applying firewall rules: [ OK ]
[ 19.507835] testvm1.both.org hello.sh[843]: ###############################
[ 19.507835] testvm1.both.org hello.sh[843]: ######### Hello World! ########
[ 19.507835] testvm1.both.org hello.sh[843]: ###############################
<剪去>
[ 21.482481] testvm1.both.org systemd[1]: hello.service: Succeeded.
[ 21.482550] testvm1.both.org smartd[856]: Opened configuration file /etc/smartmontools/smartd.conf
[ 21.482605] testvm1.both.org systemd[1]: Finished My hello shell script.
還有部分 graphical.target 的日志:
[ 19.436815] testvm1.both.org systemd[1]: Starting My hello shell script...
[ 19.437070] testvm1.both.org systemd[1]: Starting IPv4 firewall with iptables...
<剪去>
[ 19.612614] testvm1.both.org hello.sh[841]: ###############################
[ 19.612614] testvm1.both.org hello.sh[841]: ######### Hello World! ########
[ 19.612614] testvm1.both.org hello.sh[841]: ###############################
[ 19.629455] testvm1.both.org audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'
[ 19.629569] testvm1.both.org audit[1]: SERVICE_STOP pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'
[ 19.629682] testvm1.both.org systemd[1]: hello.service: Succeeded.
[ 19.629782] testvm1.both.org systemd[1]: Finished My hello shell script.
盡管單元文件的 WantedBy 部分包含了 graphical.target,hello.service 單元在啟動(dòng)后大約 19.5 或 19.6 秒后運(yùn)行。但是 hello.service 在 multi-user.target 中開始于 17.24 秒,在 graphical target 中開始于 19.43 秒。
這意味著什么呢?看看 /etc/systemd/system/default.target 這個(gè)鏈接。文件內(nèi)容顯示 systemd 先啟動(dòng)了默認(rèn)目標(biāo) graphical.target,然后 graphical.target 觸發(fā)了 multi-user.target。
[root@testvm1 system]# cat default.target
# SPDX-License-Identifier: LGPL-2.1+
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=Graphical Interface
Documentation=man:systemd.special(7)
Requires=multi-user.target
Wants=display-manager.service
Conflicts=rescue.service rescue.target
After=multi-user.target rescue.service rescue.target display-manager.service
AllowIsolate=yes
[root@testvm1 system]#
不管是用 graphical.target 還是 multi-user.target 啟動(dòng)服務(wù),hello.service 單元都在啟動(dòng)后的 19.5 或 19.6 秒后啟動(dòng)?;谶@個(gè)事實(shí)和日志結(jié)果(特別是使用單調(diào)輸出的日志),你就知道這些目標(biāo)是在并行啟動(dòng)。再看看日志中另外一件事:
[ 28.397330] testvm1.both.org systemd[1]: Reached target Multi-User System.
[ 28.397431] testvm1.both.org systemd[1]: Reached target Graphical Interface.
兩個(gè)目標(biāo)幾乎是同時(shí)完成的。這是和理論一致的,因?yàn)?nbsp;graphical.target 觸發(fā)了 multi-user.target,在 multi-user.target 到達(dá)(即完成)之前它是不會(huì)完成的。但是 hello.service 比這個(gè)完成的早的多。
這一切表明,這兩個(gè)目標(biāo)幾乎是并行啟動(dòng)的。如果你查看日志,你會(huì)發(fā)現(xiàn)各種目標(biāo)和來自這類主要目標(biāo)的服務(wù)大多是平行啟動(dòng)的。很明顯,multi-user.target 沒有必要在 graphical.target 啟動(dòng)前完成。所以,簡單的使用這些主要目標(biāo)來并不能很好地排序啟動(dòng)序列,盡管它在保證單元只在它們被 graphical.target 需要時(shí)啟動(dòng)這方面很有用。
在繼續(xù)之前,把 hello.service 單元文件回滾至 WantedBy=multi-user.target(如果還沒做的話)。
確保一個(gè)服務(wù)在網(wǎng)絡(luò)運(yùn)行后啟動(dòng)
一個(gè)常見的啟動(dòng)問題是保證一個(gè)單元在網(wǎng)絡(luò)啟動(dòng)運(yùn)行后再啟動(dòng)。Freedesktop.org 的文章《在網(wǎng)絡(luò)啟動(dòng)后運(yùn)行服務(wù)》中提到,目前沒有一個(gè)真正的關(guān)于網(wǎng)絡(luò)何時(shí)算作“啟動(dòng)”的共識(shí)。然而,這篇文章提供了三個(gè)選項(xiàng),滿足完全可用網(wǎng)絡(luò)需求的是 network-online.target。需要注意的是 network.target 是在關(guān)機(jī)階段使用的而不是啟動(dòng)階段,所以它對你做有序啟動(dòng)方面沒什么幫助。
在做出任何改變之前,一定要檢查下日志,確認(rèn) hello.service 單元在網(wǎng)絡(luò)可用之前可以正確啟動(dòng)。你可以在日志中查找 network-online.target 來確認(rèn)。
你的服務(wù)并不真的需要網(wǎng)絡(luò)服務(wù),但是你可以把它當(dāng)作是需要網(wǎng)絡(luò)的。
因?yàn)樵O(shè)置 WantedBy=graphical.target 并不能保證服務(wù)會(huì)在網(wǎng)絡(luò)啟動(dòng)可用后啟動(dòng),所以你需要其他的方法來做到這一點(diǎn)。幸運(yùn)的是,有個(gè)簡單的方法可以做到。將下面兩行代碼加入 hello.service 單元文件的 [Unit] 段:
After=network-online.target
Wants=network-online.target
兩個(gè)字段都需要才能生效。重啟機(jī)器,在日志中找到服務(wù)的記錄:
[ 26.083121] testvm1.both.org NetworkManager[842]: [1589227764.0293] device (enp0s3): Activation: successful, device activated.
[ 26.083349] testvm1.both.org NetworkManager[842]: [1589227764.0301] manager: NetworkManager state is now CONNECTED_GLOBAL
[ 26.085818] testvm1.both.org NetworkManager[842]: [1589227764.0331] manager: startup complete
[ 26.089911] testvm1.both.org systemd[1]: Finished Network Manager Wait Online.
[ 26.090254] testvm1.both.org systemd[1]: Reached target Network is Online.
[ 26.090399] testvm1.both.org audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=NetworkManager-wait-online comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? termina>"'
[ 26.091991] testvm1.both.org systemd[1]: Starting My hello shell script...
[ 26.095864] testvm1.both.org sssd[be[implicit_files]][1007]: Starting up
[ 26.290539] testvm1.both.org systemd[1]: Condition check resulted in Login and scanning of iSCSI devices being skipped.
[ 26.291075] testvm1.both.org systemd[1]: Reached target Remote File Systems (Pre).
[ 26.291154] testvm1.both.org systemd[1]: Reached target Remote File Systems.
[ 26.292671] testvm1.both.org systemd[1]: Starting Notify NFS peers of a restart...
[ 26.294897] testvm1.both.org systemd[1]: iscsi.service: Unit cannot be reloaded because it is inactive.
[ 26.304682] testvm1.both.org hello.sh[1010]: ###############################
[ 26.304682] testvm1.both.org hello.sh[1010]: ######### Hello World! ########
[ 26.304682] testvm1.both.org hello.sh[1010]: ###############################
[ 26.306569] testvm1.both.org audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'
[ 26.306669] testvm1.both.org audit[1]: SERVICE_STOP pid=1 uid=0 auid=4294967295 ses=4294967295 msg='unit=hello comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'
[ 26.306772] testvm1.both.org systemd[1]: hello.service: Succeeded.
[ 26.306862] testvm1.both.org systemd[1]: Finished My hello shell script.
[ 26.584966] testvm1.both.org sm-notify[1011]: Version 2.4.3 starting
這樣證實(shí)了 hello.service 單元會(huì)在 network-online.target 之后啟動(dòng)。這正是你想要的。你可能也看見了 “Hello World” 消息在啟動(dòng)階段出現(xiàn)。還需要注意的是,在啟動(dòng)時(shí)記錄出現(xiàn)的時(shí)間戳比之前要晚了大約 6 秒。
定義啟動(dòng)序列的最好方法
本文章詳細(xì)地探討了 Linux 啟動(dòng)時(shí) systemd 和單元文件以及日志的細(xì)節(jié),并且發(fā)現(xiàn)了當(dāng)錯(cuò)誤被引入單元文件時(shí)候會(huì)發(fā)生什么。作為系統(tǒng)管理員,我發(fā)現(xiàn)這類實(shí)驗(yàn)有助于我理解程序或者服務(wù)出故障時(shí)的行為,并且在安全環(huán)境中有意破壞是一種學(xué)習(xí)的好方法。
文章中實(shí)驗(yàn)結(jié)果證明,僅將服務(wù)單元添加至 multi-user.target 或者 graphical.target 并不能確定它在啟動(dòng)序列中的位置。它僅僅決定了一個(gè)單元是否作為圖形環(huán)境一部分啟動(dòng)。事實(shí)上,啟動(dòng)目標(biāo) multi-user.target 和 graphical.target 和所有它們的 Wants 以及 Required 幾乎是并行啟動(dòng)的。確保單元在特定位置啟動(dòng)的最好方法是確定它所依賴的單元,并將新單元配置成 Want 和 After 它的依賴。
資源
網(wǎng)上有大量的關(guān)于 systemd 的參考資料,但是大部分都有點(diǎn)簡略、晦澀甚至有誤導(dǎo)性。除了本文中提到的資料,下列的網(wǎng)頁提供了跟多可靠且詳細(xì)的 systemd 入門信息。
Fedora 項(xiàng)目有一篇切實(shí)好用的 systemd 入門,它囊括了幾乎所有你需要知道的關(guān)于如何使用 systemd 配置、管理和維護(hù) Fedora 計(jì)算機(jī)的信息。
Fedora 項(xiàng)目也有一個(gè)不錯(cuò)的 備忘錄,交叉引用了過去 SystemV 命令和 systemd 命令做對比。
關(guān)于 systemd 的技術(shù)細(xì)節(jié)和創(chuàng)建這個(gè)項(xiàng)目的原因,請查看 Freedesktop.org 上的 systemd 描述。
Linux.com 的“更多 systemd 的樂趣”欄目提供了更多高級的 systemd 信息和技巧。
此外,還有一系列深度的技術(shù)文章,是由 systemd 的設(shè)計(jì)者和主要開發(fā)者 Lennart Poettering 為 Linux 系統(tǒng)管理員撰寫的。這些文章寫于 2010 年 4 月至 2011 年 9 月間,但它們現(xiàn)在和當(dāng)時(shí)一樣具有現(xiàn)實(shí)意義。關(guān)于 systemd 及其生態(tài)的許多其他好文章都是基于這些文章:
- Rethinking PID 1
- systemd for Administrators,Part I
- systemd for Administrators,Part II
- systemd for Administrators,Part III
- systemd for Administrators,Part IV
- systemd for Administrators,Part V
- systemd for Administrators,Part VI
- systemd for Administrators,Part VII
- systemd for Administrators,Part VIII
- systemd for Administrators,Part IX
- systemd for Administrators,Part X
- systemd for Administrators,Part XI
網(wǎng)站欄目:使用systemd來管理啟動(dòng)項(xiàng)
路徑分享:http://www.5511xx.com/article/djeipei.html


咨詢
建站咨詢
