在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
两次并非必须的.