Linux守护进程

守护进程(daemon)是在后台运行且不与任何控制终端关联的进程。

与终端脱离有两个目的:

  1. 避免进程运行过程中将信息输出到终端
  2. 避免进程被终端产生的信息中断

创建守护进程前首先需要理解几个概念:进程组会话控制终端

 

每个进程除了有一进程ID(PID)之外,还属于一个进程组。

进程组是一个或多个进程的集合,每个进程组有一个进程组ID。

每个进程组有一个组长进程(process group leader),组长进程的PID等于它的进程组ID。

会话(session)是一个或多个进程组的集合。

会话中的多个进程组可被分为一个前台进程组以及多个后台进程组。

一个会话有一个控制终端。

 

进程可以通过调用setsid函数创建一个新会话。

pid_t setsid(void);

有一点需要注意:调用此函数的进程不能是一个进程组的组长。调用该函数会发生以下3件事:

(1) 该进程变成新会话的会话首进程(session leader)

(2) 该进程成为一个新进程组的组长进程

(3) 该进程没有控制终端

创建守护进程的步骤如下:

(1) 创建子进程,父进程退出

    调用fork,然后父进程exit,这么做实现下面两点:第一、如果本进程是从前台作为一个shell命令启动的,当父进程终止时,shell就认为该命令已经执行完毕,这样子进程就自动在后台运行。第二、子进程继承了父进程的进程组ID,而且它有自己的进程ID,这就保证子进程不是一个进程组的组长进程,这是接下去调用setsid函数的必要条件。

(2) 调用setsid创建一个新会话

    步骤1中的子进程继承了父进程的进程组、会话、控制终端,调用setsid函数会创建新的会话、新的进程组,而且没有控制终端。

(3) 忽略SIGHUP信号并再次fork

    步骤2中的子进程是一个会话首进程,当没有控制终端的会话首进程打开一个终端设备时,该终端自动成为这个会话首进程的控制终端。再次fork可以确保新的子进程不再是一个会话首进程,从而不能自动获得一个控制终端。这里必须忽略SIGHUP信号,因为当会话首进程终止时,其会话中的所有进程(包括再次fork产生的子进程)都会受到SIGHUP信号。

(4) 将当前工作目录更改为根目录

(5) 重设文件权限掩码

(6) 关闭所有打开的文件描述符 

(7) 重定向stdin、stdout、stderr

下面给一个具体的例子:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <time.h>

#define    MAXFD    64

void daemon_init() {
    pid_t pid;
    if ( (pid = fork()) < 0) {
        perror("fork error");
        exit(1);
    } else if (pid){
        exit(0);
    }
    // child 1
    if (setsid() < 0) {
        perror("setsid error");
        exit(1);
    }
    signal(SIGHUP, SIG_IGN);
    if ( (pid = fork()) < 0) {
        exit(1);
    } else if (pid){
        exit(0);
    }
    // child 2
    chdir("/");
    umask(0);
    for (int i = 0; i < MAXFD; i++) {
        close(i);
    }
    open("/dev/null", O_RDONLY);
    open("/dev/null", O_RDWR);
    open("/dev/null", O_RDWR);
}

int main(int argc, char **argv) {
    
    daemon_init();
    
    FILE *fp;
    time_t t;
    while (true) {
        if ( fp = fopen("/tmp/daemon.log", "a") ) {
            t = time(0);
            fprintf(fp, "current time is: %s", asctime(localtime(&t)));
            fclose(fp);
        }
        sleep(1);
    }
} 

参考文章:

Linux进程关系

守护进程

守护进程的创建方法和步骤

linux系统编程之进程(八):守护进程详解及创建,daemon()使用

原文地址:https://www.cnblogs.com/gattaca/p/6413257.html