13(守护进程)

13.2 守护进程的特征

守护进程是一种纯粹的后台进程,与运行前环境完全隔离,包括未关闭的文件描述符、控制终端、会话、进程组、工作目录以及文件创建掩码等
很多守护进程是父进程 fork 产生,所以会继承所有的父进程地址空间中的环境,所以必须在守护进程诞生之初,断绝这些相关环境,当然,守护进程也可以在 linux 系统启动时从启动脚本 /etc/rc.d 中启动,也可以由 crontab 启动
事实上,守护进程与普通进程的编写并没有特别大的区别

13.3 编程规则

(1)进程从创建他的父进程那里继承了文件创建掩码,它可能会修改守护进程创建的文件的存取位。因此需要调用:umask(0)
(2)调用fork(),然后是父进程退出exit()。这样做实现了以下两点:第一,如果该守护进程是通过shell命令启动的,那么父进程终止使得shell认为该命令已执行完毕。第二,子进程继承了父进程的进程组ID,但有一个新的进程ID,这就保证了子进程不是组长进程。
(3)调用setsid()以创建一个新会话,使调用进程(a)成为新会话的首进程(b)成为一个新进程组的组长进程(c)没有控制终端
(4)将当前目录更改为根目录。调用int chdir (const char *path);
(5)关闭打开的文件描述符。进程从创建它的父进程那里继承了打开的文件描述符,如不关闭,将会浪费系统资源。可以通过open_max()和getrlimit()来判断最高文件描述符值,并关闭直到该值的所有文件描述符。
(6)重定向标准输入、标准输出、错误输出
示例

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h> // for exit
#include <signal.h> // for sigaction
#include <syslog.h> // for syslog
#include <sys/stat.h> // for umask
#include <sys/resource.h>
void daemonize (const char *cmd)
{
    int             i, fd0, fd1, fd2;
    pid_t           pid;
    struct rlimit       rl;
    struct sigaction    sa;

    // (1)更改文件权限屏蔽字 => 不屏蔽
    umask(0);

    if (getrlimit(RLIMIT_NOFILE, &rl) < 0)//得到最高文件描述符值
    {
        perror("getrlimit");
        exit(-1);
    }

    //(2)调用fork(),然后是父进程退出exit()
    if ((pid = fork()) < 0)//
    {
        perror("fork");
        exit(-1);
    }
    else if (pid != 0)
    {
        // 父进程退出
        exit(0);
    }

    //(3)调用setsid()创建一个新会话
    // 1. 脱离控制终端
    // 2. 成为组长进程
    // 3. 成为会话组首个进程
    setsid();

    // 忽略 SIGHUP 信号
    // 当终端退出,会发送该信号给会话组长进程,默认处理方式为退出
    sa.sa_handler = SIG_IGN;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;

    if (sigaction(SIGHUP, &sa, NULL) < 0)
    {
        perror("sigaction");
        exit(-1);
    }

    // 再次 fork 让守护进程不再担当会话组长,防止他重新打开终端
    if ((pid = fork()) < 0)
    {
        perror("fork");
        exit(-1);
    }
    if (pid != 0)
    {
        // 父进程退出
        printf("%d\n", pid);
        exit(0);
    }

    if (chdir("/") < 0)//(4)将当前目录更改为根目录
    {
        perror("chdir");
        exit(-1);
    }

    // (5)关闭所有文件描述符
    if (rl.rlim_max == RLIM_INFINITY)
        rl.rlim_max = 1024;
    for (i=0; i<rl.rlim_max; ++i)
        close(i);

    // 重定向标准输入、标准输出、错误输出到 /dev/null(5)
    fd0 = open("/dev/null", O_RDWR);
    fd1 = dup(0);
    fd2 = dup(0);

    openlog(cmd, LOG_CONS, LOG_DAEMON);
    if (fd0 != 0 || fd1 != 1 || fd2 != 2)
    {
        syslog(LOG_ERR, "unexpected file descriptors %d %d %d",
                fd0, fd1, fd2);
        exit(-1);
    }
    while (1)
    {
        // TODO
    }
}
int main (int argc, char *argv[])
{
    daemonize(argv[0]);
    return 0;
}

你可能感兴趣的:(13(守护进程))