Linux Systemd 詳細介紹: Unit、Unit File、Systemctl、Target
阿新 • • 發佈:2020-06-23
## Systemd
### 簡介
CentOS 7 使用 Systemd 替換了SysV
Ubuntu 從 15.04 開始使用 Systemd
Systemd 是 Linux 系統工具,用來啟動守護程序,已成為大多數發行版的標準配置
#### 特點
優點:
1. 按需啟動程序,減少系統資源消耗
2. 並行啟動程序,提高系統啟動速度
在 SysV-init 時代,將每個服務專案編號,依次執行啟動指令碼。Ubuntu 的 Upstart 解決了沒有直接依賴的啟動之間的並行啟動。而 Systemd 通過 Socket 快取、DBus 快取和建立臨時掛載點等方法進一步解決了啟動程序之間的依賴,做到了**所有系統服務併發啟動**。對於使用者自定義的服務,Systemd 允許配置其啟動依賴專案,從而確保服務按必要的順序執行。
> SystemV Upstart 參考上一篇博文:[Linux 初始化系統 SystemV Upstart](https://www.cnblogs.com/usmile/p/13065566.html)
3. 使用 CGroup 監視和管理程序的生命週期
CGroup 提供了類似檔案系統的介面,當程序建立子程序時,子程序會繼承父程序的 CGroup。因此無論服務如何啟動新的子程序,所有的這些相關程序都會屬於同一個 CGroup
在 Systemd 之前的主流應用管理服務都是使用 程序樹 來跟蹤應用的繼承關係的,而程序的父子關係很容易通過 兩次 fork 的方法脫離。
而 Systemd 則提供通過 CGroup 跟蹤程序關係,引補了這個缺漏。通過 CGroup 不僅能夠實現服務之間訪問隔離,限制特定應用程式對系統資源的訪問配額,還能更精確地管理服務的生命週期
4. 統一管理服務日誌
5. 支援快照和系統恢復
缺點:
1. 過於複雜,與作業系統的其他部分強耦合,違反"keep simple, keep stupid"的[Unix 哲學](http://www.ruanyifeng.com/blog/2009/06/unix_philosophy.html)
#### 架構圖
![](https://img2020.cnblogs.com/blog/1418536/202006/1418536-20200608160218449-876445211.png)
### Unit(單元|服務)
Systemd 可以管理所有系統資源:
1. 將系統資源劃分為12類
2. 將每個系統資源稱為一個 Unit。Unit 是 Systemd 管理系統資源的基本單位
3. 使用一個 Unit File 作為 Unit 的單元檔案,Systemd 通過單元檔案控制 Unit 的啟動
> 例如,MySQL服務被 Systemd 視為一個 Unit,使用一個 mysql.service 作為啟動配置檔案
### Unit File(單元檔案|配置檔案)
單元檔案中包含該單元的描述、屬性、啟動命令等
#### 型別
Systemd 將系統資源劃分為12類,對應12種類型的單元檔案
| 系統資源型別 | 單元副檔名 | 單元檔案描述 |
| ------------ | -------------- | ------------------------------------------------------------ |
| Service | .service | 封裝**守護程序**的啟動、停止、重啟和過載操作,是最常見的一種 Unit 檔案 |
| Target | .target | 定義 target 資訊及依賴關係,一般僅包含 Unit 段 |
| Device | .device | 對於 `/dev` 目錄下的硬體裝置,主要用於定義裝置之間的依賴關係 |
| Mount | .mount | 定義檔案系統的掛載點,可以替代過去的 `/etc/fstab` 配置檔案 |
| Automount | .automount | 用於控制自動掛載檔案系統,相當於 SysV-init 的 autofs 服務 |
| Path | .path | 用於監控指定目錄或檔案的變化,並觸發其它 Unit 執行 |
| Scope | .scope | 這種 Unit 檔案不是使用者建立的,而是 Systemd 執行時產生的,描述一些系統服務的分組資訊 |
| Slice | .slice | 用於表示一個 CGroup 的樹 |
| Snapshot | .snapshot | 用於表示一個由 systemctl snapshot 命令建立的 Systemd Units 執行狀態快照,可以切回某個快照 |
| Socket | .socket | 監控來自於系統或網路的資料訊息 |
| Swap | .swap | 定義一個使用者做虛擬記憶體的交換分割槽 |
| Timer | .timer | 用於配置在特定時間觸發的任務,替代了 Crontab 的功能 |
> 對於操作單元檔案的命令,如果預設副檔名,則預設`.service`副檔名
>
> 而操作 target 的命令,例如 isolate,則預設`.target`副檔名
#### 語法
單元檔案的語法來源於 XDG桌面入口配置檔案`.desktop`檔案
Unit 檔案可以分為三個配置區段:
- Unit 段:所有 Unit 檔案通用,用來定義 Unit 的元資料,以及配置與其他 Unit 的關係
- Install 段:所有 Unit 檔案通用,用來定義如何啟動,以及是否開機啟動
- Service 段:服務(Service)型別的 Unit 檔案(字尾為 .service)特有的,用於定義服務的具體管理和執行動作
> 單元檔案中的區段名和欄位名大小寫敏感
>
> 每個區段內都是一些等號連線的鍵值對(鍵值對的等號兩側不能有空格)
###### Unit 段
主要欄位如下:
- `Description`:當前服務的簡單描述
- `Documentation`:文件地址,可以是一個或多個文件的 URL 路徑
【依賴關係】
- `Requires`:與其它 Unit 的強依賴關係,如果其中任意一個 Unit 啟動失敗或異常退出,當前 Unit 也會被退出
- `Wants`:與其它 Unit 的弱依賴關係,如果其中任意一個 Unit 啟動失敗或異常退出,不影響當前 Unit 繼續執行
> 只涉及依賴關係,預設情況下 兩個 Unit 同時啟動
【啟動順序】
- `After`:該欄位指定的 Unit 全部啟動完成以後,才會啟動當前 Unit
- `Before`:該欄位指定的 Unit 必須在當前 Unit 啟動完成之後再啟動
> 只涉及啟動順序,不影響啟動結果和執行情況
- `Binds To`:與 Requires 相似,該欄位指定的 Unit 如果退出,會導致當前 Unit 停止執行
- `Part Of`:一個 Bind To 作用的子集,僅在列出的 Unit 失敗或重啟時,終止或重啟當前 Unit,而不會隨列出Unit 的啟動而啟動
> http://manpages.ubuntu.com/manpages/bionic/en/man5/systemd.unit.5.html
###### Install 段
主要欄位如下:
- `WantedBy`:它的值是一個或多個 target,執行enable命令時,符號連結會放入`/etc/systemd/system`目錄下以 target 名 + `.wants`字尾構成的子目錄中
- `RequiredBy`:它的值是一個或多個 target,執行enable命令時,符號連結會放入`/etc/systemd/system`目錄下以 target 名 + `.required`字尾構成的子目錄中
- `Alias`:當前 Unit 可用於啟動的別名
- `Also`:當前 Unit 被 enable/disable 時,會被同時操作的其他 Unit
> http://manpages.ubuntu.com/manpages/bionic/en/man5/systemd.unit.5.html
###### Service 段
主要欄位如下:
【啟動型別】
- `Type`:定義啟動時的程序行為。它有以下幾種值。
- `Type=simple`:預設值,`ExecStart`欄位啟動的程序為主程序
- 服務程序不會 fork,如果該服務要啟動其他服務,不要使用此型別啟動,除非該服務是 socket 啟用型
- `Type=forking`:`ExecStart`欄位將以`fork()`方式從父程序建立子程序啟動,建立後父程序會立即退出,子程序成為主程序。
- 通常需要指定`PIDFile`欄位,以便 Systemd 能夠跟蹤服務的主程序
- 對於常規的守護程序(daemon),除非你確定此啟動方式無法滿足需求,使用此型別啟動即可
- `Type=oneshot`:只執行一次,Systemd 會等當前服務退出,再繼續往下執行
- 適用於只執行一項任務、隨後立即退出的服務
- 通常需要指定`RemainAfterExit=yes`欄位,使得 Systemd 在服務程序退出之後仍然認為服務處於啟用狀態
- `Type=dbus`:當前服務通過 D-Bus 訊號啟動。當指定的 BusName 出現在 DBus 系統總線上時,Systemd認為服務就緒
- `Type=notify`:當前服務啟動完畢會發出通知訊號,通知 Systemd,然後 Systemd 再啟動其他服務
- `Type=idle`:Systemd 會等到其他任務都執行完,才會啟動該服務。
- 一種使用場合是:讓該服務的輸出,不與其他服務的輸出相混合
【啟動行為】
- `ExecStart`:啟動當前服務的命令
> ```bash
> ExecStart=/bin/echo execstart1
> ExecStart=
> ExecStart=/bin/echo execstart2
> ```
>
> 順序執行設定的命令,把欄位置空,表示清除之前的值
- `ExecStartPre`:啟動當前服務之前執行的命令
- `ExecStartPost`:啟動當前服務之後執行的命令
- `ExecReload`:重啟當前服務時執行的命令
- `ExecStop`:停止當前服務時執行的命令
- `ExecStopPost`:停止當前服務之後執行的命令
- `RemainAfterExit`:當前服務的所有程序都退出的時候,Systemd 仍認為該服務是啟用狀態
- 這個配置主要是提供給一些並非常駐記憶體,而是啟動註冊後立即退出,然後等待訊息按需啟動的特殊型別服務使用的
- `TimeoutSec`:定義 Systemd 停止當前服務之前等待的秒數
> 注:所有的啟動設定之前,都可以加上一個連詞號(`-`),表示"抑制錯誤",即發生錯誤的時候,不影響其他命令的執行。比如,`EnvironmentFile=-/etc/sysconfig/sshd`(注意等號後面的那個連詞號),就表示即使`/etc/sysconfig/sshd`檔案不存在,也不會丟擲錯誤。
【重啟行為】
- `RestartSec`:Systemd 重啟當前服務間隔的秒數
- `KillMode`:定義 Systemd 如何停止服務,可能的值包括:
- control-group(預設值):當前控制組裡面的所有子程序,都會被殺掉
- process:只殺主程序(sshd 服務,推薦值)
- mixed:主程序將收到 SIGTERM 訊號,子程序收到 SIGKILL 訊號
- none:沒有程序會被殺掉,只是執行服務的 stop 命令。
- `Restart`:定義何種情況 Systemd 會自動重啟當前服務,可能的值包括:
- no(預設值):退出後不會重啟
- on-success:只有正常退出時(退出狀態碼為0),才會重啟
- on-failure:非正常退出時(退出狀態碼非0),包括被訊號終止和超時,才會重啟(守護程序,推薦值)
- on-abnormal:只有被訊號終止和超時,才會重啟(對於允許發生錯誤退出的服務,推薦值)
- on-abort:只有在收到沒有捕捉到的訊號終止時,才會重啟
- on-watchdog:超時退出,才會重啟
- always:不管是什麼退出原因,總是重啟
【上下文】
- `PIDFile`:指向當前服務 PID file 的絕對路徑。
- `User`:指定執行服務的使用者
- `Group`:指定執行服務的使用者組
- `EnvironmentFile`:指定當前服務的環境引數檔案。該檔案內部的`key=value`鍵值對,可以用`$key`的形式,在當前配置檔案中獲取
> 啟動`sshd`,執行的命令是`/usr/sbin/sshd -D $OPTIONS`,其中的變數`$OPTIONS`就來自`EnvironmentFile`欄位指定的環境引數檔案。
> http://manpages.ubuntu.com/manpages/bionic/en/man5/systemd.service.5.html
#### 佔位符
在 Unit 檔案中,有時會需要使用到一些與執行環境有關的資訊,例如節點 ID、執行服務的使用者等。這些資訊可以使用佔位符來表示,然後在實際執行中動態地替換為實際的值。
> 詳細瞭解見 https://cloud.tencent.com/developer/article/1516125
#### 模板
在現實中,往往有一些應用需要被複制多份執行,就會用到模板檔案
模板檔案的寫法與普通單元檔案基本相同,只是模板檔名是以 @ 符號結尾。例如:[email protected]
通過模板檔案啟動服務例項時,需要在其檔名的 @ 字元後面附加一個用於區分服務例項的引數字串,通常這個引數是用於監控的埠號或控制檯 TTY 編譯號
```
systemctl start [email protected]
```
Systemd 在執行服務時,首先尋找跟單元名完全匹配的單元檔案,如果沒有找到,才會嘗試選擇匹配模板
例如上面的命令,System 首先會在約定的目錄下尋找名為 [email protected] 的單元檔案,如果沒有找到,而檔名中包含 @ 字元,它就會嘗試去掉字尾引數匹配模板檔案。對於 [email protected],Systemd 會找到 [email protected] 模板檔案,並通過這個模板檔案將服務例項化。
> 詳細瞭解見 https://cloud.tencent.com/developer/article/1516125
#### 狀態
`systemctl list-unit-files` 將會列出檔案的 state,包括 static, enabled, disabled, masked, indirect
- masked
service軟連結到`/dev/null`
該單元檔案被禁止建立啟動連結
- static
該單元檔案沒有`[Install]`部分(無法執行),只能作為其他配置檔案的依賴
- enabled
已建立啟動連結
- disabled
沒建立啟動連結
> https://askubuntu.com/a/731674
#### 示例
1. 關掉觸控板配置檔案
```bash
Unit]
Description=Switch-off Touchpad
[Service]
Type=oneshot
ExecStart=/usr/bin/touchpad-off start
ExecStop=/usr/bin/touchpad-off stop
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
```
- oneshot 表明這個服務只要執行一次就夠了,不需要長期執行
- `RemainAfterExit`欄位設為`yes`,表示程序退出以後,服務仍然保持執行。這樣的話,一旦使用`systemctl stop`命令停止服務,`ExecStop`指定的命令就會執行,從而重新開啟觸控板
### Systemd 內建命令
###### `systemd-analyze`
Analyze and debug system manager, If no command is passed, **Systemd-analyze time** is implied
> https://www.freedesktop.org/software/systemd/man/systemd-analyze.html
###### `systemd-analyze time`
檢視初始化耗時
###### ` systemd-analyze blame`
列印所有執行單元,按它們初始化的時間排序。此資訊可用於優化啟動時間。注意,輸出可能具有誤導性,因為一個服務的初始化可能非常緩慢,因為它等待另一個服務的初始化完成
`systemd-run`
將一個指定的服務變成後臺服務
> 未測試
>
> 參考 https://www.freedesktop.org/software/systemd/man/systemd-run.html
### systemctl 系統服務管理命令
`systemctl`是 Systemd 的主命令,用於管理系統
#### 與 service 命令的區別
1. systemctl 融合了 service 和 chkconfig 的功能
2. 在 Ubuntu18.04 中沒有自帶 chkconfig 命令;service 命令實際上重定向到 systemctl 命令
| 動作 | SysV Init 指令 | Systemd 指令 |
| -------------------- | ------------------------------- | ----------------------------------------- |
| 啟動某服務 | service httpd start | systemctl start httpd |
| 停止某服務 | service httpd stop | systemctl stop httpd |
| 重啟某服務 | service httpd restart | systemctl restart httpd |
| 檢查服務狀態 | service httpd status | systemctl status httpd |
| 刪除某服務 | chkconfig --del httpd | 停掉應用,刪除其配置檔案 |
| 使服務開機自啟動 | chkconfig --level 5 httpd on | systemctl enable httpd |
| 使服務開機不自啟動 | chkconfig --level 5 httpd off | systemctl disable httpd |
| 查詢服務是否開機自啟 | chkconfig --list \| grep httpd | systemctl is-enabled httpd |
| 加入自定義服務 | chkconfig --add test | systemctl load test |
| 顯示所有已啟動的服務 | chkconfig --list | systemctl list-unit-files \| grep enabled |
#### 引數
###### `--all`
顯示載入到記憶體的所有單元
###### `--type`
`-t` `--type=`
顯示指定型別(12種類型)的單元
###### `--state`
`--state=`
顯示指定狀態的單元或單元檔案
> - 單元狀態
>
> 輸入 `systemctl list-units --state` 按 `Tab`鍵,顯示所有可用的值
>
> - 單元檔案狀態
>
> 另外還可以用 enabled static disabled 等`systemctl list-unit-files` 顯示的狀態
###### `--failed`
`--state=failed`
顯示載入失敗的單元
```
systemctl --failed
```
###### `--version`
列印 Systemd 版本
```bash
lfp@legion:/lib/systemd/system$ systemctl --version
Systemd 237
+PAM +AUDIT +SELINUX +IMA +APPARMOR +SMACK +SYSVINIT +UTMP +LIBCRYPTSETUP +GCRYPT +GNUTLS +ACL +XZ +LZ4 +SECCOMP +BLKID +ELFUTILS +KMOD -IDN2 +IDN -PCRE2 default-hierarchy=hybrid
```
#### 單元命令
> 我的理解
>
> - systemd 對單元的管理,不涉及單元檔案自身屬性和內容
>
###### `list-units`
相當於`systemctl`
列出當前已載入的單元(記憶體)
預設情況下僅顯示處於啟用狀態(正在執行)的單元
> UNIT 單元名
>
> LOAD 載入狀態
>
> ACTIVE SUB 執行狀態(大狀態 子狀態)
>
> DESCRIPTION 描述
###### `start`
啟動單元
```
systemctl start mysql.service
```
###### `stop`
停止單元
```
systemctl stop mysql.service
```
###### `kill`
殺掉單元程序
```
systemctl kill mysql.service
```
###### `reload`
不終止單元,重新載入 針對該單元的 執行配置檔案,而不是 針對 systemd的 該單元的啟動配置檔案
> 例如啟動 MySQL 服務,reload 可以在不停止服務的情況下過載 MySQL 的配置檔案 `my.cnf`
###### `restart`
重啟單元
該單元在重啟之前擁有的資源不會被完全清空,比如檔案描述符儲存設施
```
systemctl reload mysql.service
```
###### `status`
`status [unit | PID]`
顯示單元或程序所屬單元的執行資訊
```
systemctl status mysql.service
```
> `Loaded`行:配置檔案的位置,是否設為開機啟動
>
> `Active`行:表示正在執行
>
> `Main PID`行:主程序ID
>
> `CGroup`塊:應用的所有子程序
>
> 日誌塊:應用的日誌
###### `is-active`
判斷指定的單元是否處於啟用狀態
```bash
# 預設會列印當前單元的狀態,可以通過 --quiet 引數取消列印
lfp@legion:~$ systemctl is-active mysql
active
```
###### `is-failed`
判斷指定的單元是否處於啟動失敗狀態
```bash
lfp@legion:~$ systemctl is-failed mysql
active
```
###### `list-dependencies`
檢視單元之間的依賴關係
```
systemctl list-dependencies graphical.target
systemctl list-dependencies mysql.service
```
###### `show`
`show --property= Unit` <==> `show -p Unit`
顯示單元所有底層引數
```bash
lfp@legion:~$ systemctl show -p MainPID mysql
MainPID=1061
```
###### `set-property`
在單元啟動的時候設定執行時的某個屬性,立即生效,並儲存在磁碟中作為啟動配置
如果添加了`--runtime`則重啟後失效
並非所有的屬性都可以設定,只是 [systemd.resource-control](http://manpages.ubuntu.com/manpages/bionic/en/man5/systemd.resource-control.5.html) 包含的屬性
###### `isolate`
切換到某個 target(系統狀態),立即停止該 target 未包含的單元程序。也可以理解為切換 runlevel
如果沒有指定副檔名,則預設`.target`
只有當.target單元檔案中的`AllowIsolate=yes`時,才能使用 isolate 切換;也可以用`IgnoreOnIsolate=yes`欄位來拒絕使用 isolate 切換
```bash
systemctl isolate multi-user.target
```
###### `cat`
顯示單元配置檔案的備份檔案,包括插入式配置`drop-ins`,可以完整的看到單元服務的配置。注意這裡列印的依據是磁碟的上內容,如果使用者修改了配置檔案(磁碟已修改)但是未執行`daemon-reload`命令(記憶體中未更新),那麼該命令顯示的配置和實際執行情況有出入
```bash
lfp@legion:~$ systemctl cat mysql.service
# /lib/systemd/system/mysql.service
# MySQL Systemd service file
[Unit]
Description=MySQL Community Server
...
[Install]
WantedBy=multi-user.target
[Service]
Type=forking
...
# 這段就顯示的是 插入式配置 drop-in 的內容
# /etc/systemd/system/mysql.service.d/mysql.conf
# MySQL Systemd service file
[Unit]
# Description=MySQL Community Server conf
[Service]
# ExecStartPost=/home/lfp/bin/espeak.sh
```
#### 單元檔案命令
> 我的理解
>
> - systemd 對單元檔案自身屬性和內容的管理
###### `list-unit-files`
列出所有已安裝的單元檔案和它們的啟用狀態
> 和`list-units`的區別是
>
> - list-units 僅顯示當前已載入到記憶體中的單元
> - list-unit-files 會讀取單元檔案內容,列出所有單元,包括存在於硬碟未載入進記憶體的單元
>
> 實際測試結果:
>
> systemctl list-unit-files 顯示“348 Unit files listed”
>
> systemctl list-units --all 顯示“405 loaded units listed”
>
> systemctl list-units 顯示 “232 loaded units listed”
###### `enable`
使某個單元開機自啟動
這會根據**單元檔案內容**中的`[Install]`指定的 target 組,建立一個軟連結
> ```bash
> lfp@legion:/etc/systemd/system$ vim ***L.service
> # 檔案內容 [Install] 段
> ......
> [Install]
> WantedBy=multi-user.target
> ......
>
> lfp@legion:/etc/systemd/system$ systemctl is-enabled ***L.service
> disabled
> lfp@legion:/etc/systemd/system$ systemctl enable ***L.service
> # 根據 [Install] 段指定的組,新增軟連結
> Created symlink /etc/systemd/system/multi-user.target.wants/***L.service → /etc/systemd/system/***L.service.
> lfp@legion:/etc/systemd/system$ systemctl is-enabled ***L.service
> enabled
> ```
###### `disable`
取消某個單元開機自啟動設定,刪除軟連結
這會刪除**所有**指向該單元檔案的軟連結,不僅僅是 enable 操作建立的
###### `reenable`
disable 和 enable 的結合,根據單元檔案內容中的 [Install] 段,重置軟連結
###### `is-enabled`
檢查某個單元是否是開機自啟動的(建立的啟動連結)
```bash
lfp@legion:~$ systemctl is-enabled mysql
enabled
```
###### `get-default`
獲取預設啟動 target,default-target 是指向該 target 的軟連結
###### `set-default`
設定預設啟動 target,同時修改 default-target 指向設定的 target
```
systemctl set-default multi-user.target
```
#### 生命週期管理命令
###### `daemon-reload`
重新**載入**所有的單元檔案和依賴關係
對單元檔案有修改的時候,需要執行該命令重新載入檔案內容
#### 系統管理命令
###### `reboot`
```
systemctl reboot
```
重啟系統(非同步操作)
> it will return after the reboot operation is enqueued, without waiting for it to complete
###### `poweroff`
關閉系統,切斷電源(非同步操作)
###### `halt`
僅CPU停止工作,其他硬體仍處於開機狀態(非同步操作)
###### `suspend`
暫停系統(非同步操作)
將觸發執行`suspend.target`
###### `hibernate`
讓系統進入冬眠狀態(非同步操作)
將觸發執行`hibernate.target`
### 目錄、檔案
`/run/systemd/system/`
單元(服務)執行時生成的配置檔案所在目錄
`/etc/systemd/system/`
系統或使用者自定義的配置檔案,**初始化過程中**`Systemd`只執行`/etc/systemd/system`目錄裡面的配置檔案
`/lib/systemd/system/ `
軟體安裝時新增的配置檔案,類似於` /etc/init.d/`
對於支援 Systemd 的程式,安裝的時候,會自動的在 `/lib/systemd/system` 目錄新增一個配置檔案
> 其他目錄都是軟連結
`/etc/systemd/system/default.target`
Systemd 執行的第一個單元檔案,符號連結到預設啟動 target 對應的 `.target` 單元檔案
#### 優先順序
SysV 的啟動指令碼放在`/etc/init.d`目錄下
Systemd 的單元檔案放在`/etc/systemd/system` 和 `/lib/systemd/system`目錄下
當一個程式在3個目錄下都存在啟動方式時,優先順序是`/etc/systemd/system --> /lib/systemd/system --> /etc/init.d`
```bash
lfp@legion:/etc/init.d$ ll
-rwxr-xr-x 1 root root 5650 5月 19 22:09 mysql*
lfp@legion:/etc/systemd/system$ ll
-rw-r--r-- 1 root root 511 5月 20 01:42 mysql.service
lfp@legion:/lib/systemd/system$ ll
-rw-r--r-- 1 root root 499 5月 20 01:20 mysql.service
```
- `/etc/systemd/system` 裡面的同名service會覆蓋`/lib/systemd/system` 裡面的
> 注意檢視檔案資訊,該同名檔案不能是指向 /lib/systemd/system 的軟連結
>
> 軟連結不會覆蓋而會同步
- 如果某個程式不存在Systemd 單元檔案,那麼會執行`/etc/init.d`裡面的啟動指令碼
> 根據啟動過程, `/etc/systemd/system/multi-user.target.wants/` 目錄下是很多指向` /lib/systemd/system/`目錄的**軟連結**,所以兩個目錄下的單元檔案會互相同步。
>
> 如果`/etc/systemd/system/` 和 `/etc/systemd/system/multi-user.target.wants/` 同時存在單元檔案,測試發現,不管是手動啟動還是開機自啟動,使用的都是 `/etc/systemd/system/` 目錄下的service單元檔案
##### 測試
###### 執行`/etc/init.d`目錄下的指令碼
mysql 修改 `mysql.service` 為`mysql.service.bak` 然後通過`service mysql restart`啟動`/etc/init.d/mysql`指令碼
下面是啟動後的一些資訊
![](https://img2020.cnblogs.com/blog/1418536/202006/1418536-20200608160344455-1619243714.png)
> 注:在恢復`mysql.service`之前,需要先通過`service mysql stop` 利用`/etc/init.d/mysql`指令碼中的`stop`結束上面的程序,否則一旦恢復,`service mysql stop` 執行的操作就不是 `/etc/init.d/mysql`指令碼中的`stop`,無法結束上面的程序,出現命令無法正常執行的情況
結束上面的程序,恢復`mysql.service`,重新啟動
![](https://img2020.cnblogs.com/blog/1418536/202006/1418536-20200608160406468-319771464.png)
###### `/etc/systemd/system` 覆蓋測試
未修改前,檢視MySQL的狀態
```bash
lfp@legion:~$ service mysql status
● mysql.service - MySQL Community Server
# 可以發現這裡的 mysql.service 是在 /lib/systemd/system 下面
Loaded: loaded (/lib/systemd/system/mysql.service; enabled; vendor preset: en
Active: active (running) since Sat 2020-04-25 18:34:30 CST; 5h 33min ago
Main PID: 988 (mysqld)
Tasks: 28 (limit: 4915)
CGroup: /system.slice/mysql.service
└─988 /usr/sbin/mysqld --daemonize --pid-file=/run/mysqld/mysqld.pid
4月 25 18:34:30 legion Systemd[1]: Starting MySQL Community Server...
4月 25 18:34:30 legion Systemd[1]: Started MySQL Community Server.
```
將 /lib/systemd/system 下面的檔案複製到 ` /etc/systemd/system/` 下面
```bash
sudo cp /lib/systemd/system/mysql.service /etc/systemd/system/
```
修改 mysql.service
```
sudo vim /etc/systemd/system/mysql.service
```
![](https://img2020.cnblogs.com/blog/1418536/202006/1418536-20200608160451165-1855890790.png)
重啟 mysql.service ,系統提示需要重新載入
```bash
lfp@legion:~$ systemctl restart mysql.service
Warning: The Unit file, source configuration file or drop-ins of mysql.service changed on disk.
Run 'systemctl daemon-reload' to reload units.
lfp@legion:~$ systemctl daemon-reload # 重新載入
lfp@legion:~$ systemctl restart mysql.service # 重啟
lfp@legion:~$ systemctl status mysql.service
● mysql.service - MySQL Community Server hahahaha # 發現這裡是修改之後的,覆蓋了 /lib/systemd/lib 中的
# 這裡也可以看到載入路徑
Loaded: loaded (/etc/systemd/system/mysql.service; enabled; vendor preset: en
Active: active (running) since Sun 【2020-04-26】 00:47:02 CST; 5s ago
Process: 21590 ExecStart=/usr/sbin/mysqld --daemonize --pid-file=/run/mysqld/m
Process: 21581 ExecStartPre=/usr/share/mysql/mysql-Systemd-start pre (code=exi
Main PID: 21592 (mysqld)
Tasks: 27 (limit: 4915)
CGroup: /system.slice/mysql.service
└─21592 /usr/sbin/mysqld --daemonize --pid-file=/run/mysqld/mysqld.pi
4月 26 00:47:02 legion Systemd[1]: Starting MySQL Community Server hahahaha...
4月 26 00:47:02 legion Systemd[1
```
### Target
兩個含義
1. 系統的某個狀態稱為一個 target(類似於"狀態點")
2. 達到某個系統狀態,所需的一個或多個資源(Unit)稱為一個 target(一個 Unit 組)
> 1. target是一個抽象的系統資源,不像MySQL有實體
>
> 2. 如果一個target只包含一個Unit,那麼該 target,沒有對應的目錄,指的就是這個 Unit
>
> 例如 `hibernate.target `只包含 `systemd-hibernate.service`一個Unit
>
> 如果一個target包含多個Unit,那麼該target,有對應的 xxx.target.wants 目錄,指的是目錄裡面所有的Unit
>
> 例如 `multi-user.target` 包含位於`/etc/systemd/system/multi-user.target.wants`目錄下的多個 Unit
target也是一個 Target 型別的系統資源,有對應的單元檔案 xxx.target
Systemd 使用 target 來劃分和管理資源(Unit),啟動(啟用)某個 xxx.target 單元檔案,通過執行該 target 包含的 Unit,使系統達到某種狀態
> 對於狀態點的理解:
>
> 例如,執行`systemd suspend`命令讓系統暫停,會觸發啟動`suspend.target`,然後執行裡面的`systemd-suspend.service` Unit,使系統達到一個暫停的狀態
傳統的`init`啟動模式裡面,有 RunLevel 的概念,跟 Target 的作用很類似。不同的是,RunLevel 是互斥的,不可能多個 RunLevel 同時啟動,但是多個 Target 可以同時啟動
#### 啟動 target
runlevel是 SysV init 初始化系統中的概念,在Systemd初始化系統中使用的是 Target,他們之間的對映關係是
| Runlevel | Target | 說明 |
| -------- | ----------------- | ---------------------------------- |
| 0 | poweroff.target | 關閉系統 |
| 1 | rescue.target | 維護模式 |
| 2,3,4 | multi-user.target | 多使用者,無圖形系統(命令列介面) |
| 5 | graphical.target | 多使用者,圖形化系統(圖形使用者介面) |
| 6 | reboot.target | 重啟系統 |
#### 啟動過程
![](https://img2020.cnblogs.com/blog/1418536/202006/1418536-20200608160526927-216960400.png)
1. 讀入 `/boot` 目錄下的核心檔案
2. 核心檔案載入完之後,開始執行第一個程式`/sbin/init` 初始化程序,由 Systemd 初始化系統引導,完成相關的初始化工作
3. Systemd 執行`default.target` ,獲知設定的啟動 target
> 實際上 `default.target` 是指向設定的啟動 target 的軟連結
> ![](https://img2020.cnblogs.com/blog/1418536/202006/1418536-20200608160632771-62990504.png)
4. Systemd 執行啟動 target 對應的單元檔案。根據單元檔案中定義的[依賴關係](https://www.freedesktop.org/software/systemd/man/bootup.html#System%20Manager%20Bootup),傳遞控制權,依次執行其他 target 單元檔案,同時啟動每個 target 包含的單元
> 對於圖形化介面,預設 target 是 graphical,Systemd 執行位於`/lib/systemd/system/` 目錄下的 graphical.target 單元檔案,根據 target 單元檔案中定義的依賴關係,依次啟動其他 target 單元檔案以及各個 target 包含的位於`/etc/systemd/system/`目錄下的單元
>
> 例如: graphical.target 的依賴關係是
>
> ```bash
> [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
> ```
>
> 因此,依次啟動 multi-user.target --> basic.target --> sysinit.target --> local-fs.target -->local-fs-pre.target --> ...
>
> 同時啟動每個 target 包含的位於`/etc/systemd/system/`目錄下的Unit
>
> **SysV對應的 rc5.d --> /etc/init.d 目錄下的指定的指令碼就不會在開機的時候執行了**
#### 檢視預設 target
`systemctl get-default`
```bash
lfp@legion:~$ runlevel
N 5
lfp@legion:~$ systemctl get-default
graphical.target
```
#### 修改預設 target
`systemctl set-default [xxx.target]`
```bash
# Ubuntu18.04
# 圖形使用者介面 切換 命令列介面
sudo systemctl set-default multi-user.target
# 命令列介面 切換 圖形使用者介面
systemctl set-default graphical.target
reboot
# 命令列介面 想進入 圖形使用者介面(僅進入一次,重啟系統後仍然會進入命令列模式)
sudo systemctl start lightdm
```
> https://ask.csdn.net/questions/695344
>
> https://askubuntu.com/a/788465
### 其他操作
#### 修改配置檔案
1. 直接修改`/lib/systemd/system`目錄下的單元檔案
> 如果軟體包更新,修改會被丟棄
2. 將`/lib/systemd/system`中的單元檔案複製到`/etc/systemd/system/`
> 如果軟體包更新,不會同步更新
3. 在`/etc/systemd/system/` 中新增配置(推薦)
###### 新增配置
步驟:
1. 在`/etc/systemd/system/` 目錄下新建`<單元名>.d`目錄
2. 在`<單元名>.d`目錄下,新建`<單元名>.conf`檔案
3. 在`<單元名>.conf`檔案中修改配置
測試:
1. 建立目錄及檔案
```bash
# /mysql.service.d
lfp@legion:/etc/systemd/system/mysql.service.d$ ls
mysql.conf
```
2. 修改配置
```bash
# MySQL Systemd service config file
# 不需要所有的組,僅新增需要修改的組及選項
[Unit]
Description=MySQL Community Server config test
[Service]
ExecStartPost=/home/lfp/bin/espeak.sh
```
3. 重啟測試
```bash
lfp@legion:/etc/systemd/system/mysql.service.d$ systemctl daemon-reload
lfp@legion:/etc/systemd/system/mysql.service.d$ systemctl restart mysql.service
lfp@legion:/etc/systemd/system/mysql.service.d$ systemctl status mysql.service
lfp@legion:/etc/systemd/system/mysql.service.d$ systemctl status mysql.service
# 描述已經被修改
● mysql.service - MySQL Community Server config test
Loaded: loaded (/lib/systemd/system/mysql.service; enabled; vendor preset: enabled)
# 加入了配置檔案
Drop-In: /etc/systemd/system/mysql.service.d
└─mysql.conf
Active: active (running) since Thu 2020-05-21 20:18:02 CST; 12min ago
# 新增的動作執行成功
Process: 4703 ExecStartPost=/home/lfp/bin/espeak.sh (code=exited, status=0/SUCCESS)
Process: 4672 ExecStart=/usr/sbin/mysqld --daemonize --pid-file=/run/mysqld/mysqld.pid (code=exited, status=0/SUCCESS)
Process: 4663 ExecStartPre=/usr/share/mysql/mysql-Systemd-start pre (code=exited, status=0/SUCCESS)
Main PID: 4674 (mysqld)
Tasks: 27 (limit: 4915)
CGroup: /system.slice/mysql.service
└─4674 /usr/sbin/mysqld --daemonize --pid-file=/run/mysqld/mysqld.pid
5月 21 20:18:02 legion espeak.sh[4703]: ALSA lib pcm_route.c:867:(find_matching_chmap) Found no matching channel map
```
#### 新增開機啟動服務
1. 新增啟動配置檔案
2. 通過 `rc.local`檔案
`/lib/systemd/rc.local.service`
```bash
# 如果存在,就自動新增到 multi-user.target
# This Unit gets pulled automatically into multi-user.target by
# Systemd-rc-local-generator if /etc/rc.local is executable.
[Unit]
Description=/etc/rc.local Compatibility
Documentation=man:systemd-rc-local-generator(8)
ConditionFileIsExecutable=/etc/rc.local
After=network.target
[Service]
Type=forking
ExecStart=/etc/rc.local start
TimeoutSec=0
RemainAfterExit=yes
GuessMainPID=no
```
建立 rc.local 檔案,賦予可執行許可權,即可新增啟動命令
```bash
sudo touch /etc/rc.local
chmod 755 /etc/rc.local
lfp@legion:/etc$ vim rc.local
#!/bin/sh -e
echo "test rc.local" > /usr/local/rclocal.log
echo "rc.local `date +%Y-%m-%d-%H:%M:%S`" >/home/lfp/log/rclocal.log
exit 0
```
### system 工具集
#### hostnamectl 主機名管理命令
###### hostnamectl
###### `hostnamectl status`
Show current system hostname and related information
```bash
lfp@legion:/lib/systemd/system$ hostnamectl status
Static hostname: legion
Icon name: computer-laptop
Chassis: laptop
Machine ID: b28xxxxxxxx2ecafa29e
Boot ID: 21xxxxxxxxxxxx1d3a47504d
Operating System: Ubuntu 18.04.4 LTS
Kernel: Linux 5.3.0-51-generic
Architecture: x86-64
```
#### journalctl 日誌管理命令
Systemd 統一管理所有 Unit 的**啟動日誌**。帶來的好處就是,可以只用`journalctl`一個命令,檢視所有日誌(核心日誌和應用日誌)。
配置檔案
`/etc/systemd/journald.conf`
日誌儲存目錄
`/var/log/journal/`
預設日誌最大限制為所在檔案系統容量的 10%,可通過` /etc/systemd/journald.conf` 中的 SystemMaxUse 欄位來指定
> 該目錄是 systemd 軟體包的一部分。若被刪除,systemd 不會自動建立它,直到下次升級軟體包時重建該目錄。如果該目錄缺失,systemd 會將日誌記錄寫入 /run/systemd/journal。這意味著,系統重啟後日志將丟失。
###### `journalctl -u [服務名]`
檢視指定單元的日誌
###### `journalctl -b`
- journalctl -b -0 顯示本次啟動的資訊
- journalctl -b -1 顯示上次啟動的資訊
- journalctl -b -2 顯示上上次啟動的資訊
### 參考
http://manpages.ubuntu.com/manpages/bionic/en/man1/systemctl.1.html
http://manpages.ubuntu.com/manpages/bionic/en/man5/systemd.unit.5.html
https://www.cnblogs.com/yingsong/p/6012180.html
https://www.cnblogs.com/sparkdev/p/8472711.html
http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-commands.html
http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-part-two.html
https://linoxide.com/linux-how-to/systemd-boot-process/
https://cloud.tencent.com/developer/article/1516125
https://www.cnblogs.com/sparkdev/p/8472711.html
https://www.ibm.com/developerworks/cn/linux/1407_liuming_init3/index.html