守护进程的创建

守护进程的创建

守护进程的一般概念

守护进程是挂载在后台的,因此其不因远程终端会话的关闭而结束。
并且其往往用于服务器自检和重启动,其应尽可能简洁。
所以我们得到了如下两个特性:
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;
}

测试结果与说明

image

1main.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进程托管。

孤儿进程的实验:https://www.cnblogs.com/tofu-ran/p/17133585.html

posted @   吃啊吃啊吃臭豆腐  阅读(40)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示