Linux 后台运行程序
Table of Contents
1 前台任务与后台任务
前台任务 ( foreground job ) 会独占命令行窗口,只有运行完了或者手动中止,才能执行其他命令。
变成守护进程的第一步,就是把它改成 后台任务 ( background job )。
mv a ../b &
只要在命令的尾部加上符号 & ,启动的进程就会成为后台任务。
让正在运行的前台任务变为后台任务,参考这个操作流程:
mv a ../b
任务启动。Ctrl + z
任务挂起。jobs
查看所有任务。bg %1
把标号为 1 的任务放入后台。disown -h %1
把标号为 1 的任务交给系统后台。
后台任务有两个特点:
- 继承当前 session ( 对话 ) 的标准输出 ( stdout ) 和标准错误 ( stderr )。因此,后台任务的所有输出依然会同步地在命令行下显示。
- 不再继承当前 session 的标准输入 ( stdin )。你无法向这个任务输入指令了。如果它试图读取标准输入,就会暂停执行 ( halt )。
后台任务与前台任务的本质区别只有一个: 是否继承标准输入。
所以,执行后台任务的同时,用户还可以输入其他命令。
2 SIGHUP 信号
Linux 系统有这样一个设计:
- 用户准备退出 session;
- 系统向该 session 发出 SIGHUP 信号;
- session 将 SIGHUP 信号发给所有子进程;
- 子进程收到 SIGHUP 信号后,自动退出。
因为前台任务收到了 SIGHUP 信号,所以会随着 session 的退出而退出。
后台任务是否也会收到 SIGHUP 信号由 Shell 的 huponexit
参数决定。
执行 shopt | grep huponexit
可以查看 huponexit 参数的值。
大多数 Linux 系统,这个参数默认关闭 ( off )。因此,session 退出的时候,不会把 SIGHUP 信号发给后台任务。所以,一般来说,后台任务不会随着 session 一起退出。
3 disown 命令
通过后台任务启动守护进程并不保险,因为有的系统的 huponexit
参数可能是打开的 ( on )。
更保险的方法是使用 disown
命令将指定任务从后台任务列表 ( jobs
命令的返回结果 ) 之中移除。
一个后台任务只要不在这个列表之中,session 就肯定不会向它发出 SIGHUP 信号。
mv a ../b &
disown
执行上面的命令以后,server.js进程就被移出了后台任务列表。执行 jobs
命令验证,输出结果里面,不会有这个进程。
disown 的用法:
disown -r
移出所有正在执行的后台任务;disown -a
移出所有后台任务;disown -h
不移出后台任务,但是让它们不会收到 SIGHUP 信号;disown %2
disown -h %2
根据jobId,移出指定的后台任务。
4 标准 I/O
使用 disown 命令还有一个问题: 退出 session 后,如果后台进程与标准 I/O 有交互,它还是会挂。
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 端口,会发现连不上。
因为后台任务的标准 I/O 继承自当前 session,disown 命令并没有改变这一点。
一旦后台任务读写标准 I/O,就会发现它已经不存在了,所以就报错终止执行。
为了解决这个问题,需要对"后台任务"的标准 I/O 进行重定向。
node server.js > stdout.txt 2> stderr.txt < /dev/null & disown
上面这样执行,基本上就没有问题了。
5 nohup 命令
还有比 disown
更方便的命令,就是 nohup
命令。
nohup node server.js &
nohup 命令对 server.js 进程做了三件事。
- 阻止 SIGHUP 信号发到这个进程。
- 关闭标准输入。该进程不再能够接收任何输入,即使运行在前台。
- 重定向标准输出和标准错误到文件 nohup.out。
也就是说,nohup 命令实际上将子进程与它所在的 session 分离了。
注意,nohup 命令不会自动把进程变为后台任务,所以必须加上 & 符号。
nohup 命令:
- 无论是否将 nohup 命令的输出重定向到终端,输出都将附加到当前目录的 nohup.out 文件中。
- 如果当前目录的 nohup.out 文件不可写,输出重定向到 ~/nohup.out 文件中。
- 如果没有文件能创建或打开以用于追加,那么 Command 参数指定的命令不可调用。
5.1 nohup 和 & 的区别
使用 nohup 运行程序:
- 无论是否将 nohup 命令的输出重定向到终端,输出都将附加到当前目录的 nohup.out 文件中。
- 使用 Ctrl + C 发送 SIGINT 信号,程序关闭
- 关闭 Shell Session 发送 SIGHUP 信号,程序免疫
使用 & 运行程序:
- 程序转入后台运行
- 结果会输出到终端
- 使用 Ctrl + C 发送 SIGINT 信号,程序不会停止
- 关闭 Shell session 发送 SIGHUP 信号,程序关闭
5.2 nohup 和 & 使用实例
一般两个一起组合使用不会受 Ctrl + C 和 Shell 关闭的影响:
nohup <command> &
最简单的后台运行。
nohup python main.py &
输出默认重定向到当前目录下 nohup.out 文件。
nohup python main.py >> main.log 2>&1 &
自定义输出文件 ( 标准输出和错误输出合并到 main.log
)。
nohup python main.py &> main.log &
与上一个例子相同作用的简写方法。
nohup python main.py &> /dev/null &
不记录输出信息。
nohup python main.py &> /dev/null & echo $! > pidfile.txt
不记录输出信息并将程序的进程号写入 pidfile.txt
文件中,方便后续杀死进程。
使用 nohup 时,程序会自动将输出写入 nohup.out
文件中。如果程序不断向控制台输出,文件就会不停的变大。
如果不需要输出,可以用 /dev/null
解决这个问题。它相当于一个黑洞,任何输出到这个文件的东西都将消失。
nohup <command> >/dev/null 2>log &
只保留输出错误信息。
nohup <command> >/dev/null 2>&1 &
所有信息都不要。
2>&1
将错误信息重定向到标准输出。这使用到了 Linux 的重定向,其中 0
1
2
分别是标准输入、标准输出、标准错误输出,用来指定需要重定向的标准输入输出。默认情况下是标出输出,也就是 1
。
jobs -l
查看任务,返回任务编号和进程号。
bg %<jobnumber>
将一个在后台暂停的命令,变成在后台继续执行。如果后台中有多个命令,可以用 bg %<jobnumber>
将选中的命令调出。
fg %<jobnumber>
将后台中的命令调至前台继续运行。如果后台中有多个命令,可以用 fg %<jobnumber>
( 是命令编号,不是进程号 ) 将选中的命令调出。