systemd 基本工具
檢視和控制systemd的主要指令是
systemctl
。該指令可用于檢視系統狀态和管理系統及服務。詳見
man 1 systemctl
。
小貼士:
- 在
參數中添加systemctl
可以實作對其他機器的遠端控制。該過程使用 SSH連接配接。-H <使用者名>@<主機名>
-
是 systemd 的官方圖形前端。官方軟體倉庫 提供了穩定版本 systemd-ui,AUR 中的軟體包 systemd-ui-gitAUR[broken link: archived in aur-mirror] 提供了開發版本。systemadm
分析系統狀态
輸出激活的單元:
$ systemctl
以下指令等效:
$ systemctl list-units
輸出運作失敗的單元:
$ systemctl --failed
所有可用的單元檔案存放在
/usr/lib/systemd/system/
和
/etc/systemd/system/
目錄(後者優先級更高)。檢視所有已安裝服務:
$ systemctl list-unit-files
使用單元
一個單元配置檔案可以描述如下内容之一:系統服務(
.service
)、挂載點(
.mount
)、sockets(
.sockets
) 、系統裝置(
.device
)、交換分區(
.swap
)、檔案路徑(
.path
)、啟動目标(
.target
)、由 systemd 管理的計時器(
.timer
)。詳情參閱
man 5 systemd.unit
。
使用
systemctl
控制單元時,通常需要使用單元檔案的全名,包括擴充名(例如
sshd.service
)。但是有些單元可以在
systemctl
中使用簡寫方式。
- 如果無擴充名,systemctl 預設把擴充名當作
。例如.service
和netcfg
是等價的。netcfg.service
- 挂載點會自動轉化為相應的
單元。例如.mount
等價于/home
。home.mount
- 裝置會自動轉化為相應的
單元,是以.device
等價于/dev/sda2
。dev-sda2.device
Note: 有一些單元的名稱包含一個
@
标記, (e.g.
): 這意味着它是模闆單元
[email protected]
的一個 執行個體。
string
被稱作執行個體辨別符, 在 systemctl 調用模闆單元時,會将其當作一個參數傳給模闆單元,模闆單元會使用這個傳入的參數代替模闆中的
%I
訓示符。
在執行個體化之前,systemd 會先檢查
[email protected]
檔案是否存在(如果存在,應該就是直接使用這個檔案,而不是模闆執行個體化了)。大多數情況下,包換
@
标記都意味着這個檔案是模闆。如果一個模闆單元沒有執行個體化就調用,該調用會傳回失敗,因為模闆單元中的
%I
訓示符沒有被替換。
Tip:
- 下面的大部分指令都可以跟多個單元名, 詳細資訊參見
。man systemctl
- 從systemd 220版本開始,
指令在systemctl
、enable
和disable
子指令中增加了mask
選項,可以實作激活的同時啟動服務,取消激活的同時停止服務。--now
- 一個軟體包可能會提供多個不同的單元。如果你已經安裝了軟體包,可以通過
指令檢查這個軟體包提供了哪些單元。pacman -Qql package | grep systemd
立即激活單元:
# systemctl start <單元>
立即停止單元:
# systemctl stop <單元>
重新開機單元:
# systemctl restart <單元>
重新加載配置:
# systemctl reload <單元>
輸出單元運作狀态:
$ systemctl status <單元>
檢查單元是否配置為自動啟動:
$ systemctl is-enabled <單元>
開機自動激活單元:
# systemctl enable <單元>
取消開機自動激活單元:
# systemctl disable <單元>
禁用一個單元(禁用後,間接啟動也是不可能的):
# systemctl mask <單元>
取消禁用一個單元:
# systemctl unmask <單元>
顯示單元的手冊頁(必須由單元檔案提供):
# systemctl help <單元>
重新載入 systemd,掃描新的或有變動的單元:
# systemctl daemon-reload
電源管理
安裝 polkit 後才可以一般使用者身份使用電源管理。
如果你正登入在一個本地的
systemd-logind
使用者會話,且目前沒有其它活動的會話,那麼以下指令無需root權限即可執行。否則(例如,目前有另一個使用者登入在某個tty),systemd 将會自動請求輸入root密碼。
重新開機:
$ systemctl reboot
退出系統并停止電源:
$ systemctl poweroff
待機:
$ systemctl suspend
休眠:
$ systemctl hibernate
混合休眠模式(同時休眠到硬碟并待機):
$ systemctl hybrid-sleep
編寫單元檔案
systemd
單元檔案的文法來源于 XDG桌面入口配置檔案
.desktop
檔案,最初的源頭則是Microsoft Windows的
.ini
檔案。單元檔案可以從兩個地方加載,優先級從低到高分别是:
-
: 軟體包安裝的單元/usr/lib/systemd/system/
-
: 系統管理者安裝的單元/etc/systemd/system/
Note: 當
systemd
運作在 使用者模式下時,使用的加載路徑是完全不同的。
單元檔案的文法,可以參考系統已經安裝的單元,也可以參考
man systemd.service
中的EXAMPLES章節。
小貼士: 以
#
開頭的注釋可能也能用在 unit-files 中, 但是隻能在新行中使用。 不要在 systemd 的參數後面使用行末注釋, 否則 unit 将會啟動失敗。
處理依賴關系
使用systemd時,可通過正确編寫單元配置檔案來解決其依賴關系。典型的情況是,單元
A
要求單元
B
在
A
啟動之前運作。在此情況下,向單元
A
配置檔案中的
[Unit]
段添加
Requires=B
和
After=B
即可。若此依賴關系是可選的,可添加
Wants=B
和
After=B
。請注意
Wants=
和
Requires=
并不意味着
After=
,即如果
After=
選項沒有制定,這兩個單元将被并行啟動。
依賴關系通常被用在服務(service)而不是目标(target)上。例如,
network.target
一般會被某個配置網絡接口的服務引入,是以,将自定義的單元排在該服務之後即可,因為
network.target
已經啟動。
服務類型
編寫自定義的 service 檔案時,可以選擇幾種不同的服務啟動方式。啟動方式可通過配置檔案
[Service]
段中的
Type=
參數進行設定。
-
(預設值):systemd認為該服務将立即啟動。服務程序不會fork。如果該服務要啟動其他服務,不要使用此類型啟動,除非該服務是socket激活型。Type=simple
-
:systemd認為當該服務程序fork,且父程序退出後服務啟動成功。對于正常的守護程序(daemon),除非你确定此啟動方式無法滿足需求,使用此類型啟動即可。使用此啟動類型應同時指定Type=forking
,以便systemd能夠跟蹤服務的主程序。PIDFile=
-
:這一選項适用于隻執行一項任務、随後立即退出的服務。可能需要同時設定Type=oneshot
使得 systemd 在服務程序退出之後仍然認為服務處于激活狀态。RemainAfterExit=yes
-
:與Type=notify
相同,但約定服務會在就緒後向 systemd 發送一個信号。這一通知的實作由Type=simple
提供。libsystemd-daemon.so
-
:若以此方式啟動,當指定的Type=dbus
出現在DBus系統總線上時,systemd認為服務就緒。BusName
-
:Type=idle
會等待所有任務(Jobs)處理完成後,才開始執行systemd
類型的單元。除此之外,其他行為和idle
類似。Type=simple
type
的更多解釋可以參考 systemd.service(5)。
修改現存單元檔案
要更改由軟體包提供的單元檔案,先建立名為
/etc/systemd/system/<單元名>.d/
的目錄(如
/etc/systemd/system/httpd.service.d/
),然後放入
*.conf
檔案,其中可以添加或重置參數。這裡設定的參數優先級高于原來的單元檔案。例如,如果想添加一個額外的依賴,建立這麼一個檔案即可:
/etc/systemd/system/<unit>.d/customdependency.conf
[Unit]
Requires=<新依賴>
After=<新依賴>
As another example, in order to replace the
ExecStart
directive for a unit that is not of type
oneshot
, create the following file:
/etc/systemd/system/unit.d/customexec.conf
[Service]
ExecStart=
ExecStart=new command
想知道為什麼修改
ExecStart
前必須将其置空,參見 ([1]).
下面是自動重新開機服務的一個例子:
/etc/systemd/system/unit.d/restart.conf
[Service]
Restart=always
RestartSec=30
然後運作以下指令使更改生效:
# systemctl daemon-reload
# systemctl restart <單元>
此外,把舊的單元檔案從
/usr/lib/systemd/system/
複制到
/etc/systemd/system/
,然後進行修改,也可以達到同樣效果。在
/etc/systemd/system/
目錄中的單元檔案的優先級總是高于
/usr/lib/systemd/system/
目錄中的同名單元檔案。注意,當
/usr/lib/
中的單元檔案因軟體包更新變更時,
/etc/
中自定義的單元檔案不會同步更新。此外,你還得執行
systemctl reenable <unit>
,手動重新啟用該單元。是以,建議使用前面一種利用
*.conf
的方法。
小貼士: 用
systemd-delta
指令來檢視哪些單元檔案被覆寫、哪些被修改。系統維護的時候需要及時了解哪些單元已經有了更新。
可從 官方倉庫 安裝 vim-systemd 軟體包,可以使 unit 配置檔案在 Vim 下支援文法高亮。
目标(target)
啟動級别(runlevel)是一個舊的概念。現在,systemd 引入了一個和啟動級别功能相似又不同的概念——目标(target)。不像數字表示的啟動級别,每個目标都有名字和獨特的功能,并且能同時啟用多個。一些目标繼承其他目标的服務,并啟動新服務。systemd 提供了一些模仿 sysvinit 啟動級别的目标,仍可以使用舊的
telinit 啟動級别
指令切換。
擷取目前目标
不要使用
runlevel
指令了:
$ systemctl list-units --type=target
建立新目标
在 Fedora 中,啟動級别 0、1、3、5、6 都被賦予特定用途,并且都對應一個 systemd 的目标。然而,沒有什麼很好的移植使用者定義的啟動級别(2、4)的方法。要實作類似功能,可以以原有的啟動級别為基礎,建立一個新的目标
/etc/systemd/system/<新目标>
(可以參考
/usr/lib/systemd/system/graphical.target
),建立
/etc/systemd/system/<新目标>.wants
目錄,向其中加入額外服務的連結(指向
/usr/lib/systemd/system/
中的單元檔案)。
目标表
SysV 啟動級别 | Systemd 目标 | 注釋 |
---|---|---|
runlevel0.target, poweroff.target | 中斷系統(halt) | |
1, s, single | runlevel1.target, rescue.target | 單使用者模式 |
2, 4 | runlevel2.target, runlevel4.target, multi-user.target | 使用者自定義啟動級别,通常識别為級别3。 |
3 | runlevel3.target, multi-user.target | 多使用者,無圖形界面。使用者可以通過終端或網絡登入。 |
5 | runlevel5.target, graphical.target | 多使用者,圖形界面。繼承級别3的服務,并啟動圖形界面服務。 |
6 | runlevel6.target, reboot.target | 重新開機 |
emergency | emergency.target | 急救模式(Emergency shell) |
切換啟動級别/目标
systemd 中,啟動級别通過“目标單元”通路。通過如下指令切換:
# systemctl isolate graphical.target
該指令對下次啟動無影響。等價于
telinit 3
或
telinit 5
。
修改預設啟動級别/目标
開機啟動進的目标是
default.target
,預設連結到
graphical.target
(大緻相當于原來的啟動級别5)。可以通過核心參數更改預設啟動級别:
-
(大緻相當于級别3)systemd.unit=multi-user.target
-
(大緻相當于級别1)systemd.unit=rescue.target
另一個方法是修改
default.target
。可以通過
systemctl
修改它:
# systemctl set-default multi-user.target
要覆寫已經設定的default.target,請使用 force:
# systemctl set-default -f multi-user.target
可以在
systemctl
的輸出中看到指令執行的效果:連結
/etc/systemd/system/default.target
被建立,指向新的預設啟動級别。
臨時檔案
/usr/lib/tmpfiles.d/
和
/etc/tmpfiles.d/
中的檔案描述了 systemd-tmpfiles 如何建立、清理、删除臨時檔案和目錄,這些檔案和目錄通常存放在
/run
和
/tmp
中。配置檔案名稱為
/etc/tmpfiles.d/<program>.conf
。此處的配置能覆寫
/usr/lib/tmpfiles.d/
目錄中的同名配置。
臨時檔案通常和服務檔案同時提供,以生成守護程序需要的檔案和目錄。例如 Samba 服務需要目錄
/run/samba
存在并設定正确的權限位,就象這樣:
/usr/lib/tmpfiles.d/samba.conf
D /run/samba 0755 root root
此外,臨時檔案還可以用來在開機時向特定檔案寫入某些内容。比如,要禁止系統從USB裝置喚醒,利用舊的
/etc/rc.local
可以用
echo USBE > /proc/acpi/wakeup
,而現在可以這麼做:
/etc/tmpfiles.d/disable-usb-wake.conf
w /proc/acpi/wakeup - - - - USBE
詳情參見
systemd-tmpfiles(8)
和
man 5 tmpfiles.d
。
注意: 該方法不能向
/sys
中的配置檔案添加參數,因為
systemd-tmpfiles-setup
有可能在相關子產品加載前運作。這種情況下,需要首先通過
modinfo <子產品名>
确認需要的參數,并在
/etc/modprobe.d
下的一個檔案中設定改參數。另外,還可以使用 udev 規則,在裝置就緒時設定相應屬性。
定時器
定時器是以 .timer 為字尾的配置檔案,記錄由system的裡面由時間觸發的動作, 定時器可以替代 cron 的大部分功能。詳情參閱 systemd/Timers (簡體中文).
日志
systemd 提供了自己日志系統(logging system),稱為 journal. 使用 systemd 日志,無需額外安裝日志服務(syslog)。讀取日志的指令:
# journalctl
預設情況下(當
Storage=
在檔案
/etc/systemd/journald.conf
中被設定為
auto
),日志記錄将被寫入
/var/log/journal/
。該目錄是 systemd 軟體包的一部分。若被删除,systemd 不會自動建立它,直到下次更新軟體包時重建該目錄。如果該目錄缺失,systemd 會将日志記錄寫入
/run/systemd/journal
。這意味着,系統重新開機後日志将丢失。
Tip: 如果
/var/log/journal/
位于 btrfs 檔案系統,應該考慮對這個目錄禁用寫入時複制,方法參閱 Btrfs#Copy-On-Write (CoW).
過濾輸出
journalctl
可以根據特定字段過濾輸出。如果過濾的字段比較多,需要較長時間才能顯示出來。
示例:
顯示本次啟動後的所有日志:
# journalctl -b
不過,一般大家更關心的不是本次啟動後的日志,而是上次啟動時的(例如,剛剛系統崩潰了)。可以使用
-b
參數:
-
顯示本次啟動的資訊journalctl -b -0
-
顯示上次啟動的資訊journalctl -b -1
-
顯示上上次啟動的資訊journalctl -b -2
journalctl -b -2
- Show all messages from date (and optional time):
# journalctl --since="2012-10-30 18:17:16"
- Show all messages since 20 minutes ago:
# journalctl --since "20 min ago"
- 顯示最新資訊
# journalctl -f
- 顯示特定程式的所有消息:
# journalctl /usr/lib/systemd/systemd
- 顯示特定程序的所有消息:
# journalctl _PID=1
- 顯示指定單元的所有消息:
# journalctl -u netcfg
- Show kernel ring buffer:
# journalctl -k
- Show auth.log equivalent by filtering on syslog facility:
# journalctl -f -l SYSLOG_FACILITY=10
詳情參閱
man journalctl
、
man systemd.journal-fields
,以及 Lennert 的這篇博文。
日志大小限制
如果按上面的操作保留日志的話,預設日志最大限制為所在檔案系統容量的 10%,即:如果
/var/log/journal
儲存在 50GiB 的根分區中,那麼日志最多存儲 5GiB 資料。可以修改
/etc/systemd/journald.conf
中的
SystemMaxUse
來指定該最大限制。如限制日志最大 50MiB:
SystemMaxUse=50M
詳情參見
man journald.conf
.
配合 syslog 使用
systemd 提供了 socket
/run/systemd/journal/syslog
,以相容傳統日志服務。所有系統資訊都會被傳入。要使傳統日志服務工作,需要讓服務連結該 socket,而非
/dev/log
(官方說明)。Arch 軟體倉庫中的syslog-ng 已經包含了需要的配置。
As of systemd 216 the default
journald.conf
for forwarding to the socket is
no
. This means you will need to set the option
ForwardToSyslog=yes
in
/etc/systemd/journald.conf
to actually use syslog-ng with journald. See Syslog-ng#Overview for details.
If you use rsyslog instead, it is not necessary to change the option because rsyslog pulls the messages from the journal by itself.
設定開機啟動 syslog-ng:
# systemctl enable syslog-ng
這裡有一份很不錯的
journalctl
指南。
Forward journald to /dev/tty12
In
/etc/systemd/journald.conf
enable the following:
ForwardToConsole=yes
TTYPath=/dev/tty12
MaxLevelConsole=info
Restart journald with:
# systemctl restart systemd-journald
疑難解答
Investigating systemd errors
As an example, we will investigate an error with
systemd-modules-load
service:
1. Lets find the systemd services which fail to start:
$ systemctl --state=failed
systemd-modules-load.service loaded failed failed Load Kernel Modules
2. Ok, we found a problem with
systemd-modules-load
service. We want to know more:
$ systemctl status systemd-modules-load
systemd-modules-load.service - Load Kernel Modules
Loaded: loaded (/usr/lib/systemd/system/systemd-modules-load.service; static)
Active: failed (Result: exit-code) since So 2013-08-25 11:48:13 CEST; 32s ago
Docs: man:systemd-modules-load.service(8).
man:modules-load.d(5)
Process: 15630 ExecStart=/usr/lib/systemd/systemd-modules-load (code=exited, status=1/FAILURE)
If the
Process ID
is not listed, just restart the failed service with
systemctl restart systemd-modules-load
3. Now we have the process id (PID) to investigate this error in depth. Enter the following command with the current
Process ID
(here: 15630):
$ journalctl -b _PID=15630
-- Logs begin at Sa 2013-05-25 10:31:12 CEST, end at So 2013-08-25 11:51:17 CEST. --
Aug 25 11:48:13 mypc systemd-modules-load[15630]: Failed to find module 'blacklist usblp'
Aug 25 11:48:13 mypc systemd-modules-load[15630]: Failed to find module 'install usblp /bin/false'
4. We see that some of the kernel module configs have wrong settings. Therefore we have a look at these settings in
/etc/modules-load.d/
:
$ ls -Al /etc/modules-load.d/
...
-rw-r--r-- 1 root root 79 1. Dez 2012 blacklist.conf
-rw-r--r-- 1 root root 1 2. Mär 14:30 encrypt.conf
-rw-r--r-- 1 root root 3 5. Dez 2012 printing.conf
-rw-r--r-- 1 root root 6 14. Jul 11:01 realtek.conf
-rw-r--r-- 1 root root 65 2. Jun 23:01 virtualbox.conf
...
5. The
Failed to find module 'blacklist usblp'
error message might be related to a wrong setting inside of
blacklist.conf
. Lets deactivate it with inserting a trailing # before each option we found via step 3:
/etc/modules-load.d/blacklist.conf
# blacklist usblp
# install usblp /bin/false
6. Now, try to start
systemd-modules-load
:
$ systemctl start systemd-modules-load
If it was successful, this should not prompt anything. If you see any error, go back to step 3 and use the new PID for solving the errors left.
If everything is ok, you can verify that the service was started successfully with:
$ systemctl status systemd-modules-load
systemd-modules-load.service - Load Kernel Modules
Loaded: loaded (/usr/lib/systemd/system/systemd-modules-load.service; static)
Active: active (exited) since So 2013-08-25 12:22:31 CEST; 34s ago
Docs: man:systemd-modules-load.service(8)
man:modules-load.d(5)
Process: 19005 ExecStart=/usr/lib/systemd/systemd-modules-load (code=exited, status=0/SUCCESS)
Aug 25 12:22:31 mypc systemd[1]: Started Load Kernel Modules.
Often you can solve these kind of problems like shown above. For further investigation look at #Diagnosing boot problems.
診斷啟動問題
使用如下核心參數引導:
systemd.log_level=debug systemd.log_target=kmsg log_buf_len=1M
更多有關調試的資訊,參見該文。
Diagnosing problems with a specific service
If some systemd service misbehaves and you want to get more information about what is going on, set the
SYSTEMD_LOG_LEVEL
environment variable to
debug
. For example, to run the systemd-networkd daemon in debug mode:
# systemctl stop systemd-networkd
# SYSTEMD_LOG_LEVEL=debug /lib/systemd/systemd-networkd
Or, equivalently, modify the service file temporarily for gathering enough output. For example:
/lib/systemd/system/systemd-networkd.service
[Service]
...
Environment=SYSTEMD_LOG_LEVEL=debug
....
If debug information is required long-term, add the variable the regular way.
關機/重新開機十分緩慢
如果關機特别慢(甚至跟當機了一樣),很可能是某個拒不退出的服務在作怪。systemd 會等待一段時間,然後再嘗試殺死它。請閱讀這篇文章,确認你是否是該問題受害者。
短時程序無日志記錄
若
journalctl -u foounit.service
沒有顯示某個短時程序的任何輸出,那麼改用 PID 試試。例如,若
systemd-modules-load.service
執行失敗,那麼先用
systemctl status systemd-modules-load
查詢其 PID(比如是123),然後檢索該 PID 相關的日志
journalctl -b _PID=123
。運作時程序的日志中繼資料(諸如 _SYSTEMD_UNIT 和 _COMM)被亂序收集在
/proc
目錄。要修複該問題,必須修改核心,使其通過套接字連接配接來提供上述資料,該過程類似于 SCM_CREDENTIALS。
禁止在程式崩潰時轉儲記憶體
要使用老的核心轉儲,建立下面檔案:
/etc/sysctl.d/49-coredump.conf
kernel.core_pattern = core
kernel.core_uses_pid = 0
然後運作:
# /usr/lib/systemd/systemd-sysctl
同樣可能需要執行“unlimit”設定檔案大小:
$ ulimit -c unlimited
更多資訊請參閱 sysctl.d 和 /proc/sys/kernel 文檔。
Error message on reboot or shutdown
cgroup : option or name mismatch, new: 0x0 "", old: 0x4 "systemd"
See this thread for an explanation.
watchdog watchdog0: watchdog did not stop!
See this thread for an explanation.
Boot time increasing over time
After using
systemd-analyze
a number of users have noticed that their boot time has increased significantly in comparison with what it used to be. After using
systemd-analyze blame
NetworkManager is being reported as taking an unusually large amount of time to start.
The problem for some users has been due to
/var/log/journal
becoming too large. This may have other impacts on performance, such as for
systemctl status
or
journalctl
. As such the solution is to remove every file within the folder (ideally making a backup of it somewhere, at least temporarily) and then setting a journal file size limit as described in #Journal size limit.
相關資源
- 官方網站
- 維基百科上的介紹
- man 手冊頁
- systemd 優化
- 常見問題 FAQ
- 小技巧
- systemd for Administrators (PDF)
- Fedora 項目對 systemd 的介紹
- 如何調試 systemd 故障
- The H Open 雜志上的兩篇科普文章
- Lennart 的博文
- 更新報告
- 更新報告2
- 更新報告3
- 最新動态
- Fedora Wiki 上的 SysVinit 和 systemd 指令對比表