我们经常会碰到这样的问题,用 telnet/ssh 登录了远程的 Linux 服务器,运行了一些耗时较长的任务, 结果却由于网络的不稳定导致任务中途失败。
解决办法:
当用户注销(logout)或者网络断开时,终端会收到 HUP(hangup)信号从而关闭其所有子进程。因此,我们的解决办法就有两种:要么让进程忽略 HUP 信号,要么让进程运行在新的会话里从而成为不属于此终端的子进程。
模拟一个断开的会话,看一下前台进程是否还会正常运行:
[root@node1 ~]# cat b.sh
#!/bin/bash
for((;;)) do
sleep 1
echo "no" >> /tmp/b.txt
done
第一个会话:
[root@node1 ~]# bash b.sh #执行脚本,会占据整个当前会话页面
新打开一个会话:
[root@node1 ~]# ps -ef | grep b.sh
root 34693 28170 0 11:32 pts/0 00:00:00 bash b.sh
...
[root@node1 ~]# kill -1 34693 #模拟第一个会话断开的情况,发送SIGHUP信号
再次切换至第一个会话:
[root@node1 ~]# bash b.sh
Hangup #已经断线
[root@node1 ~]#
nohup
nohup - run a command immune to hangups, with output to a non-tty
运行一个忽略hangups(SIGHUP)影响的命令,输出到一个非tty
格式:
nohup COMMAND [ARG]...
示例:
[root@node1 ~]# nohup bash b.sh #占据当前会话,使得当前会话不可执行其它命令
nohup: ignoring input and appending output to ‘nohup.out’ #如不指定日志输出位置,默认输出至当前目录下的nohup.out文件中。
重新打开一个会话:
[root@node1 ~]# ps -ef | grep b.sh
root 25973 16490 0 09:56 pts/1 00:00:00 bash b.sh
[root@node1 ~]# kill -1 25973 #向进程发送SIGHUP信号,nohup进程还在运行。关闭上一个session会话,效果和kill -1效果一样
[root@node1 ~]#
nohup将标准输出和标准错误缺省会被重定向到nohup.out文件中。一般我们可在结尾加上&
来将命令同时放入后台运行,也可用>xxx.log 2>&1
(将标准输出和标准错误输出至指定的文件中)来更改缺省的重定向文件名。
setsid
setsid - run a program in a new session
在一个新的会话中运行一个程序。
格式:
setsid program [arg...]
示例:
[root@node1 ~]# setsid bash b.sh #使用setsid来代替nohup
[root@node1 ~]# jobs #后台无法查看
[root@node1 ~]# ps -ef | grep c.sh #ps查找其进程,可以看到PID是43019,但PPID确为1(systemd)
root 43593 1 0 14:15 ? 00:00:00 bash b.sh
root 44473 43385 0 14:22 pts/1 00:00:00 grep --color=auto b.sh
[root@node1 ~]# pstree -H 43593 -p
systemd(1)─┬─NetworkManager(3403)─┬─{NetworkManager}(3543)
│ └─{NetworkManager}(3551)
├─agetty(3528)
├─auditd(2802)───{auditd}(2806)
├─bash(43593)───sleep(44564) #因为父进程是1,不属于某个终端的子进程,所以也就无法接收到HUP信号。但是不能使用专门指定这个此进程发送HUP信号,这样是会中断的。
├─chronyd(3472)
├─crond(3490)
...
├─sshd(4154)───sshd(16488)─┬─bash(36542)───less(40656)
│ ├─bash(42726)
│ └─bash(43385)───pstree(44565)
...
同样的,在shell也有一种可以使用进程的PPID改成1的方法,使用`()`括起来并加上`&`,也和`setsid`效果一样。 ``` [root@node1 ~]# (bash b.sh &) #后台执行命令 [root@node1 ~]# jobs #jobs无法查看 [root@node1 ~]# ps -ef | grep b.sh root 45631 1 0 14:30 pts/1 00:00:00 bash b.sh #PPID为1 root 45706 43385 0 14:31 pts/1 00:00:00 grep --color=auto b.sh [root@node1 ~]# kill -1 45631 #向进程发送HUP信号 [root@node1 ~]# ps -ef | grep b.sh #进程被中断 root 45784 43385 0 14:31 pts/1 00:00:00 grep --color=auto b.sh [root@node1 ~]#
<br />
##jobs:
> 显示当前会话下后台任务列表。也就是说,如果当命令切换至后台和想要显示后台有哪些进程不是同一个会话,那么将查不出来有哪些后台进程。
**格式:**
`jobs [option]`
**常用选项:**
* -l:列出进程ID及其它信息
* -p:仅列出PID
* -n:仅列出自从上次输出了状态变化提示。run -> stop
* -r:显示运行中的进程
* -s:显示停止的进程
<br />
##fg:
> 将命令从后台作业提到前台终端运行。使用格式`fg 任务号`。任务号是由jobs命令获取的第一列。
<br />
##bg:
> 使用格式`bg 任务号`。想要使用bg命令,首先对前台命令`ctrl+z`下放置后台成stop模式后,在对其使用bg命令才能像一开始在命令后添加`&`一样后台运行。需要注意的是,如果挂起会影响到当前进程执行的结果,请慎用`ctrl+z`在`bg`。
<br />
**示例:**
[root@node1 ~]# bash b.sh #ctrl+z挂起进程至后台
^Z
[1]+ Stopped bash b.sh
[root@node1 ~]# jobs
[1]+ Stopped bash b.sh
[root@node1 ~]# bg 1 #后台继续运行
[1]+ bash b.sh &
[root@node1 ~]# jobs #已经运行
[1]+ Running bash b.sh &
[root@node1 ~]#
[root@node1 ~]# bash #重新打开一个bash
[root@node1 ~]# jobs #查询后台进程,查询为空,所以jobs只能查询当前会话的后台进程。
[root@node1 ~]#
[root@node1 ~]# exit #退出新的bash
exit
[root@node1 ~]# jobs #查看jobs
[1]+ Running bash b.sh &
[root@node1 ~]# fg 1 #调至前台运行
bash b.sh
<br />
**总结:**
`nohup`是使用进程本身忽略HUP信号从而达到可以后台运行不被意外中断。`setsid`和shell中的`( &)`是将进程的父进程改为1,从而达到不受sshd连接的各种情况影响。
`jobs`,`bg`,`fg`三个命令只能在当前的会话终端上执行,一但会话终端意外剥离,就无法在操作。
<br />
参考资料:[https://www.ibm.com/developerworks/cn/linux/l-cn-nohup/index.html](https://www.ibm.com/developerworks/cn/linux/l-cn-nohup/index.html)