天天看點

systemd 介紹

systemd 基本工具

檢視和控制systemd的主要指令是

systemctl

。該指令可用于檢視系統狀态和管理系統及服務。詳見

man 1 systemctl

小貼士:

  • 在 

    systemctl

     參數中添加 

    -H <使用者名>@<主機名>

     可以實作對其他機器的遠端控制。該過程使用 SSH連接配接。
  • systemadm

     是 systemd 的官方圖形前端。官方軟體倉庫 提供了穩定版本 systemd-ui,AUR 中的軟體包 systemd-ui-gitAUR[broken link: archived in aur-mirror] 提供了開發版本。

分析系統狀态

輸出激活的單元:

$ 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]

): 這意味着它是模闆單元 

[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=

 參數進行設定。

  • Type=simple

    (預設值):systemd認為該服務将立即啟動。服務程序不會fork。如果該服務要啟動其他服務,不要使用此類型啟動,除非該服務是socket激活型。
  • Type=forking

    :systemd認為當該服務程序fork,且父程序退出後服務啟動成功。對于正常的守護程序(daemon),除非你确定此啟動方式無法滿足需求,使用此類型啟動即可。使用此啟動類型應同時指定 

    PIDFile=

    ,以便systemd能夠跟蹤服務的主程序。
  • Type=oneshot

    :這一選項适用于隻執行一項任務、随後立即退出的服務。可能需要同時設定 

    RemainAfterExit=yes

     使得 systemd 在服務程序退出之後仍然認為服務處于激活狀态。
  • Type=notify

    :與 

    Type=simple

     相同,但約定服務會在就緒後向 systemd 發送一個信号。這一通知的實作由 

    libsystemd-daemon.so

     提供。
  • Type=dbus

    :若以此方式啟動,當指定的 

    BusName

     出現在DBus系統總線上時,systemd認為服務就緒。
  • Type=idle

    systemd

    會等待所有任務(Jobs)處理完成後,才開始執行

    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)。可以通過核心參數更改預設啟動級别:

  • systemd.unit=multi-user.target

     (大緻相當于級别3)
  • systemd.unit=rescue.target

     (大緻相當于級别1)

另一個方法是修改 

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 指令對比表

繼續閱讀