Manpages

NAME

daemon - 編 寫 與 打 包 系 統 守 護 進 程

描 述

"守 護 進 程 "的 意 思 是 在 後 臺 運 行 的 服 務 進 程 , 常 用 於 監 督 系 統 的 運 行 或 者 提 供 某 種 功 能 。 在 傳 統 的 SysV Unix 系 統 上 , 多 個 守 護 進 程 必 須 嚴 格 按 照 特 定 的 順 序 依 次 啓 動 。 在 "新 型 "的 systemd(1) 系 統 上 , 守 護 進 程 的 啓 動 順 序 非 常 簡 單 且 非 常 強 大 。 本 手 冊 同 時 解 說 了 上 述 兩 種 不 同 的 啓 動 方 案 , 並 特 別 推 薦 了 應 該 包 含 在 systemd 系 統 中 的 守 護 進 程 。 傳 統 的 SysV守 護 進 程 傳 統 的 SysV守 護 進 程 在 啓 動 的 時 候 , 應 該 在 初 始 化 階 段 執 行 下 面 的 步 驟 :

1. 關 閉 除 STDIN STDOUT STDERR 之 外 的 所 有 文 件 描 述 符

2. 重 置 所 有 信 號 處 理 器

3. 重 置 所 有 信 號 掩 碼

4. 清 理 環 境 變 量 (重 置 一 部 分 , 移 除 一 部 分 )

5. 調 用 fork() 創 建 一 個 後 臺 進 程

6. 在 子 進 程 中 調 用 setsid() 從 終 端 脫 離 並 創 建 一 個 獨 立 的 會 話

7. 在 子 進 程 中 再 一 次 調 用 fork() 以 確 保 守 護 進 程 永 遠 無 法 獲 取 任 何 終 端 。

8. 第 一 個 子 進 程 主 動 退 出 , 只 有 第 二 個 子 進 程 (實 際 的 守 護 進 程 )保 持 運 行 , 並 且 以 init(PID=1) 爲 父 進 程 。

9. 守 護 進 程 (第 二 個 子 進 程 )將 STDIN STDOUT STDERR 連 接 到 /dev/null 虛 擬 設 備

10. 守 護 進 程 將 umask 設 爲 0

11. 守 護 進 程 將 當 前 目 錄 切 換 到 根 目 錄 (/)

12. 守 護 進 程 將 自 身 的 PID記 錄 到 例 如 /run/foobar.pid 這 樣 的 文 件 中

13. 守 護 進 程 丟 棄 自 己 不 需 要 的 權 限 (如 果 可 以 )

14. 守 護 進 程 通 知 最 初 的 父 進 程 : 初 始 化 工 作 已 完 成

15. 最 初 的 父 進 程 自 身 退 出 注 意 , 這 些 步 驟 對 於 下 文 講 述 的 新 型 守 護 進 程 是 不 需 要 的 , 除 非 爲 了 刻 意 兼 容 傳 統 的 SysV系 統 。 新 型 守 護 進 程

Linux 系 統 上 的 新 型 守 護 進 程 更 容 易 被 監 控 也 更 容 易 實 現 。 守 護 進 程 無 需 實 現 前 文 所 描 述 的 複 雜 步 驟 , 即 可 直 接 在 systemd 提 供 的 乾 淨 的 上 下 文 環 境 中 運 行 : 環 境 變 量 已 經 被 清 理 、 信 號 處 理 器 與 信 號 掩 碼 已 經 被 重 置 、 沒 有 遺 留 的 文 件 描 述 符 、 守 護 進 程 自 動 在 其 專 屬 的 會 話 中 執 行 、 標 準 輸 入 (STDIN)已 被 連 接 到 /dev/null 虛 擬 設 備 (除 非 另 有 配 置 )、 標 準 輸 出 (STDOUT)與 標 準 錯 誤 (STDERR)已 被 連 接 到 systemd-journald.service(8) 日 誌 服 務 (除 非 另 有 配 置 )、 umask 已 經 被 重 置 ... 等 等 新 型 守 護 進 程 只 需 要 遵 守 如 下 要 求 :

1. 收 到 SIGTERM 信 號 後 關 閉 進 程 並 確 保 乾 淨 的 退 出

2. 收 到 SIGHUP 信 號 後 重 新 加 載 配 置 文 件 (若 需 要 )

3. 主 守 護 進 程 在 退 出 時 應 該 按 照 LSB recommendations for SysV init scripts [1] 的 要 求 返 回 恰 當 的 退 出 碼 , 以 便 於 systemd 判 斷 服 務 的 退 出 狀 態 。

4. 若 可 行 , 在 初 始 化 的 最 後 一 步 , 通 過 D-Bus 創 建 進 程 的 控 制 接 口 , 並 在 D-Bus 上 註 冊 一 個 總 線 名 稱 。

5. 提 供 一 個 .service 單 元 文 件 , 包 含 如 何 啓 動 /停 止 /維 護 該 服 務 的 配 置 。 詳 見 systemd.service(5) 手 冊 。

6. 儘 可 能 依 賴 於 systemd 的 資 源 控 制 與 權 限 剝 奪 功 能 (CPU與 內 存 佔 用 /文 件 訪 問 等 等 ), 而 不 要 自 己 實 現 它 們 。 詳 見 systemd.exec(5) 手 冊 。

7. 若 使 用 了 D-Bus , 則 強 烈 推 薦 使 用 基 於 D-Bus 的 啓 動 機 制 。 這 樣 做 有 許 多 好 處 : 守 護 進 程 可 以 按 需 延 遲 啓 動 ; 可 以 和 依 賴 於 它 的 進 程 並 行 啓 動 (提 升 啓 動 速 度 ); 守 護 進 程 可 以 在 失 敗 時 被 自 動 重 啓 而 不 丟 失 D-Bus總 線 上 的 請 求 (詳 見 下 文 )

8. 若 守 護 進 程 通 過 套 接 字 提 供 服 務 , 則 強 烈 推 薦 使 用 基 於 套 接 字 的 啓 動 機 制 (詳 見 下 文 )。 這 樣 做 有 許 多 好 處 : 守 護 進 程 可 以 按 需 延 遲 啓 動 ; 可 以 和 依 賴 於 它 的 進 程 並 行 啓 動 (提 升 啓 動 速 度 ); 對 於 無 狀 態 協 議 (例 如 syslog, DNS), 守 護 進 程 可 以 在 失 敗 時 被 自 動 重 啓 而 不 丟 失 套 接 字 上 的 請 求 (詳 見 下 文 )

9. 若 可 能 , 守 護 進 程 應 該 通 過 sd_notify(3) 接 口 通 知 systemd "啓 動 已 完 成 "或 "狀 態 已 更 新 "這 樣 的 消 息 。

10. 不 要 使 用 syslog() 記 錄 日 誌 , 只 需 簡 單 的 使 用 fprintf() 向 STDERR 輸 出 日 誌 即 可 。 如 果 必 須 指 明 日 誌 等 級 , 則 可 以 在 日 誌 的 行 首 加 上 類 似 "<4>" 這 樣 的 前 綴 即 可 (這 裏 表 示 4級 "WARNING")。 詳 見 sd-daemon(3)systemd.exec(5) 手 冊 。 上 述 要 求 與 Apple MacOS X Daemon Requirements [2] 類 似 , 但 並 不 完 全 相 同 。

啓 動

systemd 提 供 了 多 種 啓 動 機 制 (見 下 文 ), 而 服 務 單 元 也 經 常 同 時 使 用 其 中 的 幾 種 。 例 如 bluetoothd.service 可 以 在 插 入 藍 牙 硬 件 時 被 啓 動 , 也 可 以 在 某 進 程 訪 問 其 D-Bus 接 口 時 被 啓 動 。 又 如 打 印 服 務 可 以 在 IPP端 口 有 流 量 接 入 時 被 啓 動 , 也 可 以 在 插 入 打 印 機 硬 件 時 被 啓 動 , 還 可 以 在 有 文 件 進 入 打 印 機 spool 目 錄 時 被 啓 動 。 甚 至 對 於 必 須 在 系 統 啓 動 時 無 條 件 啓 動 的 服 務 , 爲 了 儘 可 能 併 發 啓 動 , 也 應 該 使 用 某 些 啓 動 機 制 。 如 果 某 守 護 進 程 實 現 了 一 個 D-Bus 服 務 或 者 監 聽 一 個 套 接 字 , 那 麼 使 用 基 於 D-Bus 或 基 於 套 接 字 的 啓 動 機 制 , 將 允 許 該 進 程 與 其 客 戶 端 同 時 並 行 啓 動 (從 而 加 快 啓 動 速 度 )。 因 爲 所 有 的 通 信 渠 道 都 已 事 先 建 立 , 並 且 不 會 丟 失 任 何 客 戶 端 請 求 , 同 時 D-Bus 總 線 或 者 內 核 會 將 客 戶 端 請 求 排 入 隊 列 等 候 , 直 到 完 成 啓 動 。 系 統 啓 動 時 啓 動 傳 統 的 守 護 進 程 一 般 是 在 系 統 啓 動 時 通 過 SysV初 始 化 腳 本 自 動 啓 動 , systemd 也 支 持 這 種 啓 動 方 式 。 對 於 systemd 來 說 , 如 果 希 望 確 保 某 單 元 在 系 統 啓 動 時 自 動 啓 動 , 那 麼 最 佳 的 做 法 是 在 默 認 啓 動 目 標 (通 常 是 multi-user.target 或 graphical.target)的 .wants/ 目 錄 中 爲 該 單 元 建 立 軟 鏈 接 。 參 見 systemd.unit(5) 手 冊 以 瞭 解 .wants/ 目 錄 , 參 見 systemd.special(7) 手 冊 以 瞭 解 上 述 兩 個 特 殊 的 啓 動 目 標 。 基 於 套 接 字 的 啓 動 爲 了 儘 可 能 提 高 並 行 性 與 健 壯 性 , 以 及 簡 化 配 置 與 開 發 , 對 於 需 要 監 聽 套 接 字 的 服 務 , 強 烈 推 薦 使 用 基 於 套 接 字 的 啓 動 機 制 。 使 用 此 機 制 後 , 守 護 進 程 不 再 需 要 創 建 和 綁 定 套 接 字 , 而 是 由 systemd 接 管 這 個 工 作 。 systemd 將 會 根 據 單 元 文 件 的 設 置 , 預 先 創 建 所 需 的 套 接 字 , 並 在 第 一 個 客 戶 端 請 求 接 入 的 時 候 啓 動 該 服 務 , 以 實 現 服 務 的 按 需 啓 動 。 該 機 制 的 好 處 還 在 於 , 預 先 創 建 好 套 接 字 之 後 , 所 有 使 用 此 套 接 字 通 信 的 進 程 可 以 並 行 啓 動 (包 括 客 戶 端 和 服 務 端 )。 此 外 , 重 啓 服 務 只 會 導 致 丟 失 最 低 限 度 的 客 戶 端 連 接 , 甚 至 不 丟 失 任 何 客 戶 端 請 求 (例 如 對 於 DNS 或 syslog 這 樣 的 無 狀 態 協 議 )。 因 爲 套 接 字 在 服 務 重 啓 期 間 始 終 保 持 有 效 並 且 可 被 訪 問 , 同 時 所 有 客 戶 端 請 求 也 都 被 排 入 隊 列 等 候 處 理 。 使 用 此 機 制 之 後 , 守 護 進 程 必 須 要 從 systemd 接 收 已 創 建 好 的 套 接 字 , 而 不 能 自 己 創 建 並 綁 定 套 接 字 。 關 於 如 何 使 用 該 機 制 , 參 見 sd_listen_fds(3)sd-daemon(3) 手 冊 。 只 需 要 小 小 的 修 改 , 即 可 在 原 有 啓 動 機 制 的 基 礎 上 添 加 基 於 套 接 字 的 啓 動 機 制 , 至 於 如 何 移 植 , 詳 見 後 文 。

systemd 通 過 .socket 單 元 實 現 該 機 制 , 詳 見 systemd.socket(5) 手 冊 。 必 須 確 保 所 有 爲 支 持 基 於 套 接 字 啓 動 而 創 建 的 監 聽 socket 單 元 都 被 包 含 在 sockets.target 中 。 建 議 在 socket 單 元 的 "[Install]" 小 節 加 入 WantedBy=sockets.target 設 置 , 以 確 保 在 啓 用 該 單 元 時 能 夠 自 動 添 加 上 述 依 賴 關 係 。 除 非 明 確 設 置 了 DefaultDependencies=no , 否 則 會 爲 所 有 socket 單 元 隱 含 的 創 建 必 要 的 順 序 依 賴 。 有 關 sockets.target 的 解 釋 , 詳 見 systemd.special(7) 手 冊 。 如 果 某 socket 單 元 已 被 包 含 在 sockets.target 中 , 那 麼 不 建 議 在 其 中 再 添 加 任 何 額 外 的 依 賴 關 係 (例 如 multi-user.target 之 類 )。 基 於 D-Bus 的 啓 動 如 果 守 護 進 程 使 用 D-Bus 與 客 戶 端 通 信 , 那 麼 它 應 該 使 用 基 於 D-Bus 的 啓 動 機 制 , 這 樣 當 客 戶 端 訪 問 其 D-Bus 接 口 時 , 該 服 務 將 被 自 動 啓 動 。 該 機 制 是 通 過 D-Bus service 文 件 實 現 的 (不 要 與 普 通 的 單 元 文 件 混 淆 )。 爲 了 確 保 讓 D-Bus 使 用 systemd 來 啓 動 與 維 護 守 護 進 程 , 必 須 在 這 些 D-Bus service 文 件 中 使 用 SystemdService= 指 明 其 匹 配 的 服 務 單 元 。 例 如 , 對 於 文 件 名 爲 org.freedesktop.RealtimeKit.service 的 D-Bus service 來 說 , 爲 了 將 其 綁 定 到 rtkit-daemon.service 服 務 單 元 , 必 須 確 保 在 該 文 件 中 設 置 了 SystemdService=rtkit-daemon.service 指 令 。 注 意 , 必 須 明 確 設 置 SystemdService= 指 令 , 否 則 當 服 務 單 元 同 時 使 用 多 種 啓 動 機 制 時 , 可 能 會 導 致 競 爭 條 件 的 出 現 。 基 於 設 備 的 啓 動 用 於 管 理 特 定 類 型 硬 件 的 守 護 進 程 , 只 應 該 在 符 合 條 件 的 硬 件 變 爲 可 用 或 者 被 插 入 時 , 才 需 要 啓 動 。 爲 了 達 到 上 述 目 的 , 可 以 將 服 務 的 啓 動 /停 止 與 硬 件 的 插 入 /拔 出 事 件 綁 定 。 當 帶 有 "systemd" 標 籤 的 設 備 出 現 在 sysfs/udev 設 備 樹 中 時 , systemd 將 會 自 動 爲 其 創 建 對 應 的 device 單 元 。 通 過 向 這 些 單 元 中 添 加 對 其 他 單 元 的 Wants= 依 賴 , 就 可 以 實 現 當 該 device 單 元 被 啓 動 (也 就 是 硬 件 被 插 入 )時 , 連 帶 啓 動 其 他 單 元 , 從 而 實 現 基 於 設 備 的 啓 動 。 這 可 以 通 過 向 udev 規 則 庫 中 添 加 SYSTEMD_WANTS= 屬 性 來 實 現 , 詳 見 systemd.device(5) 手 冊 。 通 常 , 並 不 是 將 service 單 元 直 接 添 加 到 設 備 的 Wants= 依 賴 中 , 而 是 通 過 專 用 的 target 單 元 間 接 添 加 。 例 如 , 不 是 將 bluetoothd.service 添 加 到 各 種 藍 牙 設 備 的 Wants= 依 賴 中 , 而 是 將 bluetoothd.service 添 加 到 bluetooth.target 的 Wants= 依 賴 中 , 同 時 再 將 bluetooth.target 添 加 到 各 種 藍 牙 設 備 的 Wants= 依 賴 中 。 通 過 引 入 bluetooth.target 這 個 抽 象 層 , 系 統 管 理 員 無 需 批 量 修 改 udev 規 則 庫 , 僅 通 過 systemctl enable|disable ... 命 令 修 改 bluetooth.target.wants/ 目 錄 中 的 軟 鏈 接 , 即 可 控 制 bluetoothd.service 的 使 用 。 基 於 路 徑 的 啓 動 對 於 處 理 spool 文 件 或 目 錄 的 守 護 進 程 (例 如 打 印 服 務 )來 說 , 僅 在 spool 文 件 或 目 錄 狀 態 發 生 變 化 或 者 內 容 非 空 時 , 才 需 要 啓 動 。 通 過 .path 單 元 實 現 的 、 基 於 路 徑 的 啓 動 機 制 正 好 適 用 於 這 種 場 合 , 詳 見 systemd.path(5) 手 冊 。 基 於 定 時 器 的 啓 動 對 於 週 期 性 的 操 作 (例 如 垃 圾 文 件 清 理 或 者 網 絡 對 時 ), 可 以 通 過 基 於 定 時 器 的 啓 動 機 制 來 實 現 。 這 種 機 制 通 過 .timer 單 元 實 現 , 詳 見 systemd.timer(5) 手 冊 。 其 他 啓 動 方 式 在 其 他 操 作 系 統 上 還 存 在 着 其 他 的 啓 動 機 制 , 不 過 這 些 機 制 都 可 以 被 前 述 的 各 種 機 制 的 組 合 替 代 。 因 此 在 這 裏 不 再 贅 述 。

與 SYSTEMD 整 合

編 寫 systemd 單 元 文 件 在 編 寫 單 元 文 件 時 應 當 考 慮 下 列 建 議 :

1. 儘 可 能 不 用 Type=forking 。 若 非 用 不 可 , 則 必 須 正 確 設 置 PIDFile= 指 令 。 參 見 systemd.service(5) 手 冊 。

2. 若 守 護 進 程 在 D-Bus 上 註 冊 了 一 個 名 字 , 則 應 儘 可 能 使 用 Type=dbus

3. 設 置 一 個 易 於 理 解 的 Description=

4. 確 保 DefaultDependencies=yes , 除 非 該 單 元 必 須 在 系 統 啓 動 的 早 期 啓 動 或 者 必 須 在 系 統 關 閉 的 末 期 關 閉 。

5. 通 常 無 需 顯 式 定 義 依 賴 關 係 。 不 過 , 如 果 確 實 需 要 顯 式 定 義 依 賴 關 係 , 爲 了 確 保 單 元 文 件 不 侷 限 於 特 定 的 發 行 版 , 僅 應 該 依 賴 於 systemd.special(7) 中 列 出 的 單 元 以 及 自 身 所 屬 軟 件 包 中 提 供 的 單 元 。

6. 確 保 在 "[Install]" 小 節 中 包 含 完 整 的 啓 用 信 息 (參 見 systemd.unit(5) 手 冊 )。 若 希 望 自 動 啓 動 該 單 元 , 則 應 該 設 置 WantedBy=multi-user.targetWantedBy=graphical.target 若 希 望 自 動 啓 動 該 單 元 的 套 接 字 , 則 應 該 設 置 WantedBy=sockets.target 。 通 常 你 還 希 望 在 啓 用 該 單 元 時 , 一 起 啓 用 對 應 的 套 接 字 單 元 (假 定 爲 foo.service), 因 此 還 應 該 設 置 Also=foo.socket 安 裝 service 單 元 文 件 當 從 源 代 碼 編 譯 安 裝 (make install)軟 件 包 時 , 其 中 的 系 統 服 務 單 元 文 件 會 被 默 認 安 裝 到 pkg-config systemd --variable=systemdsystemunitdir 命 令 返 回 的 目 錄 中 (通 常 是 /usr/lib/systemd/system); 而 其 中 的 用 戶 服 務 單 元 文 件 會 被 默 認 安 裝 到 pkg-config systemd --variable=systemduserunitdir 命 令 返 回 的 目 錄 中 (通 常 是 /usr/lib/systemd/user); 但 並 不 應 該 使 用 systemctl enable ... 命 令 啓 用 它 們 。 當 從 包 管 理 器 安 裝 (rpm -i)二 進 制 軟 件 包 時 , 其 中 的 單 元 文 件 應 該 同 樣 安 裝 到 上 述 位 置 。 但 不 同 之 處 在 於 , 還 應 該 使 用 systemctl enable ... 命 令 啓 用 它 們 , 因 此 安 裝 的 單 元 有 可 能 會 在 開 機 時 自 動 啓 動 。

移 植 已 有 的 守 護 進 程

雖 然 systemd 兼 容 傳 統 的 SysV 初 始 化 系 統 , 但 是 移 植 舊 有 的 守 護 進 程 可 以 更 好 的 利 用 systemd 的 先 進 特 性 。 建 議 對 舊 有 的 SysV 守 護 進 程 做 如 下 改 進 : ...[省 略 ]...

放 置 守 護 進 程 的 數 據

建 議 遵 守 file-hierarchy(7) 所 建 議 的 通 用 準 則 。

參 見

systemd(1), sd-daemon(3), sd_listen_fds(3), sd_notify(3), daemon(3), systemd.service(5), file-hierarchy(7)

NOTES

1.

LSB recommendations for SysV init scripts

http://refspecs.linuxbase.org/LSB_3.1.1/LSB-Core-generic/LSB-Core-generic/iniscrptact.html

2.

Apple MacOS X Daemon Requirements

https://developer.apple.com/library/mac/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html

本 頁 面 中 文 版 由 中 文 man 手 冊 頁 計 劃 提 供 。 翻 譯 人 員 : 金 步 國 金 步 國 作 品 集 : http://www.jinbuguo.com 中 文 man 手 冊 頁 計 劃 : https://github.com/man-pages-zh/manpages-zh