在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
兩次並非必須的.