74-守护进程(实现)

前面已经介绍了守护进程的基本概念,还介绍了一个系统为我们提供的函数 daemon 函数,通过该函数可以创建守护进程。

本文主要分析 daemon 函数是如何实现的。

通过前文的实验结果可以猜测,守护进程的创建步骤必定需要 fork 子进程,创建会话,关闭标准输入、标准输出和标准错误文件描述符。

1. 守护进程编写规则

(1) 设置 umask。
(2) 调用 fork,然后使父进程退出。这样做保证子进程不是进程组组长,为第三步提供保证。
(3) 调用 setsid 创建新会话。这一步可以保证子进程没有控制终端。
(4) 捕获 SIGHUP 信号,防止因为孤儿进程组中的进程收到 SIGHUP 信号而终止。
(5) 切换当前工作目录。(对应 daemon 函数的第一个参数。)
(6) 关闭不再需要的文件描述符。这一步保证标准输入、标准输出和标准错误被关闭。(对应 daemon 函数的第二个参数。)
(7) 将标准输入、标准输出和标准错误定向到文件描述符 0、1 和 2.

当然有些步骤是可选的,比如第 1、4、5 步不写也没多大事。

2. 实现细节

  • 代码
#include <stdio.h>
#include <fcntl.h>
#include <sys/resource.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <signal.h>

void daemonize() {
  pid_t pid;
  int i, fd0, fd1, fd2;
  struct rlimit rl;

  getrlimit(RLIMIT_NOFILE, &rl);

  // 1. 设置 umask
  umask(0);

  // 2. 调用 fork,让父进程退出,保存子进程不是进程组组长
  if ((pid = fork()) < 0) {
    perror("fork");
    exit(-1);
  }
  else if (pid > 0) {
    // 让父进程退出
    exit(0);
  }

  // 3. 调用 setsid 创建新会话
  if (setsid() < 0) {
    perror("setsid");
    exit(-1);
  }

  // 4. 捕获 SIGHUP 信号
  struct sigaction sa; 
  sa.sa_handler = SIG_IGN;
  sigemptyset(&sa.sa_mask);
  sa.sa_flags = 0;
  if (sigaction(SIGHUP, &sa, NULL) < 0) {
    perror("sigaction");
    exit(-1);
  }


  // 5. 更改工作目录
  if (chdir("/") < 0) {
    perror("chdir");
    exit(-1);
  }

  // 6. 关闭不用的描述符,保证标准输入、标准输出和标准错误的文件描述符与控制终端文件脱离关系
  if (rl.rlim_max == RLIM_INFINITY) {
    rl.rlim_max = 1024;
  }
  for (i = 0; i < rl.rlim_max; ++i) {
    close(i);
  }

  // 7. 为守护进程打开 /dev/null,并让描述符 0、1、2 指向它
  fd0 = open("/dev/null", O_RDWR);
  fd1 = dup(0);
  fd2 = dup(0);
}


// 这部分和上一篇的基本差不多,只是把 daemon 函数换成了 daemonize 而已。
int  main() {

  printf("pid: %d, ppid: %d, sid: %d\n", getpid(), getppid(), getsid(getpid()));
  daemonize();

  int fd;
  char buf[256];

  while(1) {

    printf("pid: %d, ppid: %d, sid: %d\n", getpid(), getppid(), getsid(getpid()));
    fd = open("/home/allen/apue/relationship/daemon/test.log", O_WRONLY | O_APPEND | O_CREAT, 0664);
    if (fd > 0) {
      sprintf(buf, "pid: %d, ppid: %d, sid: %d\n", getpid(), getppid(), getsid(getpid()));
      write(fd, buf, strlen(buf));
    }

    sleep(3);
  }
  return 0;
}

  • 编译和运行
$ gcc mydaemon.c -o mydaemon
$ ./mydaemon
  • 运行结果

这里运行结果和《守护进程(概念)》分析的并无二致。

3. 总结

  • 理解守护进程的概念
  • 掌握守护进程的编写规则

练习:按照守护进程编写规则,编写 daemonize 函数。(如果实在不会写,请掌握第 2 节中的代码,并抄写一遍,然后编译运行,查看结果。注意文件路径可能需要更改。)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值