Centos7特性——systemd

Centos7特性——systemd

理解Linux啓動過程

wKioL1nHbMCyURr4AAOv0Ok9jDc516.jpg-wh_50

在我們打開Linux電腦的電源後第一個啓動的進程就是init。分配給init進程的PID是1。它是系統其他所有進程的父進程。當一臺Linux電腦啓動後,處理器會先在系統存儲中查找BIOS,之後BIOS會檢測系統資源然後找到第一個引導設備,通常爲硬盤,然後會查找硬盤的主引導記錄(MBR),然後加載到內存中並把控制權交給它,以後的啓動過程就由MBR控制。

主引導記錄會初始化引導程序(Linux上有兩個著名的引導程序,GRUB和LILO,80%的Linux系統在用GRUB引導程序),這個時候GRUB或LILO會加載內核模塊。內核會馬上查找/sbin下的“init”程序並執行它。從這裏開始init成爲了Linux系統的父進程。init讀取的第一個文件是/etc/inittab,通過它init會確定我們Linux操作系統的運行級別。它會從文件/etc/fstab裏查找分區表信息然後做相應的掛載。然後init會啓動/etc/init.d裏指定的默認啓動級別的所有服務/腳本。所有服務在這裏通過init一個一個被初始化。在這個過程裏,init每次只啓動一個服務,所有服務/守護進程都在後臺執行並由init來管理。

關機過程差不多是相反的過程,首先init停止所有服務,最後階段會卸載文件系統。

以上提到的啓動過程有一些不足的地方。而用一種更好的方式來替代傳統init的需求已經存在很長時間了。也產生了許多替代方案。其中比較著名的有Upstart,Epoch,Muda和Systemd。而Systemd獲得最多關注並被認爲是目前最佳的方案。

理解Systemd

開發Systemd的主要目的就是減少系統引導時間和計算開銷。Systemd(系統管理守護進程),最開始以GNU GPL協議授權開發,現在已轉爲使用GNU LGPL協議,它是如今討論最熱烈的引導和服務管理程序。如果你的Linux系統配置爲使用Systemd引導程序,它取替傳統的SysV init,啓動過程將交給systemd處理。Systemd的一個核心功能是它同時支持SysV init的後開機啓動腳本。

Systemd引入了並行啓動的概念,它會爲每個需要啓動的守護進程建立一個套接字,這些套接字對於使用它們的進程來說是抽象的,這樣它們可以允許不同守護進程之間進行交互。Systemd會創建新進程併爲每個進程分配一個控制組(cgroup)。處於不同控制組的進程之間可以通過內核來互相通信。systemd處理開機啓動進程的方式非常漂亮,和傳統基於init的系統比起來優化了太多。讓我們看下Systemd的一些核心功能。

·        和init比起來引導過程簡化了很多

·        Systemd支持併發引導過程從而可以更快啓動

·        通過控制組來追蹤進程,而不是PID

·        優化了處理引導過程和服務之間依賴的方式

·        支持系統快照和恢復

·        監控已啓動的服務;也支持重啓已崩潰服務

·        包含了systemd-login模塊用於控制用戶登錄

·        支持加載和卸載組件

·        低內存使用痕跡以及任務調度能力

·        記錄事件的Journald模塊和記錄系統日誌的syslogd模塊

Systemd同時也清晰地處理了系統關機過程。它在/usr/lib/systemd/目錄下有三個腳本,分別叫systemd-halt.service,systemd-poweroff.service,systemd-reboot.service。這幾個腳本會在用戶選擇關機,重啓或待機時執行。在接收到關機事件時,systemd首先卸載所有文件系統並停止所有內存交換設備,斷開存儲設備,之後停止所有剩下的進程。

systemd基本工具

監視和控制systemd的主要命令是systemctl。該命令可用於查看系統狀態和管理系統及服務。詳見man1 systemctl

注意:

§  在 systemctl 參數中添加 -H <用戶名>@<主機名> 可以實現對其他機器的遠程控制。該過程使用ssh鏈接。

§  systemadm是systemd 的官方圖形前端

分析系統狀態

輸出激活的單元:

1.    $ systemctl

以下命令等效:

1.    $ systemctl list-units

輸出運行失敗的單元:

1.    $ systemctl --failed

所有可用的單元文件存放在 /usr/lib/systemd/system/ 和/etc/systemd/system/ 目錄(後者優先級更高)。查看所有已安裝服務:

1.    $ systemctl list-unit-files

使用單元unit

一個單元配置文件可以描述如下內容之一:系統服務(.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。

注: 有一些單元的名稱包含一個 @ 標記, (e.g. [email protected]): 這意味着它是模板單元[email protected] 的一個 實例。 string 被稱作實例標識符, 在 systemctl 調用模板單元時,會將其當作一個參數傳給模板單元,模板單元會使用這個傳入的參數代替模板中的 %I 指示符。 在實例化之前,systemd 會先檢查 [email protected] 文件是否存在(如果存在,應該就是直接使用這個文件,而不是模板實例化了)。大多數情況下,包換 @ 標記都意味着這個文件是模板。如果一個模板單元沒有實例化就調用,該調用會返回失敗,因爲模板單元中的 %I 指示符沒有被替換。

立即激活單元:

1.    # systemctl start <單元>

立即停止單元:

1.    # systemctl stop <單元>

重啓單元:

1.    # systemctl restart <單元>

命令單元重新讀取配置:

1.    # systemctl reload <單元>

輸出單元運行狀態:

1.    $ systemctl status <單元>

檢查單元是否配置爲自動啓動:

1.    $ systemctl is-enabled <單元>

開機自動激活單元:

1.    # systemctl enable <單元>

注意: 如果服務沒有Install段落,一般意味着應該通過其它服務自動調用它們。如果真的需要手動安裝,可以直接連接服務,如下(將foo替換爲真實的服務名):

1.    # ln -s /usr/lib/systemd/system/foo.service /etc/systemd/system/graphical.target.wants/

取消開機自動激活單元:

1.    # systemctl disable <單元>

顯示單元的手冊頁(必須由單元文件提供):

1.    # systemctl help <單元>

重新載入 systemd,掃描新的或有變動的單元:

1.    # systemctl daemon-reload

電源管理


安裝 polkit 後纔可使用電源管理。

如果你正登錄在一個本地的systemd-logind用戶會話,且當前沒有其它活動的會話,那麼以下命令無需root權限即可執行。否則(例如,當前有另一個用戶登錄在某個tty),systemd 將會自動請求輸入root密碼。

重啓:

1.    $ systemctl reboot

退出系統並停止電源:

1.    $ systemctl poweroff

待機:

1.    $ systemctl suspend

休眠:

1.    $ systemctl hibernate

混合休眠模式(同時休眠到硬盤並待機):

1.    $ systemctl hybrid-sleep

編寫單元文件

systemd單元文件的語法來源於 XDG桌面入口配置文件.desktop文件,最初的源頭則是Microsoft Windows的.ini文件。單元文件可以從兩個地方加載,優先級從低到高分別是:

§  /usr/lib/systemd/system/: 軟件包安裝的單元

§  /etc/systemd/system/: 系統管理員安裝的單元

注意: 當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=<新依賴>

其它舉例,

/etc/systemd/system/unit.d/customexec.conf

 

[Service]

ExecStartExecStart=

ExecStart=new command

想知道爲什麼修改 ExecStart 前必須將其置空

下面是自動重啓服務的一個例子:

/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/ 中自定義的單元文件不會同步更新。此外,你還得執行 systemctlreenable <unit>,手動重新啓用該單元。因此,建議使用前面一種利用 *.conf 的方法。

小貼士: 用 systemd-delta 命令來查看哪些單元文件被覆蓋、哪些被修改。系統維護的時候需要及時瞭解哪些單元已經有了更新

單元配置文件的 vim 語法高亮支持

可從官方倉庫安裝 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 目標

註釋

0

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)。可以通過內核參數更改默認啓動級別:

小貼士: 可以省略擴展名 .target。

 systemd.unit=multi-user.target (大致相當於級別3)

 systemd.unit=rescue.target (大致相當於級別1)

另一個方法是修改 default.target

可以通過systemctl 修改它:

# systemctl enable multi-user.target

命令執行情況由 systemctl 顯示:鏈接/etc/systemd/system/default.target 被創建,指向新的默認啓動級別。該方法當且僅當目標配置文件中有以下內容時有效:

[Install]

Alias=default.target

目前,multi-user.target、graphical.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

詳情參見 man 5 tmpfiles.d。

注意: 該方法不能向 /sys 中的配置文件添加參數,因爲 systemd-tmpfiles-setup 有可能在相關模塊加載前運行。這種情況下,需要首先通過 modinfo <模塊名> 確認需要的參數,並在 /etc/modprobe.d 下的一個文件中設置改參數。另外,還可以使用 udev 規則,在設備就緒時設置相應屬性。

定時器

定時器是以 .timer 爲後綴的配置文件,記錄由system的裏面由時間觸發的動作, 定時器可以替代 cron 的大部分功能。

日誌

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 文件系統,應該考慮對這個目錄禁用寫入時複製

過濾輸出


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-3018: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 已經包含了需要的配置。

設置開機啓動 syslog-ng:

# systemctl enable syslog-ng

這裏有一份很不錯的journalctl指南。

Forward journald to /dev/tty12

In /etc/systemd/journald.conf enable the following:

1.      ForwardToConsole=yes

2.      TTYPath=/dev/tty12

3.      MaxLevelConsole=info

重啓journald:

1.      # systemctl restart systemd-journald

特殊問題

關機/重啓十分緩慢

如果關機特別慢(甚至跟死機了一樣),很可能是某個拒不退出的服務在作怪。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。

診斷啓動問題


使用如下內核參數引導: systemd.log_level=debug systemd.log_target=kmsg log_buf_len=1M

禁止在程序崩潰時轉儲內存


要使用老的內核轉儲,創建下面文件:

1.      /etc/sysctl.d/49-coredump.conf

2.       

3.      kernel.core_pattern = core

4.      kernel.core_uses_pid = 0

然後運行:

1.      # /usr/lib/systemd/systemd-sysctl

同樣可能需要執行"unlimit"設置文件大小:

1.      $ ulimit -c unlimited

 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章