守护进程必须通过两次 fork + setsid 脱离终端控制,再清理环境(chdir("/")、umask(0)、关闭文件描述符)、重定向标准流,或交由 systemd 统一管理。

普通程序默认依附于终端,关掉 SSH 或退出 Shell 就会终止;要让它变成持续运行、不受终端影响的后台服务,关键不是简单加个 &,而是通过一套标准的“守护化”流程,切断与终端的所有关联,并重置运行环境。
脱离终端控制:两次 fork + setsid
第一次 fork 后父进程退出,子进程成为孤儿进程,被 init(PID=1)收养;接着调用 setsid() 创建新会话,使进程脱离原控制终端、不再响应 SIGHUP 等终端信号;再 fork 一次,确保它不能重新获得控制终端(因为会话首进程在某些条件下可能再次打开终端)。这三步共同保证进程彻底“断联”。
清理运行环境:目录、权限、文件描述符
守护进程不能卡在某个挂载点下,否则卸载该目录会导致异常;也不能继承父进程的 umask 或打开的文件句柄:
- 用
chdir("/")切换到根目录,避免阻塞文件系统卸载 - 调用
umask(0)重置权限掩码,让后续创建的文件权限可控 - 遍历关闭所有已打开的文件描述符(通常用
sysconf(_SC_OPEN_MAX)获取上限),防止资源泄漏
重定向标准流:屏蔽输入输出干扰
守护进程没有终端,标准输入、输出、错误不能再指向 tty。常规做法是:
- 打开
/dev/null三次,分别用于重定向 stdin(fd 0)、stdout(fd 1)、stderr(fd 2) - 也可重定向到日志文件,但需确保路径存在、有写权限,并考虑日志轮转
现代系统推荐:交给 systemd 管理
手动实现守护化容易出错,且缺乏自动重启、依赖管理、资源限制等能力。现在更推荐编写 systemd service 单元文件,由 systemd 负责启动、守护、日志收集和故障恢复:
- 设置
Type=simple(默认)或Type=notify(配合 sd_notify) - 用
Restart=always实现崩溃自启 - 通过
User=、LimitNOFILE=等字段安全约束运行环境











