守护线程简介

这篇博客简单介绍了守护进程(daemon process)

什么是守护进程

守护进程一般提供一个服务,比如ssh登录服务,ftp文件服务,他们长时间运行,等待客户
端来调用这个服务

一般的编码规则

守护线程初始时,一般有以下行为

  1. 完全控制生成文件的权限,因此一般会调用umask(0),完全去掉系统默认的权限掩码
  2. 调用fork,生成一个子线程,父线程退出,这样做的目的是:
    a.如果守护线程是一个简单的shell命令,父线程退出后,shell会认为该指令已经执行完毕
    b. 新生成的子线程继承了父进程的线程组ID,保证了子线程不是线程组的领导者,这是执
    行第三步的必要条件
  3. 调用setid创建一个新的会话,这样守护线程就从当前的控制终端中独立出来,不再和
    当前关联
  4. 更改当前工作目录,一般更改到根目录或者是某个特定目录。
    这样做是因为守护线程长期运行,如果不更改目录,会影响当前目录所在磁盘的卸载和重新
    挂载
  5. 关闭不使用的文件描述符
  6. 将标准输入流、输出流、错误流都定向到/dev/null,因为守护线程不关联任何控制终端
    标准输入、输出、错误流本身就是无意义的

日志

守护进程是后台进程,为了便于管理,系统提供了标准的日志输出函数,直接调用即可

单例守护进程

有些守护进程要求在同一时刻,只能有一个实例在运行。要实现此功能,可以利用file和
record-locking机制

例子

#include <stdio.h>
#include <errno.h>
#include <syslog.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/resource.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>

void
daemonize(const char *cmd)
{
    int fd0, fd1, fd2;
    pid_t pid;
    struct rlimit rl;
    struct sigaction sa;

    /**
     * clear file creation mask
     */
    umask(0);

    /**
     * Get maximum number of file descriptors
     */
    if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
    {
        perror("can't get file limit");
        exit(1);
    }


    /**
     * Became a session leader to lose controlling TTY
     */
    if ((pid = fork()) < 0)
    {
        perror("fail to fork");
        exit(0);
    } else if (pid != 0) {
        /*parent*/
        exit(0);
    }
    setsid();

    /*
    * Initialize the log file.
    */
    openlog(cmd, LOG_CONS, LOG_DAEMON);

    /**
     * Change the current working directory to the root so
     * we won’t prevent file systems from being unmounted.
     */
    if (chdir("/") < 0)
    {
        syslog(LOG_ERR, "fail to change dir");
        exit(1);
    }

    /**
     * Close all open file descriptors
     */
    if (rl.rlim_max == RLIM_INFINITY)
    {
        rl.rlim_max = 1024;
    }
    for (int i = 0; i < rl.rlim_max; i++)
        close(i);

    /**
     * Attach file descriptor 0, 1, 2 to /dev/null
     */
    fd0 = open("/dev/null", O_RDWR);
    fd1 = dup(fd0);
    fd2 = dup(fd0);

    if (fd0 != 0 || fd1 != 1 || fd2 != 2) {
        syslog(LOG_ERR, "unexpected file descriptors %d %d %d",
        fd0, fd1, fd2);
        exit(1);
    }

    /**
     * write pid to log
     */
    syslog(LOG_INFO, "pid is %d", getpid());
}

int
main(int argc, char *argv[])
{
    daemonize(argv[0]);
    sleep(60);
    syslog(LOG_INFO, "exit");
}

上述程序运行后,就会自动进入后台运行,运行过程中记录了进程的pid,这些日志记录到
了系统日志里,在使用systemd管理守护进程的系统,可以试用systemctl查看日志。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。