APUE中为何创建守护进程时,为何需要2次fork?
2013-03-28 23:15
363 查看
1. 创建守护进程的一般步骤(参考链接:http://learn.akae.cn/media/ch34s03.html)
1. 创建守护进程的一般步骤(参考链接:http://learn.akae.cn/media/ch34s03.html)
解释:
(1). 第一次调用fork,是为了保证后面的setsid能够成功,因为调用setsid成功的前提是当前进程不能是进程组的Leader,否则返回-1,要保证当前进程不是进程组首进程,只需要掉用fork在调用setsid就可以了。fork创建的子进程和父进程同属于一个进程组,进程组的leader必然是第一个进程,而子进程不可能是改组的第一个进程,在子进程中调用setsid就不会有问题了。
(2). 调用setsid()
调用该函数的的结果是:
使调用进程(1)成为新会话(session)的首进程,(2)成为一个新进程组的组长(leader),(3)没有控制终端。
在执行setsid()后,APUE提供的源码进行了第二次fork(),这次fork是必须的么?
APUE给的解释如下:
回到APUE中第9.6节,原书给出的解释如下:
参考链接:http://webcache.googleusercontent.com/search?q=cache:XGMOwrA0CNIJ:jessinio.blogspot.com/2010/05/daemonizefork.html+linux+daemon+%E4%B8%BA%E4%BD%95%E8%A6%812%E6%AC%A1fork&cd=9&hl=zh-CN&ct=clnk&gl=cn
fork第二次是防止被在打开一个terminal device时被系统分配到controlling terminal
只要不乱open(或者使用O_NOCTTY是不需要fork第二次的).
话又说回来, linux是怎么分配controlling terminal的呢?
APUE在这里没有提到linux, 只是提到了BSD和system-V的不同. 搜了一下, linux和BSD是一样的, 使用ioctl得到controlling terminal, URL: http://linux.die.net/man/4/tty_ioctl
TIOCSCTTY int arg Make the given tty the controlling tty of the current process.
总结如下:
第二次fork不是必须的,加上它是为了system-V系统兼容性的考虑。
(1)在System-V的系统下,在调用open时没有指定O_NOCTTY标志,当会话首进程打开第一个尚未与一个会话相关联的终端设备时,System-V系统将此终端作为控制终端分配此会话。
(2)在Free-BSD系统中,当会话首进程采用TIOCSSCTTY作为request参数(第3个参数是空指针)调用ioctl时,基于BSD的系统会为会话分配控制终端。
APUE的daemon例程:
#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); }
1. 创建守护进程的一般步骤(参考链接:http://learn.akae.cn/media/ch34s03.html)
解释:
(1). 第一次调用fork,是为了保证后面的setsid能够成功,因为调用setsid成功的前提是当前进程不能是进程组的Leader,否则返回-1,要保证当前进程不是进程组首进程,只需要掉用fork在调用setsid就可以了。fork创建的子进程和父进程同属于一个进程组,进程组的leader必然是第一个进程,而子进程不可能是改组的第一个进程,在子进程中调用setsid就不会有问题了。
(2). 调用setsid()
调用该函数的的结果是:
使调用进程(1)成为新会话(session)的首进程,(2)成为一个新进程组的组长(leader),(3)没有控制终端。
在执行setsid()后,APUE提供的源码进行了第二次fork(),这次fork是必须的么?
APUE给的解释如下:
在基于系统V的系统中,有些人建议在此时再调用fork,并使父进程终止。第二个子进程作为精灵进程继续运行。这样就保证了该精灵进程不是对话期的首进程,于是按照SVR规则(见9.6节)可以防止它取得控制终端。另一方面,为了避免取得控制终端,无论何时打开一个终端设备都要指定O_NOCTTY。 |
|
fork第二次是防止被在打开一个terminal device时被系统分配到controlling terminal
只要不乱open(或者使用O_NOCTTY是不需要fork第二次的).
话又说回来, linux是怎么分配controlling terminal的呢?
APUE在这里没有提到linux, 只是提到了BSD和system-V的不同. 搜了一下, linux和BSD是一样的, 使用ioctl得到controlling terminal, URL: http://linux.die.net/man/4/tty_ioctl
TIOCSCTTY int arg Make the given tty the controlling tty of the current process.
总结如下:
第二次fork不是必须的,加上它是为了system-V系统兼容性的考虑。
(1)在System-V的系统下,在调用open时没有指定O_NOCTTY标志,当会话首进程打开第一个尚未与一个会话相关联的终端设备时,System-V系统将此终端作为控制终端分配此会话。
(2)在Free-BSD系统中,当会话首进程采用TIOCSSCTTY作为request参数(第3个参数是空指针)调用ioctl时,基于BSD的系统会为会话分配控制终端。
|
#include "apue.h" #include <syslog.h> #include <fcntl.h> #include <sys/resource.h> void daemonize(const char *cmd) { int i, fd0, fd1, fd2; pid_t pid; struct rlimit rl; struct sigaction sa; /* * Clear file creation mask. */ umask(0); /* * Get maximum number of file descriptors. */ if (getrlimit(RLIMIT_NOFILE, &rl) < 0) err_quit("%s: can't get file limit", cmd); /* * Become a session leader to lose controlling TTY. */ if ((pid = fork()) < 0) err_quit("%s: can't fork", cmd); else if (pid != 0) /* parent */ exit(0); setsid(); /* * Ensure future opens won't allocate controlling TTYs. */ sa.sa_handler = SIG_IGN; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if (sigaction(SIGHUP, &sa, NULL) < 0) err_quit("%s: can't ignore SIGHUP"); if ((pid = fork()) < 0) err_quit("%s: can't fork", cmd); else if (pid != 0) /* parent */ exit(0); /* * Change the current working directory to the root so * we won't prevent file systems from being unmounted. */ if (chdir("/") < 0) err_quit("%s: can't change directory to /"); /* * Close all open file descriptors. */ if (rl.rlim_max == RLIM_INFINITY) rl.rlim_max = 1024; for (i = 0; i < rl.rlim_max; i++) close(i); /* * Attach file descriptors 0, 1, and 2 to /dev/null. */ fd0 = open("/dev/null", O_RDWR); fd1 = dup(0); fd2 = dup(0); /* * Initialize the log file. */ openlog(cmd, LOG_CONS, LOG_DAEMON); if (fd0 != 0 || fd1 != 1 || fd2 != 2) { syslog(LOG_ERR, "unexpected file descriptors %d %d %d", fd0, fd1, fd2); exit(1); } }
相关文章推荐
- 创建守护进程为什仫要fork两次
- 守护进程 & 创建守护进程 & fork一次和fork两次的区别
- 创建守护进程为什么fork两次
- 守护进程&创建守护进程&fork一次和fork两次的区别
- 守护进程&创建守护进程&fork一次和fork两次的区别
- 创建守护进程以及为什么fork两次
- 详解守护进程的创建与fork两次分析
- 守护进程 & 创建守护进程 & fork一次和fork两次的区别
- 如何创建守护进程fork()函数的运用
- Linux——守护进程(精灵进程)创建fork一次.两次的区别
- Daemon进程为何需要两次fork
- 创建守护进程为什么要fork两次
- 创建守护进程fork一次与fork两次的区别
- 创建守护进程为何fork两次
- 创建守护进程为什么fork两次
- 创建简单的守护进程的编写规则
- python 创建进程fork
- Linux进程的管理与调度(八) -- Linux下进程的创建过程分析(_do_fork/do_fork详解)
- C进程创建fork与vfork的区别
- linux守护进程原理及创建详解