守护进程

守护进程的一些原理和方法。

首先来看几个概念:

  1. 守护进程:daemon,运行在后台的特殊进程,是生存期长的一种进程,它独立于控制终端并且周期性的执行某种任务或等待处理某些发生的事件。
  2. 前台进程:在多任务操作系统(诸如 UNIX/Linux)中,前台进程是用户当前与之交互的程序(例如,数据输入)。随着用户在程序之间切换,会导致这些程序在不同的时刻处于前台。在层叠的窗口环境中,前台进程是最前面的窗口。在命令行中,默认程序和命令都作为前台进程运行,要在后台运行,需要加&符号。
  3. 进程组:一个或者多个进程的集合。每个进程组有一个组长进程
  4. 会话(session):一个或多个进程组的集合。一个会话可以有一个控制终端,通常是终端设备。会话通常是一个登录进程创建的,创建会话的进程为会话首进程(session leader),只有session leader才能控制终端。

那么,成为一个守护进程的关键是什么?

脱离终端,不继承标准输入。脱离终端的目的是避免进程在运行过程中的信息在任何终端中显示,并且进程也不会被任何终端所产生的终端信息打断。

如何命令行启动一个后台进程

可以参考这篇Linux 守护进程的启动方法
这里简单说下里面的nohup,比如:nohup php 1.php 2>&1 &
nohup表示挂起,标准输入会重定向到/dev/null,并且忽略sigup信号,它并不会让进程变为后台任务,所以需要在命令行末尾加上&符号。

  • 2表示标准错误(stderr)
  • ‘>’表示重定向
  • 1表示标准输出(stdout)
  • 0表示标准输入(stdin)

2>&1整个意思是:将标准错误重定向到标准输出,在大于号后面必须加上&符号,否则,它只会重定向到一个名为1的文件。
如果没有2>&1,标准输出会以nohup.out文件输出。

程序如何实现守护进程呢

  1. 使用umask重设文件创建掩码,通常设为0。这是为了创建文件后,设置更多权限。
  2. 调用fork创建子进程,然后父进程退出。fork得来的子进程复制了父进程的环境,最重要的是这样子进程不是session leader了,也不是进程组的组长进程。这是下面setsid的条件。
  3. setsid创建新会话。这样是为了摆脱控制终端。但是这样子进程就成为了新session的session leader,而只有session leader才能控制终端。假如它打开一个终端设备文件,比如/dev/console,就有可能控制终端。所以也有人会在此时再次fork来保证进程不是session leader,从而没有可能控制终端。还有一种避免取得控制终端的方式是:open后面加上O_NOCTTY参数,比如int fd = open(“/dev/console”,O_NOCTTY);
  4. 更改当前工作目录到根目录。这个其实只要你的当前工作目录不会被卸载就行。
  5. 关闭不再需要的文件描述符
  6. 有些可能会将0,1,2重定向到/dev/null,使标准输入输出错误无效。

但是,还留下几个问题

  1. fork之后,父进程退出,子进程脱离了控制终端吗?如果脱离了,那为什么还要setsid呢?如果没有脱离,子进程已经不是session leader了,而只有session leader才能控制终端。
  2. 终端退出时,发生了什么,何时发送signup信号

参考资料:
http://stackoverflow.com/questions/881388/what-is-the-reason-for-performing-a-double-fork-when-creating-a-daemon
http://stackoverflow.com/questions/10932592/why-fork-twice/16655124#16655124
http://www.win.tue.nl/~aeb/linux/lk/lk-10.html
http://stackoverflow.com/questions/32780706/does-linux-kill-background-processes-if-we-close-the-terminal-from-which-it-has
http://blog.csdn.net/ithomer/article/details/9288353
《UNIX环境高级编程》