如何规范实现Shell脚本作为守护进程启动(无需外部工具)
嘿,我正好研究过符合《Linux守护进程编写指南》规范的Shell守护进程实现,不用daemontools或daemonize这类外部工具完全没问题,咱们一步步来拆解实现:
规范Shell守护进程的核心步骤
按照指南要求,一个标准守护进程需要完成以下关键操作,我会结合代码示例逐个说明:
1. Fork子进程并让父进程退出
这一步是为了让进程脱离当前终端的控制,成为后台进程的基础:
# 第一次fork,脱离父进程 fork if [ $? -ne 0 ]; then exit 1 # fork失败直接退出 fi # 父进程退出,让子进程成为孤儿进程,由init/systemd接管 exit 0
2. 创建新会话(setsid)
这一步会让进程脱离原会话组和终端,彻底成为独立的会话领导者:
setsid
3. 切换到持久化工作目录
通常切换到根目录/,避免因为原工作目录被卸载导致进程异常:
cd /
4. 重置文件模式掩码(umask)
重置umask可以确保守护进程创建的文件权限符合预期,比如设置为022(默认文件权限644,目录755):
umask 022
5. 关闭所有不必要的文件描述符
默认继承的stdin、stdout、stderr不再需要,直接关闭,避免占用资源:
# 关闭标准输入、输出、错误描述符 exec 0>&- exec 1>&- exec 2>&-
6. 重定向输出到日志文件(可选但推荐)
如果需要记录日志,可以把stdout和stderr重定向到指定日志文件:
# 日志文件路径可根据需求修改 LOG_FILE="/var/log/my_daemon.log" # 重定向stdout和stderr到日志文件,追加模式 exec 1>>"$LOG_FILE" 2>&1
7. 处理信号实现优雅退出
为了让守护进程能被正常终止,最好捕获SIGTERM等信号,执行清理操作:
# 定义信号处理函数 cleanup() { echo "Daemon is shutting down..." # 这里添加你的清理逻辑,比如关闭文件、释放资源等 exit 0 } # 捕获SIGTERM、SIGINT信号 trap cleanup SIGTERM SIGINT
完整的Shell守护进程示例
把上面的步骤整合起来,一个完整的守护进程脚本大概是这样:
#!/bin/bash # 守护进程主逻辑 daemon_main() { while true; do # 这里替换成你的实际业务逻辑,比如每隔10秒打印一次日志 echo "Daemon is running at $(date)" sleep 10 done } # 启动守护进程 start_daemon() { # 第一步:Fork子进程 fork if [ $? -ne 0 ]; then echo "Failed to fork process" >&2 exit 1 fi exit 0 # 父进程退出 # 以下是子进程执行的逻辑 setsid # 创建新会话 cd / # 切换工作目录 umask 022 # 重置umask # 关闭标准文件描述符 exec 0>&- exec 1>&- exec 2>&- # 重定向输出到日志 LOG_FILE="/var/log/my_daemon.log" exec 1>>"$LOG_FILE" 2>&1 # 设置信号捕获 trap 'echo "Shutting down..."; exit 0' SIGTERM SIGINT # 执行主逻辑 daemon_main } # 根据参数执行对应操作 case "$1" in start) start_daemon ;; stop) # 通过进程名查找PID并发送终止信号 PID=$(pgrep -f "$0") if [ -n "$PID" ]; then kill "$PID" echo "Daemon stopped" else echo "Daemon is not running" >&2 fi ;; status) if pgrep -f "$0" >/dev/null; then echo "Daemon is running" else echo "Daemon is not running" fi ;; *) echo "Usage: $0 {start|stop|status}" >&2 exit 1 ;; esac
设置开机自启
如果要让这个脚本在系统启动时自动运行,推荐用systemd(现代Linux发行版默认):
- 创建一个systemd服务文件,比如
/etc/systemd/system/my_daemon.service:
[Unit] Description=My Custom Shell Daemon After=network.target [Service] Type=forking ExecStart=/path/to/your/daemon_script.sh start ExecStop=/path/to/your/daemon_script.sh stop Restart=always [Install] WantedBy=multi-user.target
- 重新加载systemd配置并启用服务:
sudo systemctl daemon-reload sudo systemctl enable --now my_daemon.service
这样你的Shell脚本就完全符合规范,成为一个独立的守护进程,并且能开机自动运行了。
内容的提问来源于stack exchange,提问作者user339676
相关产品推荐
相关产品推荐

