守护进程就是 Linux 系统下常驻内存型进程与用户会话分离的的实现方式。守护进程没有控制终端,不能与用户交互,也不会因为用户终端注销而结束。

# 守护进程的特征

使用 ps axj 命令查看当前系统运行的进程

凡是 TPGID 一栏写着 - 1 的都是没有控制终端的进程,也就是守护进程。在 COMMAND 一列用 "[]" 括起来的名字表示内核线程,这些线程在内核里创建,没有用户空间代码,因此没有程序文件名和命令行,通常采用以 k 开头的名字,表示 Kernel。init 进程我们已经很熟悉了,udevd 负责维护 /dev 目录下的设备文件,acpid 负责电源管理,syslogd 负责维护 /var/log 下的日志文件,可以看出,守护进程通常采用以 d 结尾的名字,表示 Daemon。

# C 语言实现

创建守护进程最关键的一步是调用 setsid 函数创建一个新的 Session,并成为 Session Leader。

c
#include <unistd.h>
pid_t setsid(void);

该函数调用成功时返回新创建的 Session 的 id(其实也就是当前进程的 id),出错返回 - 1。注意,调用这个函数之前,当前进程不允许是进程组的 Leader,否则该函数返回 - 1。要保证当前进程不是进程组的 Leader 也很容易,只要先 fork 再调用 setsid 就行了。fork 创建的子进程和父进程在同一个进程组中,进程组的 Leader 必然是该组的第一个进程,所以子进程不可能是该组的第一个进程,在子进程中调用 setsid 就不会有问题了。

# 代码示例

c
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
void daemonize(void)
{
    pid_t  pid;
    /*
    * Become a session leader to lose controlling TTY.
    */
    if ((pid = fork()) < 0) {
        perror("fork");
        exit(1);
    } else if (pid != 0) /* parent */
        exit(0);
    setsid();
    /*
     * Change the current working directory to the root.
     */
    if (chdir("/") < 0) {
        perror("chdir");
        exit(1);
    } 
    /*
    * Attach file descriptors 0, 1, and 2 to /dev/null.
    */
    close(0);
    open("/dev/null", O_RDWR);
    dup2(0, 1);
    dup2(0, 2);
}
int main(void)
{
    daemonize();
    while(1);
}

为了确保调用 setsid 的进程不是进程组的 Leader,首先 fork 出一个子进程,父进程退出,然后子进程调用 setsid 创建新的 Session,成为守护进程。按照守护进程的惯例,通常将当前工作目录切换到根目录,将文件描述符 0、1、2 重定向到 /dev/null。Linux 也提供了一个库函数 daemon (3) 实现我们的 daemonize 函数的功能,它带两个参数指示要不要切换工作目录到根目录,以及要不要把文件描述符 0、1、2 重定向到 /dev/null。

参考文献:Linux C 编程一站式学习 © 2008, 2009 宋劲杉,北京亚嵌教育研究中心

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

HuaYu 微信支付

微信支付

HuaYu 支付宝

支付宝

HuaYu 贝宝

贝宝