Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

1. 功能概述

start-stop-daemon 是一个用于管理系统守护进程的工具,主要功能包括:

  • 启动守护进程:创建并运行后台服务程序。
  • 停止守护进程:终止正在运行的服务程序。
  • 检查进程状态:验证服务是否在运行。
  • 发送信号:向守护进程发送特定的控制信号。
    它主要用于 Unix/Linux 系统的 init 脚本和服务管理,确保同一程序只运行一个实例。

2. 工作原理

2.1. 核心工作机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
启动流程:
1. 检查是否已有实例运行
- 通过 PID 文件检查
-通过进程名称匹配
-通过/proc文件系统检查

2. 决定操作:
-如果实例存在且 --oknodo 未设置 → 退出
-如果没有实例 → 启动新进程

3. 进程启动:
-fork() 新进程
-可选 chroot/chdir
-设置用户/组权限
-重定向标准 I/O
-exec() 目标程序

2.2. 进程检测机制

1
2
3
4
5
6
// 简化的检测逻辑
检测优先级:
1. --pidfile:最精确,读取文件中的 PID
2. --exec:匹配可执行文件路径
3. --name:匹配进程名称
4. --user:结合以上条件过滤

3. 详细用法

3.1. 基本语法

1
2
3
start-stop-daemon [选项] --start -- 执行命令 [参数]
start-stop-daemon [选项] --stop
start-stop-daemon [选项] --status

3.2. 启动选项

选项 描述 示例
--start 启动进程 --start
--stop 停止进程 --stop
--status 检查状态 --status
--pidfile 指定 PID 文件 --pidfile /var/run/nginx.pid
--exec 指定可执行文件 --exec /usr/bin/nginx
--name 指定进程名 --name nginx
--user 指定运行用户 --user www - data
--chdir 指定工作目录 --chdir /var/www
--chroot 改变根目录 --chroot /jail
--make-pidfile 创建 PID 文件 --make-pidfile
--background 后台运行 --background
--nicelevel 设置优先级 --nicelevel - 5

3.3. 停止选项

选项 描述 示例
--signal 指定停止信号 --signal TERM
--retry 重试策略 --retry TERM/5/KILL
--oknodo 无操作也返回成功 --oknodo
--quiet 安静模式 --quiet

4. 实战示例

4.1. 启动服务示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 简单启动 Nginx
# 说明: Nginx 自己会创建 PID 文件,并保存到/var/run/nginx.pid(写入自己的 PID),所以不需要指定`make-pidfile`参数。start-stop-daemon 读取这个文件来验证进程状态。
start-stop-daemon --start \
--pidfile /var/run/nginx.pid \
--exec /usr/sbin/nginx \
-- - c /etc/nginx/nginx.conf

# 以特定用户启动
start-stop-daemon --start \
--pidfile /var/run/myservice.pid \
--user nobody \
--group nogroup \
--chdir /var/lib/service \
--background \
--make-pidfile \
--exec /usr/bin/myservice

# 启动 Java 应用
start-stop-daemon --start \
--pidfile /var/run/myapp.pid \
--user appuser \
--chdir /opt/app \
--background \
--make-pidfile \
--exec /usr/bin/java -- \
- jar myapp.jar

4.2. 停止服务示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 通过 PID 文件停止
start-stop-daemon --stop \
--pidfile /var/run/nginx.pid

# 带重试的优雅停止
start-stop-daemon --stop \
--pidfile /var/run/mysql.pid \
--retry TERM/30/KILL/5 \
--oknodo

# 通过进程名停止
start-stop-daemon --stop \
--name sshd \
--signal HUP

4.3. 完整 init 脚本示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#!/bin/sh
# /etc/init.d/myservice

NAME = myservice
DAEMON = /usr/bin/$NAME
PIDFILE = /var/run/$NAME.pid
USER = nobody

start() {
echo "Starting $NAME..."
start-stop-daemon --start \
--pidfile $PIDFILE \
--user $USER \
--chdir /var/lib/$NAME \
--background \
--make-pidfile \
--exec $DAEMON
echo "OK"
}

stop() {
echo "Stopping $NAME..."
start-stop-daemon --stop \
--pidfile $PIDFILE \
--retry TERM/20/KILL/5 \
--oknodo
rm - f $PIDFILE
echo "OK"
}

status() {
start-stop-daemon --status --pidfile $PIDFILE
case "$?" in
0) echo "$NAME is running" ;;
3) echo "$NAME is not running" ;;
4) echo "Unable to determine status" ;;
esac
}

case "$1" in
start) start ;;
stop) stop ;;
restart) stop; sleep 2; start ;;
status) status ;;
*) echo "Usage: $0 {start|stop|restart|status}" ;;
esac

5. 高级用法

5.1. 信号处理策略

1
2
3
4
5
6
7
8
9
# 多重信号重试
--retry TERM/30/KILL/5
# 解释:发送 TERM 信号,等待 30 秒
# 如果失败,发送 KILL 信号,等待 5 秒
# 如果仍然失败,报错并退出

# 自定义重试时间
--retry 10 # 重试10次,每次间隔1秒
--retry TERM/10 # 发送TERM信号,等待10秒

5.2. 复杂的进程匹配

1
2
3
4
5
6
7
8
9
10
11
12
# 组合条件匹配
start-stop-daemon --stop \
--user www - data \
--exec /usr/bin/php \
--name "php - fpm*" \
--pidfile /var/run/php - fpm.pid

# 排除特定进程
start-stop-daemon --stop \
--user mysql \
--exec /usr/sbin/mysqld \
--test # 测试模式,不实际停止

5.3. 监控和日志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 带输出重定向的启动
start-stop-daemon --start \
--background \
--make-pidfile \
--pidfile /var/run/app.pid \
--exec /usr/bin/app \
-- \
--log - file /var/log/app.log 2>&1

# 状态检查脚本
if start-stop-daemon --status --pidfile /var/run/service.pid; then
echo "Service is running"
else
echo "Service is not running"
start-stop-daemon --start ...
fi

6. 退出码详解

start-stop-daemon 的退出码设计用于脚本编程,不会在终端输出信息,而是通过返回值传递状态。不同命令的退出码含义有所不同,下面详细说明。

6.1. 通用退出码(所有命令都可能返回)

退出码 含义 说明
0 成功 操作成功完成
1 一般性错误 无法确定具体错误类型
2 无效选项 命令行参数语法错误或不支持
3 权限不足 没有足够权限执行操作(如无法读取pidfile)
4 资源问题 如内存不足、进程表满等
5 配置错误 配置文件或环境问题
6 程序未安装 指定的可执行文件不存在

6.2. --start 命令的退出码

退出码 含义 详细说明
0 启动成功 进程已启动(或已在运行且未指定 --oknodo 时的默认行为)
1 启动失败 一般性启动失败(如fork失败、exec失败)
2 已在运行 仅在指定了 --oknodo:进程已在运行且没有采取任何操作
3 权限不足 无法以指定用户身份运行
4 资源不足 系统资源不足无法启动新进程
5 pidfile错误 无法创建pidfile或pidfile已存在且属于其他进程(且未指定 --oknodo

关键细节:

  • 如果不加 --oknodo,当进程已在运行时,--start 默认仍然返回0(表示”操作成功”——因为进程已在运行,符合期望)
  • 如果加了 --oknodo,当进程已在运行时,返回2(表示”无事可做”)

示例:

1
2
3
4
5
6
7
# 不加 --oknodo
start-stop-daemon --start --pidfile /run/app.pid --exec /usr/bin/app
# 如果app已在运行,返回0

# 加 --oknodo
start-stop-daemon --start --oknodo --pidfile /run/app.pid --exec /usr/bin/app
# 如果app已在运行,返回2

6.3. --stop 命令的退出码

退出码 含义 详细说明
0 停止成功 进程已停止
1 停止失败 一般性停止失败(如发送信号失败)
2 未在运行 仅在指定了 --oknodo:进程未在运行且没有采取任何操作
3 pidfile错误 pidfile存在但无法读取或内容无效
4 进程匹配失败 根据指定条件未找到匹配的进程
5 超时 使用 --retry 时,在指定时间内进程未终止

关键细节:

  • 如果不加 --oknodo,当进程未运行时,--stop 默认返回1(表示”操作失败”——因为本应停止的进程不存在)
  • 如果加了 --oknodo,当进程未运行时,返回2(表示”无事可做”)

示例:

1
2
3
4
5
6
7
# 不加 --oknodo
start-stop-daemon --stop --pidfile /run/app.pid
# 如果app未运行,返回1

# 加 --oknodo
start-stop-daemon --stop --oknodo --pidfile /run/app.pid
# 如果app未运行,返回2

--retry 的退出码影响:

  • 如果指定了 --retry TIMEOUT--retry SCHEDULE,且进程在超时后仍未终止,返回5
  • 例如:--retry 5 表示先发SIGTERM,等5秒,若未终止则发SIGKILL,若仍失败则返回5

6.4. --restart 的退出码

注意:--restart 在标准 start-stop-daemon不是直接支持的参数,通常是init脚本组合使用 --stop--start 实现。但有些发行版(如Debian/Ubuntu)的版本支持 --restart

退出码 含义 详细说明
0 重启成功 成功停止并重新启动
1 一般性失败 整体重启流程失败
2 停止失败 无法停止现有进程
3 启动失败 成功停止但无法启动新进程
4 未在运行 进程未运行且启动了新进程(类似--start
5 未在运行且未启动 进程未运行且因某些原因未启动(如条件不满足)

如果没有内置的 --restart,手动实现的伪代码:

1
2
3
start-stop-daemon --stop --oknodo --pidfile $PIDFILE
start-stop-daemon --start --pidfile $PIDFILE --exec $EXEC
# 根据两个命令的返回值综合判断

6.5. --status 命令的退出码详解

这是你遇到的情况,--status 的退出码设计最为详细:

退出码 含义 详细说明
0 进程正在运行 根据pidfile或进程名匹配到活动进程
1 进程未运行 pidfile不存在 或 pidfile中的PID已不存在
2 进程未运行,且pidfile已失效 (较少使用)
3 进程未运行,但pidfile存在 pidfile存在但对应的进程已死亡(stale pidfile
4 无法确定状态 权限不足无法读取/proc,或指定的匹配条件不明确

示例判断逻辑:

1
2
3
4
5
6
7
8
start-stop-daemon --status --pidfile /run/app.pid --exec /usr/bin/app
case $? in
0) echo "✅ Running" ;;
1) echo "❌ Not running (no pidfile or dead)" ;;
3) echo "⚠️ Stale pidfile exists" ;;
4) echo "❓ Cannot determine (permission?)" ;;
*) echo "💥 Error: $?" ;;
esac

6.6. 退出码速查表

操作 --oknodo 影响 成功 已运行(stop时未运行) 失败/错误
--status 无关 0 1或3 4
--start 0 0(视为成功) 1,3,4,5
--start 0 2 1,3,4,5
--stop 0 1(视为失败) 3,4,5
--stop 0 2 3,4,5
--restart* 视子命令 0 4或5 1,2,3

*注:--restart 的退出码因实现而异

6.7. 实际应用示例

6.7.1. 检查状态并给出人类可读输出

1
2
3
4
5
6
7
8
9
10
check_status() {
start-stop-daemon --status --pidfile "$1" --exec "$2"
case $? in
0) echo "RUNNING" ;;
1) echo "STOPPED (no pidfile)" ;;
3) echo "STOPPED (stale pidfile)" ;;
4) echo "UNKNOWN (permission?)" ;;
*) echo "ERROR" ;;
esac
}

6.7.2. 安全的停止脚本

1
2
3
4
5
6
7
8
9
10
11
12
stop_service() {
start-stop-daemon --stop --retry 5 \
--oknodo \
--pidfile "$PIDFILE" \
--exec "$EXEC"
case $? in
0) echo "Stopped successfully";;
2) echo "Already stopped";;
5) echo "Failed to stop after timeout"; exit 1;;
*) echo "Stop failed"; exit 1;;
esac
}

6.7.3. 启动脚本考虑已运行情况

1
2
3
4
5
6
7
8
9
10
11
start_service() {
start-stop-daemon --start --background --make-pidfile \
--pidfile "$PIDFILE" \
--exec "$EXEC" -- "$@"

case $? in
0) echo "Started (or already running)" ;;
2) echo "Already running (--oknodo would return 2)";;
*) echo "Start failed"; exit 1;;
esac
}

7. 注意事项

7.1. 常见问题

  1. PID 文件不一致

    1
    2
    # 确保 PID 文件正确写入
    --make-pidfile # 让工具自动创建
  2. 权限问题

    1
    2
    3
    # 确保有足够权限
    --user 指定用户必须有执行权限
    --chdir 目录必须可访问
  3. 进程检测失败

    1
    2
    # 使用多个检测条件
    --pidfile + --exec + --user 组合使用

7.2. 最佳实践

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 1. 总是使用 PID 文件
start-stop-daemon --start \
--pidfile $PIDFILE \
--make-pidfile \
--exec $DAEMON

# 2. 停止时使用重试机制
start-stop-daemon --stop \
--pidfile $PIDFILE \
--retry TERM/30/KILL/10 \
--oknodo

# 3. 启动前验证配置
test - x $DAEMON || exit 0
test - f $CONFIG || exit 0

8. 跨平台差异

系统 命令位置 特性差异
Debian/Ubuntu /sbin/start-stop-daemon 完整功能
RedHat/CentOS /usr/sbin/start-stop-daemon 可能需要安装 dpkg
Alpine Linux /sbin/start-stop-daemon BusyBox 版本,功能简化
macOS 需要安装 通过 brew 安装 dpkg

9. 调试技巧

由于 start-stop-daemon 不输出任何信息,调试时可以:

  1. 详细输出

    添加 --verbose 选项(如果版本支持),可以查看详细的调试信息。

    1
    start-stop-daemon --verbose --status --pidfile ...
  2. 检查退出码

    1
    start-stop-daemon --status --pidfile ...; echo "Exit code: $?"
  3. 查看系统日志

    1
    tail -f /var/log/syslog | grep start-stop-daemon
  4. 使用 strace 跟踪(高级):

    1
    strace -e trace=process start-stop-daemon --status --pidfile ...
  5. 测试模式(不实际执行)

    1
    2
    3
    4
    5
    # 测试模式(不实际执行)
    start-stop-daemon --start \
    --pidfile $PIDFILE \
    --exec $DAEMON \
    --test

通过合理使用 start-stop-daemon,可以编写出健壮、可靠的服务管理脚本,确保系统服务的稳定运行。

评论