寫守護進程時, 須要fork兩次嗎?

glibc源碼中我找到了daemon函數的實現:nginx

int
daemon(nochdir, noclose)
    int nochdir, noclose;
{
    int fd;

    switch (fork()) {
    case -1:
        return (-1);
    case 0:
        break;
    default:
        _exit(0);
    }

    if (__setsid() == -1)
        return (-1);

    if (!nochdir)
        (void)__chdir("/");

    if (!noclose && (fd = __open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
        (void)__dup2(fd, STDIN_FILENO);
        (void)__dup2(fd, STDOUT_FILENO);
        (void)__dup2(fd, STDERR_FILENO);
        if (fd > 2)
            (void)__close (fd);
    }
    return (0);
}

這個把普通進程變成守護進程的函數,很明顯只fork了一次. 一樣的代碼還有nginx:編程

/*
 * Copyright (C) Igor Sysoev
 * Copyright (C) Nginx, Inc.
 */


#include <ngx_config.h>
#include <ngx_core.h>


ngx_int_t
ngx_daemon(ngx_log_t *log)
{
    int  fd;

    switch (fork()) {
    case -1:
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "fork() failed");
        return NGX_ERROR;

    case 0:
        break;

    default:
        exit(0);
    }

    ngx_pid = ngx_getpid();

    if (setsid() == -1) {
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "setsid() failed");
        return NGX_ERROR;
    }

    umask(0);

    fd = open("/dev/null", O_RDWR);
    if (fd == -1) {
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
                      "open(\"/dev/null\") failed");
        return NGX_ERROR;
    }

    if (dup2(fd, STDIN_FILENO) == -1) {
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDIN) failed");
        return NGX_ERROR;
    }

    if (dup2(fd, STDOUT_FILENO) == -1) {
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDOUT) failed");
        return NGX_ERROR;
    }

#if 0
    if (dup2(fd, STDERR_FILENO) == -1) {
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDERR) failed");
        return NGX_ERROR;
    }
#endif

    if (fd > STDERR_FILENO) {
        if (close(fd) == -1) {
            ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "close() failed");
            return NGX_ERROR;
        }
    }

    return NGX_OK;
}

也只fork了一次, 那爲何有的文章中卻說要fork兩次呢?session

分析以下:函數

第一次fork的做用是爲setsid服務的, 由於執行setsid的進程不能是session leader, 因此fork一個子進程, 在子進程裏進行setsid動做.ui

並且第一次fork後, 咱們已經結束掉了父進程, 子進程已經變成了孤兒進程, 掛靠在init進程下了. 那第二次fork還有必要嗎?this

那在unix高級環境編程 第13章是這樣解釋的:翻譯

Under System V–based systems, some people recommend calling fork again at this point and having the parent terminate. The second child continues as the daemon. This guarantees that the daemon is not a session leader, which prevents it from acquiring a controlling terminal under the System V rules (Section 9.6). Alternatively, to avoid acquiring a controlling terminal, be sure to specify O_NOCTTY
whenever opening a terminal device.unix

簡單翻譯一下:code

在基於System V的系統中, 有些人推薦再fork一次, 這些fork產生的進程就再也不是session leader了, 避免打開控制終端. 還有一種可選的方法,就是打開終端設備的時候指定O_NOCTTY來避免打開控制終端.進程

因此在寫守護進程時, fork兩次並非必須的.

相關文章
相關標籤/搜索