How to set Linux Daemon

前台任务与后台任务

在终端启动一个服务程序后,该程序会占用一个终端窗口,称为 前台任务.只有该程序运行完成或者终止程序,才能执行其他命令.

$ node server.js &

在程序后添加符号 & 就可以把它改为后台任务.如果要让一个前台任务变成后台任务,可以先按 ctrl + z,然后执行bg命令,让最后一个暂停的后台任务继续执行.

后台任务的两个特点:

  1. 集成当前对话的标准输出和标准错误.因此,后台任务的所有输出依然会同步的在命令行下显示.
  2. 不再继承当前会话的标准输入.即无法再向该任务输入命令,如果试图读入标准输入,会暂停执行(halt).

前台任务与后台任务的本质区别只有一个:是否继承标准输入.所以执行后台任务时,用户还可以输入其他命令.

Sighup信号

变成后台任务后,一个进程是否成了守护进程呢? 或者说用户退出session后,后台任务是否还会继续执行呢?

Linux系统中是这样设计的:

  1. 用户准备退出session
  2. 系统向该session发出sighup信号
  3. session将sighup信号发给所有子进程
  4. 子进程收到sighup信号后自动退出

上面的流程解释了为什么前台任务会随着session的关闭而退出,因为他收到了sighup信号.那么后台任务会收到sighup信号呢?

这由shell的huponexit参数决定:

$ shopt | grep huponexit

执行上面的命令就会看到huponexit参数的值.

大多数Linux系统中,这个参数默认关闭.因此,session退出的时候,不会吧sighup信号发给后台任务,因此后台任务不会随着session关闭而退出.

disown命令

通过后台任务启动守护进程并不保险,因为有的Linux系统的huponexit参数可能是打开的(on).

更保险的办法是使用disown命令.它可以将指定任务从后台任务列表(jobs命令的返回结果)之中移除.一个后台任务只要不在这个列表中,session就肯定不会向它发出sighup信号.

$ node server.js &
$ disown

执行上面的命令后,server.js进程就被移出了后台任务列表.可以执行jobs命令进行验证,输出结果中不会有这个进程.

disown的用法如下:

# 移出最近一个正在执行的后台任务
$ disown

# 移出所有正在执行的后台任务
$ disown -r

# 移出所有后台任务
$ disown -a

# 不移出后台任务,但是让它们不会收到SIGHUP信号
$ disown -h

# 根据jobId,移出指定的后台任务
$ disown %2
$ disown -h %2

标准I/O

使用disown命令之后,还有一个问题.那即是,退出session之后,如果后台进程与标准IO有交互,它还会挂掉.

还是以上面的脚本为例,下面加上一行:

var http = require('http');

http.createServer(function(req, res) {
  console.log('server starts...'); // 加入此行
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World');
}).listen(5000);

启动上面的脚本,使用disown命令:

$ node server.js &
$ disown

然后退出session范文5000端口,就会发现连不上.

这是因为后台任务的标准IO集成当前session,disown命令并没有改变这一点.一旦后台任务读写标准IO,就会发现他已经不存在了,就会报错终止执行.

为了解决这个问题,需要对后台任务的标准IO进行重定向:

$ node server.js > stdout.txt 2> stderr.txt < /dev/null &
$ disown

上面这样执行基本上就不会有问题了.

nohup命令

还有比disown更方便的命令,就是nohup.

$ nohup node server.js &

nohup对server.js进程做了三件事:

  1. 阻止sighup信号发到这个进程
  2. 关闭标准输入.该进程不再能够就收任何输入,即使运行在前台.
  3. 重定向标准输入和标准错误到nohup.out文件.

也即是说,nohup命令实际上将子进程与他所在的session分离了.

注意,nohup命令不会吧进程变成后台任务,必须加上 & 符号.

Screen命令与Tmux命令

另一种思路是使用terminal multiplexer(终端复用器:在一个终端中管理多个session),典型的就是screen和tmux命令.

他们可以在当店session中创建另一个session.这样的话,当前session一旦结束,不影响其他session.而且,以后重新登录还可以连上早先新建的session.

screen的用法如下:

# 新建一个 session
$ screen
$ node server.js

然后,按下 ctrl + A 和 ctrl + D,回到原来的session.下次登录时再切回去.

$ screen -r

如果新建多个后台session就需要为他们指定名字:

$ screen -S name

# 切回指定 session
$ screen -r name
$ screen -r pid_number

# 列出所有 session
$ screen -ls

如果要停掉某个session,可以先切回它,然后按下 ctrl + C 和 ctrl + D.

tmux比screen功能更多更强大,他的基本用法如下:

$ tmux
$ node server.js

# 返回原来的session
$ tmux detach

除了tmux detach,另一种方法是按下 ctrl + B 和 D, 也可以回到原来的session.

# 下次登录时,返回后台正在运行服务session
$ tmux attach

如果先建多个session,同样需要为每个session指定名字:

# 新建 session
$ tmux new -s session_name

# 切换到指定 session
$ tmux attach -t session_name

# 列出所有 session
$ tmux list-sessions

# 退出当前 session,返回前一个 session 
$ tmux detach

# 杀死指定 session
$ tmux kill-session -t session-name

Node工具

对于Node应用来说,可以不用上面的方法,有一些专门来启动的工具:forever,nodemon,pm2.

forever功能很简单,就是保证进程退出时,应用会自动启动:

# 作为前台任务启动
$ forever server.js

# 作为服务进程启动 
$ forever start app.js

# 停止服务进程
$ forever stop Id

# 重启服务进程
$ forever restart Id

# 监视当前目录的文件变动,一有变动就重启
$ forever -w server.js

# -m 参数指定最多重启次数
$ forever -m 5 server.js 

# 列出所有进程
$ forever list

nodemon一般只在开发时使用,他的长处在于watch功能,一旦文件发生变化就会自动重启进程.

# 默认监视当前目录的文件变化
$ nodemon server.js

# 监视指定文件的变化   
$ nodemon --watch app --watch libs server.js  

pm2的功能最强大,除了重启进程之外还能收集日志和监控:

# 启动应用
$ pm2 start app.js

# 指定同时起多少个进程(由CPU核心数决定),组成一个集群
$ pm2 start app.js -i max

# 列出所有任务
$ pm2 list

# 停止指定任务
$ pm2 stop 0

# 重启指定任务
$ pm2 restart 0

# 删除指定任务
$ pm2 delete 0

# 保存当前的所有任务,以后可以恢复
$ pm2 save

# 列出每个进程的统计数据
$ pm2 monit

# 查看所有日志
$ pm2 logs

# 导出数据
$ pm2 dump

# 重启所有进程
$ pm2 kill
$ pm2 resurect

# 启动web界面 http://localhost:9615
$ pm2 web

Systemd

除了专用工具外,Linux还有自己的守护进程管理工具Systemd.它是操作系统的一部分,直接与内核交互,性能出色,功能极其强大.

来源:阮一峰的网络日志