进程守护进程

2021-04-01  本文已影响0人  wayyyy

什么是守护进程?
Linux 的大多数服务器就是用守护进程实现的,使用ps -axj可以查看守护进程:

image.png

如何编写守护进程?

设置umask

Linux 不同,它是通过使用 umask 默认权限来给所有新建的文件和目录赋予初始权限的。

# umask  # root用户默认是0022,普通用户默认是 0002
  0022

这里先调用umask将文件模式创建屏蔽之设置为一个已知的值,通常是0。

umask(0);
在后台运行
//1)在父进程中,fork返回新创建子进程的进程ID;
//2)在子进程中,fork返回0;
//3)如果出现错误,fork返回一个负值;
int pid = fork();
if (pid < 0)
{
    std::cout << "fork error" << std::endl;
    exit(-1);
}
//父进程退出,子进程独立运行
else if (pid > 0) 
{
    exit(0);
}

调用fork,然后使父进程exit,这样做的目的是:
1、如果该进程是作为一条简单的 shell 命令启动的,那么父进程终止会让 shell 认为这条命令已经执行完毕。
2、虽然子进程继承了父进程的进程组ID,但获得了一个新的进程ID,这样就保证了 子进程不是一个进程组的组长进程,这是下面将要进行的setsid调用的先决条件。

脱离控制终端,登录会话和进程组
setsid();
signal(SIG_HUP, SIG_IGN);
if ((pid = fork()) < 0)
{
    std::cout << "fork error" << std::endl;
    exit(-1);
}
else if (pid != 0)
{
    exit(0);
}

关于setsid()

pid_t setsid();    // 成功返回进程组ID,出错返回-1
image.png

如果调用此函数的进程不是一个进程组的组长,则此函数创建一个新会话,具体会发生下列3件事:
1、该进程变成新会话的会话首进程
2、成为一个新进程组的组长进程,组ID就是该调用进程的ID
3、该进程没有控制终端

如果该调用进程已经是一个进程组的组长进程,则函数返回出错,为了便面这种情况,通常先调用fork,然后使其父进程终止,子进程继续。

改变当前的工作目录

更改工作目录,从父进程继承过来的当前工作目录可能在 一个挂载的文件系统中,如果守护进程的当前目录挂载在一个文件系统中,那么该文件系统就不能被卸载。

chdir("/");
关闭打开的文件描述符
int fd = open("/dev/null", O_RDWR, 0);
if (fd != -1)
{
    dup2(fd, STDIN_FILENO);
    dup2(fd, STDOUT_FILENO);
    dup2(fd, STDERR_FILENO);
}
if (fd > 2)
{
    close(fd);
}
处理 SIGCHLD 信号
signal(SIGCHLD, SIG_IGN);
完整实现
#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>

void daemon_run()
{
    pid_t pid = fork();
    if (pid < 0)
    {
        std::cout << "fork error" << std::endl;
        exit(-1);
    }
    else if (pid > 0)
    {
        exit(0);
    }

    pid_t group = setsid();
    if (group < 0)
    {
        std::cout << "setsid error" << std::endl;
        exit(-1);
    }

    if ((pid = fork()) < 0)
    {
        std::cout << "fork error" << std::endl;
        exit(-1);
    }
    else if (pid != 0)
    {
        exit(0);
    }

    int fd = open("/dev/null", O_RDWR, 0);
    if (fd != -1)
    {
        dup2(fd, STDIN_FILENO);
        dup2(fd, STDOUT_FILENO);
        dup2(fd, STDERR_FILENO);
    }
    if (fd > 2)
    {
        close(fd);
    }

    signal(SIGHUP, SIG_IGN);
    signal(SIGCHLD, SIG_IGN);
}

int main(int argc, char **argv)
{
    int ch;
    bool b_daemon = false;
    while ((ch = getopt(argc, argv, "d")) != -1)
    {
        switch (ch)
        {
        case 'd':
            b_daemon = true;
            break;
        }
    }

    if (b_daemon)
    {
        daemon_run();
    }

    while (1)
    {
    }
}

参考资料
1、《UNIX环境高级编程》
2、https://blog.csdn.net/lianghe_work/article/details/47659889

上一篇 下一篇

猜你喜欢

热点阅读