Daemon 是运行在后台的进程,俗称守护进程,常见的 apache, nginx 和 mysql 等都是以守护进程的形式运行。本文主要介绍其的原理以及如何优雅地运行一个 daemon。

The theory

Advanced Programming in the Unix Environmen 第三版本的 13 章详细的介绍了 daemon 的原理、特征和编程规则。它通常具有以下特点:

  • 父进程多数为 1,父进程为 0 的进程通常是内核进程
  • 无控制终端,即不受终端的控制
  • 多数以超级用户特权运行

创建 daemon 的过程如下:

  • 设置 umask 为 0,避免因父进程权限小而引起权限相关问题
  • 调用 fork 创建子进程,之后父进程退出,子进程继续运行
  • 调用 setsid 创建新会话并成为新进程组组长和新会话首进程,同时失去控制终端
  • 把工作目录改为根目录
  • 关闭不需要的文件描述符
  • 把标准输出和错误重定向到 /dev/null,并且以 /dev/null 作为标准输入,隔绝与终端的交互。

The simple way

以下是一种运行 daemon 简单的方法:

nohup COMMAND >/dev/null 2>&1 &

>/dev/null 2>&1

先介绍 Linux shell 的一些基本概念,如文件描述符重定向

  • 0: 标准输入的文件描述符
  • 1: 标准输出的文件描述符,如果没有明确指出,那么通常 1 为默认值,即标准输出
  • 2: 标准错误输出的文件描述符
  • >: 重定向符
  • /dev/null: 空设备文件,它丢弃一切写入其中的数据,但会报告写入成功

所以 >/dev/null 2>&1 的含义如下:

  • >/dev/null 因没有明确指出文件描述符,所以默认为标准输出,即把标准输出重定向到 /dev/null
  • 2>&1,标准错误重定向到标准输入,因标准输出被重定向到 /dev/null,所以标准错误最终也会被重定向到 /dev/null
+---------+                    +------------+   >   +-----------+
|         | ----------------   | standout 1 | ----> | /dev/null |
|         |            +--->   +------------+       +-----------+
| Program |            | >
|         |            |
|         |      +------------+
|         | ---- | standerr 2 |
+---------+      +------------+

以下是一些功能等价的命令,都是把标准输出和标准错误重定向到 /dev/null 中,更多的细节请参考 Adanced Bash-Scripting Guide

  • >/dev/null 2>/dev/null
  • &>/dev/null
  • >/dev/null 2>&-

&

如果某个 Command 以 & 结尾,shell 会在后台执行该命令并且立马返回,不需要等到命令执行完。

nohup

忽略 SIGHUP 信号,避免因父 shell 退出而导致 COMMAND 启动的子进程被杀死。


The better way

优秀从模仿开始,欲知如何优雅的运行一个 daemon,我们应该分析 linux 的做法,linux 环境下通常用 service 启动各种 daemon:

service apache start
service sshd start
service nginx start

......

Service 最终调用 /etc/inti.d/ 目录下的脚本启动相应的服务,这些脚本就是我们的学习样本,以 nginx 为例,精简版的脚本如下:

. /lib/lsb/init-functions

case "$1" in
  start)
    start-stop-daemon --start --quiet --pidfile /run/nignx.pid --exec /usr/sbin/nginx 2>/dev/null
    ;;
  stop)
    start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile /run/nignx.pid --name nginx
    ;;
esac

exit 0

和上节的方法相比,通过 service 命令具有以下优点:

  • 操作简洁,友好规范
  • 完整的生命周期管理,支持 start/stop/restart/status 操作
  • 可在脚本中做前置工作检查、后置清理等

另一件很有趣的事情就是绝大多数 /etc/init.d 下的守护进程都是以 start-stop-daemon 命令启动,和 nohup 相比,它的功能更为丰富,支持启动和杀死 daemon,支持后台运行,支持以某个用户的身份运行等等,更多信息请见 man start-stop-daemon