0

0

c++如何编写守护进程_c++ fork子进程与脱离控制终端【指南】

穿越時空

穿越時空

发布时间:2026-01-01 12:03:09

|

348人浏览过

|

来源于php中文网

原创

必须 fork 两次:第一次让子进程调用 setsid() 脱离终端并成为会话首进程,第二次确保该进程不再是会话首进程,从而永久失去获取控制终端的能力。

c++如何编写守护进程_c++ fork子进程与脱离控制终端【指南】

为什么 fork 一次不够,必须 fork 两次?

直接 fork() 出子进程后,进程仍可能继承父进程的会话(session)和控制终端(controlling terminal),一旦父进程退出或终端关闭,子进程会被发送 SIGHUP 信号终止——这和“守护进程要长期运行”的目标冲突。

关键在于:守护进程必须不是会话首进程(session leader),否则后续调用 setsid() 会失败;而 setsid() 又是脱离终端的必要步骤。所以标准做法是:

  • 第一次 fork():让子进程脱离父进程的上下文,然后父进程退出
  • 子进程调用 setsid():成为新会话首进程、脱离控制终端、清空进程组 ID
  • 第二次 fork():让这个新会话的首进程退出,确保它**永远无法重新获得控制终端**(POSIX 规定:只有会话首进程才能打开终端设备,而这次 fork 后的新子进程不再是会话首进程)

如何正确调用 setsid() 并处理工作目录与文件描述符?

setsid() 必须在第一次 fork() 的子进程中立即调用,且不能在父进程中调用(会失败并返回 -1)。但它只是第一步,还必须配合其他清理动作,否则守护进程可能意外占用资源或阻塞系统。

常见遗漏点:

立即学习C++免费学习笔记(深入)”;

  • 不重定向 stdin/stdout/stderr:默认仍连着终端,日志写入失败或引发 SIGPIPE
  • 不更改工作目录(chdir("/")):导致当前挂载点无法卸载(如守护进程启动时在 /mnt/usb 下)
  • 不设置文件掩码(umask(0)):子进程创建的文件权限受父进程 umask 影响,不可控
  • 不关闭继承的文件描述符:父进程打开的 socket、log 文件等可能被意外读写

建议在第二次 fork() 后的最终子进程中执行这些操作:

Videoleap
Videoleap

Videoleap是一个一体化的视频编辑平台

下载
umask(0);
chdir("/");
for (int i = 0; i < sysconf(_SC_OPEN_MAX); ++i) {
    close(i);
}
open("/dev/null", O_RDONLY);
open("/dev/null", O_WRONLY);
open("/dev/null", O_WRONLY); // 保证 0,1,2 被重定向到 /dev/null

Linux 上如何防止守护进程被 systemd 拦截或误杀?

现代 Linux 发行版普遍使用 systemd 管理服务,直接用传统 fork + setsid 启动的进程容易被归入用户 session,随登录登出被 kill,或被 systemd --user 收集为 transient unit 限制资源。

若你坚持手写 C++ 守护进程(而非写 .service 文件),需主动规避 systemd 的自动管理:

  • 避免从交互式 shell 直接启动(比如不要在 ssh 会话里 ./daemon 运行)
  • 启动前显式脱离 cgroup:调用 prctl(PR_SET_CHILD_SUBREAPER, 0)(可选,降低被父 cgroup 接管概率)
  • 检查 /proc/self/cgroup 内容,若路径含 user.slicesession-,说明仍在用户会话中,应改用 systemctl --system enable && systemctl start
  • 更稳妥的做法:放弃纯 fork 方案,改用 systemd 托管,C++ 程序只做核心逻辑,不自己 fork

std::this_thread::sleep_for 在守护进程中为什么不能替代信号等待?

很多初学者用一个无限循环加 std::this_thread::sleep_for(std::chrono::seconds(1)) 模拟守护行为,但这只是“假装”在运行,实际无法响应外部事件(如配置重载、平滑重启、优雅退出)。

真正健壮的守护进程必须基于异步事件驱动,例如:

  • signalfd()(Linux)或 sigwait() 等待 SIGHUPSIGTERM
  • epoll_wait()poll() 监听配置文件 inotify fd、socket、定时器 fd
  • 避免阻塞在 sleep 上——它让进程对信号不敏感,且无法实现“收到信号立刻处理”

哪怕只是最简版本,也建议至少处理 SIGTERM

volatile sig_atomic_t keep_running = 1;
void signal_handler(int sig) { if (sig == SIGTERM) keep_running = 0; }
signal(SIGTERM, signal_handler);
while (keep_running) {
    // do work
    sleep(1);
}
守护进程的难点不在 fork 次数,而在“彻底断连”——断开终端、会话、父进程、文件描述符、信号屏蔽、cgroup 和 systemd 的隐式绑定。漏掉任意一环,都可能在某个环境里突然失效。

相关专题

更多
session失效的原因
session失效的原因

session失效的原因有会话超时、会话数量限制、会话完整性检查、服务器重启、浏览器或设备问题等等。详细介绍:1、会话超时:服务器为Session设置了一个默认的超时时间,当用户在一段时间内没有与服务器交互时,Session将自动失效;2、会话数量限制:服务器为每个用户的Session数量设置了一个限制,当用户创建的Session数量超过这个限制时,最新的会覆盖最早的等等。

302

2023.10.17

session失效解决方法
session失效解决方法

session失效通常是由于 session 的生存时间过期或者服务器关闭导致的。其解决办法:1、延长session的生存时间;2、使用持久化存储;3、使用cookie;4、异步更新session;5、使用会话管理中间件。

706

2023.10.18

cookie与session的区别
cookie与session的区别

本专题整合了cookie与session的区别和使用方法等相关内容,阅读专题下面的文章了解更详细的内容。

88

2025.08.19

磁盘配额是什么
磁盘配额是什么

磁盘配额是计算机中指定磁盘的储存限制,就是管理员可以为用户所能使用的磁盘空间进行配额限制,每一用户只能使用最大配额范围内的磁盘空间。php中文网为大家提供各种磁盘配额相关的内容,教程,供大家免费下载安装。

1345

2023.06.21

如何安装LINUX
如何安装LINUX

本站专题提供如何安装LINUX的相关教程文章,还有相关的下载、课程,大家可以免费体验。

700

2023.06.29

linux find
linux find

find是linux命令,它将档案系统内符合 expression 的档案列出来。可以指要档案的名称、类别、时间、大小、权限等不同资讯的组合,只有完全相符的才会被列出来。find根据下列规则判断 path 和 expression,在命令列上第一个 - ( ) , ! 之前的部分为 path,之后的是 expression。还有指DOS 命令 find,Excel 函数 find等。本站专题提供linux find相关教程文章,还有相关

294

2023.06.30

linux修改文件名
linux修改文件名

本专题为大家提供linux修改文件名相关的文章,这些文章可以帮助用户快速轻松地完成文件名的修改工作,大家可以免费体验。

773

2023.07.05

linux系统安装教程
linux系统安装教程

linux系统是一种可以免费使用,自由传播,多用户、多任务、多线程、多CPU的操作系统。本专题提供linux系统安装教程相关的文章,大家可以免费体验。

571

2023.07.06

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

7

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PostgreSQL 教程
PostgreSQL 教程

共48课时 | 6.3万人学习

Git 教程
Git 教程

共21课时 | 2.3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号