Linux下的进程(六):守护进程 - HeavyYuan/A-CD-Record-Management-System GitHub Wiki

一、成为守护进程

守护进程(Daemon)的生命周期是系统的生命周期,其在后台运行,没有控制终端。

普通进程成为守护进程的过程,称为daemonize

1. 设定umask

一般会设置成0,也可以根据需求自行设定。

2. 调用fork, 然后使父进程exit

1)通常情况下,用户是从shell下通过命令启动了守护进程,我们需要这条命令快速返回,而父进程exit可以满足这一点。 2)fork之后,子子进程不是进程组组长,为后续setsid做铺垫

3. 调用setsid创建新会话

只有进程组组长,才能调用会话id

上诉子进程调用setsid,成为会话首进程,并且切断与已有的控制终端的连接(表现为tpgrp = -1)

此时,该子进程已经成为后台进程。

4. 再次fork,父进程退出

守护进程都没有控制终端,第3点的子进程是会话首进程,其可以获取到控制终端

为了避免其再次获取控制终端,需要再次fork,此时子进程就不在是会话首进程了。

5. 更改当前工作目录

从父进程继承的当前工作目录可能在一个挂载的文件系统中,所以该文件系统不能被卸载

一般做法是,将当前工作目录改成为根目录,或者是一个指定的目录.

6. 关闭继承父进程的所有文件描述符

因为没有exec,所以close_on_exec标志不能使用

这里方法是,在fork之前获取最高文件描述符值(getrlimit), fork之后全部关闭。

7. 将描述符0,1,2 attach 到/dev/null

后续例程在读写标准输入、输出、错误时,没有任何效果

此时的进程已经没有控制终端,对使用终端的用户不构成影响。所以个人任务此步骤不是很必要。

8. 初始化日志输出(rsyslog)

此时无法输出到终端,需要有日志途径

二、单例模式

通过创建锁文件来实现,如:/var/run/daemon.pid

文件中是daemon的进程ID

fd = open(LOCKFILE, O_RDWR|O_CREAT, LOCKMODE);
    if (fd < 0) {
        syslog(LOG_ERR, "can't open %s: %s", LOCKFILE, strerror(errno));
        exit(1);
    }
if (flock(fd,LOCK_EX|LOCK_NB) < 0) {
        if (errno == EWOULDBLOCK) { /*locked*/
            close(fd);
            return(1);
        }
        syslog(LOG_ERR, "can't lock %s: %s", LOCKFILE, strerror(errno));
        exit(1);
}

三、守护进程的惯例

  1. 锁文件用/var/run/procg_name.pid
  2. 配置文件在/etc/name.conf
  3. 可以手动命令行启动 or 系统初始化脚本(/etc/rc* 或 /etc/init.d/*)启动
  4. 通过信号处理来重读配置文件