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