结论:守护进程(daemon)是脱离终端、在后台长期运行的服务进程。经典创建步骤:fork 后父进程退出(让 shell 认为命令结束、子进程成为孤儿被 init 收养)→ 子进程 setsid() 创建新会话脱离控制终端 → 再 fork 一次(防止重新获得终端)→ chdir 到根目录、重设 umask、关闭(或重定向到 /dev/null)标准输入输出和多余文件描述符。
展开:setsid 是关键一步:它让进程成为新会话的会话首进程和新进程组组长,与原控制终端彻底脱钩——终端关闭时内核会向会话首进程发 SIGHUP,未脱离的进程随之终止(nohup 的原理就是忽略 SIGHUP,但未脱离会话)。为什么要先 fork 再 setsid:setsid 要求调用者不能是进程组组长,fork 出的子进程 PID 与父进程不同、必然不是组长,保证 setsid 成功。二次 fork 的原因:会话首进程打开终端设备时会自动成为其控制终端,System V 传统下再 fork 一次让守护进程不再是会话首进程,杜绝意外获得终端。现代实践:直接交给 systemd(Type=simple 或 Type=notify),守护化、日志、重启策略都由系统管理,程序保持前台运行反而更易管理。易错点:不关闭继承的文件描述符会阻止卸载文件系统、占用端口。
if (fork() > 0) _exit(0);
setsid();
if (fork() > 0) _exit(0);
chdir("/"); umask(0);
追问方向:nohup、disown、tmux 各自保住进程的原理、systemd 的 cgroup 如何防止守护进程"逃跑"。