守护进程的创建
守护进程的创建
守护进程的一般概念
守护进程是挂载在后台的,因此其不因远程终端会话的关闭而结束。
并且其往往用于服务器自检和重启动,其应尽可能简洁。
所以我们得到了如下两个特性:
1.后台运行。不受输入输出和控制终端影响
2.与当前环境隔离。包括未关闭的文件描述符、控制终端、会话、进程组、工作目录和文件创建掩码等等
守护进程创建的流程图
操作一
创建子进程并杀死主进程。子进程变为孤儿进程由Init进程托管
pid_t ret = fork();
if (ret == -1) { return -1; }
if (ret > 0) { exit(0); }
操作二
setsid函数创建新的会话。子进程会成为新的会话组组长。由于会话过程对终端的独占性,新进程组与控制终端脱离。
ret = setsid();
if (ret == -1) { return -2; }
操作三
创建孙进程并杀死子进程。孙进程才是真正的守护进程。
ret = fork();
if (ret == -1) { return -3; }
if (ret > 0) { exit(0); }
操作四
关闭打开的文件描述符
进程从创建它的父进程那里继承了打开的文件描述符。如不关闭,将会浪费系统资源,造成进程所在的文件系统无法卸下以及引起无法预料的错误。
umask(0);
signal(SIGCHLD, SIG_IGN);
测试代码
int switchDaemon();
int main()
{
switchDaemon();
return 0;
}
int switchDaemon() {
printf("% s(% d) :< % s> pid=%d ppid=%d pgid=%d sid=%d\n", __FILE__, __LINE__, __FUNCTION__,getpid(), getppid(), getpgid(getpid()), getsid(getpid()));
pid_t ret = fork();
if (ret == -1) { return -1; }
if (ret > 0) { exit(0); }
printf("% s(% d) :< % s> pid=%d ppid=%d pgid=%d sid=%d\n", __FILE__, __LINE__, __FUNCTION__, getpid(), getppid(), getpgid(getpid()), getsid(getpid()));
sleep(1);
printf("% s(% d) :< % s> pid=%d ppid=%d pgid=%d sid=%d\n", __FILE__, __LINE__, __FUNCTION__, getpid(), getppid(), getpgid(getpid()), getsid(getpid()));
ret = setsid();
if (ret == -1) { return -2; }
printf("% s(% d) :< % s> pid=%d ppid=%d pgid=%d sid=%d\n", __FILE__, __LINE__, __FUNCTION__, getpid(), getppid(), getpgid(getpid()), getsid(getpid()));
ret = fork();
if (ret == -1) { return -3; }
if (ret > 0) { exit(0); }
printf("% s(% d) :< % s> pid=%d ppid=%d pgid=%d sid=%d\n", __FILE__, __LINE__, __FUNCTION__, getpid(), getppid(), getpgid(getpid()), getsid(getpid()));
sleep(1);
printf("% s(% d) :< % s> pid=%d ppid=%d pgid=%d sid=%d\n", __FILE__, __LINE__, __FUNCTION__, getpid(), getppid(), getpgid(getpid()), getsid(getpid()));
umask(0);
signal(SIGCHLD, SIG_IGN);
return 1;
}
测试结果与说明
1:main.cpp(25) :< switchDaemon> pid=241719 ppid=237439 pgid=241719 sid=237439
2:main.cpp(29) :< switchDaemon> pid=241720 ppid=241719 pgid=241719 sid=237439
3:main.cpp(31) :< switchDaemon> pid=241720 ppid=1 pgid=241719 sid=237439
4:main.cpp(36) :< switchDaemon> pid=241720 ppid=1 pgid=241720 sid=241720
5:main.cpp(41) :< switchDaemon> pid=241721 ppid=241720 pgid=241720 sid=241720
6:main.cpp(43) :< switchDaemon> pid=241721 ppid=1 pgid=241720 sid=241720
1:远程ssh会话,创建进程,我们叫它“父进程”。父进程的ppid和sid指向会话ssh进程id,pid和pgid均为父进程自己。
2:父进程创建子进程。子进程的ppid和pgid指向pid,sid指向会话
3:杀死父进程。子进程的ppid指向1(被init进程托管),sid指向会话,pgid依旧指向被杀死的父进程,不会自己更新。
4:子进程创建全新的会话。子进程依旧被init进程托管,sid指向子进程自己,pgid指向自己。它成了会话绑定的进程。
5:创建孙进程。孙进程ppid和sid和pgid都指向了子进程。
6:杀死子进程。孙进程其余不变,ppid指向1,被Init进程托管。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!