Linux 下创建简单的守护进程(Daemon)

守护进程运行在背景,其父进程 pid=1(init/systemd). 创建守护进程的主要思路,就是 fork 一个子进程,然后父进程挂掉让子进程变为孤儿,最终孤儿被 pid=1 的进程领养。

Daemon 的创建步骤 (SysV)

1. Fork
fork off the parent process & let it terminate if forking was successful. -> Because the parent process has terminated, the child process now runs in the background.

2. Setsid
setsid - Create a new session. The calling process becomes the leader of the new session and the process group leader of the new process group. The process is now detached from its controlling terminal (CTTY).

3. Signal
Catch signals - Ignore and/or handle signals.

4. Fork again
fork again & let the parent process terminate to ensure that you get rid of the session leading process. (Only session leaders may get a TTY again.)

5. chdir
chdir - Change the working directory of the daemon.

6. umask
umask - Change the file mode mask according to the needs of the daemon.

7. Close FDs
close - Close all open file descriptors that may be inherited from the parent process.

引用 Pascal Werkl 的示例代码:

//By Pascal Werkl
/*
 * daemonize.c
 * This example daemonizes a process, writes a few log messages,
 * sleeps 20 seconds and terminates afterwards.
 */

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

static void skeleton_daemon()
{
    pid_t pid;

    /* Fork off the parent process */
    pid = fork();

    /* An error occurred */
    if (pid < 0)
        exit(EXIT_FAILURE);

    /* Success: Let the parent terminate */
    if (pid > 0)
        exit(EXIT_SUCCESS);

    /* On success: The child process becomes session leader */
    if (setsid() < 0)
        exit(EXIT_FAILURE);

    /* Catch, ignore and handle signals */
    //TODO: Implement a working signal handler */
    signal(SIGCHLD, SIG_IGN);
    signal(SIGHUP, SIG_IGN);

    /* Fork off for the second time*/
    pid = fork();

    /* An error occurred */
    if (pid < 0)
        exit(EXIT_FAILURE);

    /* Success: Let the parent terminate */
    if (pid > 0)
        exit(EXIT_SUCCESS);

    /* Set new file permissions */
    umask(0);

    /* Change the working directory to the root directory */
    /* or another appropriated directory */
    chdir("/");

    /* Close all open file descriptors */
    int x;
    for (x = sysconf(_SC_OPEN_MAX); x>0; x--)
    {
        close (x);
    }

    /* Open the log file */
    openlog ("firstdaemon", LOG_PID, LOG_DAEMON);
}

int main()
{
    skeleton_daemon();

    while (1)
    {
        //TODO: Insert daemon code here.
        syslog (LOG_NOTICE, "First daemon started.");
        sleep (20);
        break;
    }

    syslog (LOG_NOTICE, "First daemon terminated.");
    closelog();

    return EXIT_SUCCESS;
}

Linux 中的 daemon()调用

在 Linux 中,我们可以调用 daemon() 来创建守护进程。不过该方法并非 POSIX 定义,所以如果代码需要在非 Linux 平台运行,可能会出现与 Linux 平台不一致的行为。

The daemon function is not defined in POSIX, so its implementation (if any) could behave differently on different platforms.

On Linux with glibc, daemon only does one fork, optionally chdirs (but only to /, you can't specify a path), does not touch umask, and does not close the std* descriptors (it optionally reopens them to /dev/null though). (source)

So it depends on the platform, and at least one implementation does less than what you do. If you need all of what you're doing, stick with that (or stick to a platform where the daemon function does exactly that).

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


int main()
{
    daemon(0,0);

    openlog ("linuxdaemon", LOG_PID, LOG_DAEMON);
    while (1)
    {
        //TODO: Insert daemon code here.
        syslog (LOG_NOTICE, "First daemon started.");
        sleep (20);
        break;
    }

    syslog (LOG_NOTICE, "First daemon terminated.");
    closelog();

    return EXIT_SUCCESS;
}

Systemd 的 daemon

Systemd 有自己的一套 Systemd-style daemon, 据说是为了加快服务启动速度而设计的。关于 Systemd 的守护进程,可以参考 man daemon (在 RHEL7/CentOS7 可以 man 到一篇很详细的文档。)

参考资料

http://cjh.polyplex.org/software/daemon.pdf

Linux Daemon Writing HOWTO
http://www.netzmafia.de/skripten/unix/linux-daemon-howto.html

Creating a daemon in Linux
http://stackoverflow.com/questions/17954432/creating-a-daemon-in-linux

Using Daemon to create Linux services in 5 minutes
http://blog.terminal.com/using-daemon-to-daemonize-your-programs/

What's the difference between calling daemon() and calling fork(), setsid(), fork(), etc.?
http://stackoverflow.com/questions/7645378/whats-the-difference-between-calling-daemon-and-calling-fork-setsid-for