SSH 断开后使进程仍在后台运行¶
对于 Linux 运维,经常会使用 ssh 登录到服务器。如果任务需要很长时间或不间断运行,在关闭终端窗口或网络不稳定的情况下,任务就会中断。 当然这只对于普通程序,不包括如 mysqld、httpd 这样的守护进程。
原因分析:
[root@DigMouse ~]# ping www.baidu.com > /dev/null &
[1] 21945
[root@DigMouse ~]# pstree -H 21945
systemd─┬─AliYunDun───17*[{AliYunDun}]
├─AliYunDunUpdate───3*[{AliYunDunUpdate}]
├─acpid
├─2*[agetty]
├─aliyun-service───5*[{aliyun-service}]
├─atd
├─cron
├─dbus-daemon
├─nscd─┬─{nscd) S 1 434 43
│ └─9*[{nscd}]
├─ntpd───ntpd
├─python3
├─rpcbind
├─rsyslogd─┬─{in:imklog}
│ ├─{in:imuxsock}
│ └─{rs:main Q:Reg}
├─sshd───sshd───bash─┬─ping
│ └─pstree
├─systemd-journal
├─systemd-logind
├─systemd-network
├─systemd-udevd
├─vsftpd
└─wrapper─┬─java───13*[{java}]
└─{wrapper}
当用户注销(logout)或者网络断开时,终端会收到 HUP(hangup)信号从而关闭其所有子进程。 从上面的例子可以看出当前所处的 bash 是 sshd 的子进程,当 ssh 断开连接时,HUP 信号会影响到它下面的所有子进程,包括 ping 进程。
解决思路:
- 让进程运行在新的 session(会话)里即不属于此终端的子进程。
- 让进程忽略 HUP 信号
解决方法:¶
1. nohup 命令¶
功能:不挂断地运行命令,忽略HUP信号。
语法:nohup command &
实例:
[root@DigMouse ~]# nohup ping www.baidu.com &
[1] 22050
[root@DigMouse ~]# nohup: ignoring input and redirecting stderr to stdout
[root@DigMouse ~]# ls
nohup.out
[root@DigMouse ~]# cat nohup.out
PING www.wshifen.com (103.235.46.39) 56(84) bytes of data.
64 bytes from 122.225.57.246: icmp_seq=1 ttl=56 time=48.6 ms
64 bytes from 122.225.57.246: icmp_seq=2 ttl=56 time=47.8 ms
64 bytes from 122.225.57.246: icmp_seq=3 ttl=56 time=49.9 ms
64 bytes from 122.225.57.246: icmp_seq=4 ttl=56 time=49.5 ms
# 从另一个终端执行以下命令
[root@DigMouse ~]# ps -ef | grep ping
root 22050 21736 0 21:01 pts/0 00:00:00 ping www.baidu.com
root 22070 21736 0 21:04 pts/0 00:00:00 grep ping
关闭此终端,打开另一个终端使用 ps 命令,查看到后台有 ping 进程在运行。 终端输出将附加到当前目录的 nohup.out 文件中。如果当前目录的 nohup.out 文件不可写,输出将重定向到 $HOME/nohup.out 文件中。
2. setsid命令¶
功能:在新的会话中运行程序
语法:setsid command
实例:
[root@DigMouse ~]# setsid ping www.baidu.com > /dev/null
[root@DigMouse ~]# ps -ef | grep ping
UID PID PPID C STIME TTY TIME CMD
root 22146 1 0 21:14 ? 00:00:00 ping www.baidu.com
root 22152 21736 0 21:15 pts/0 00:00:00 grep ping
[root@DigMouse ~]#
从上例可以看出 ping 进程的 PID 是 22146,进程的父 ID(PPID) 是 init 而不是当前终端的进程 ID,可与 nohup 比较。
3. 将"&"也放入“()”内执行命令¶
将一个或多个命名包含在“()”中就能让这些命令在子 shell 中运行
[root@DigMouse ~]# (ping www.baidu.com > /dev/null &)
[root@DigMouse ~]# ps -ef | grep ping
root 22202 1 0 21:22 pts/0 00:00:00 ping www.baidu.com
root 22204 21736 0 21:22 pts/0 00:00:00 grep ping
[root@DigMouse ~]#
进程的父 ID 是 init 而不是当前终端的进程 ID,因而关闭终端无任何影响。
4. disown 命令¶
用 disown -h jobspec 来使某个作业忽略 HUP 信号。
用 disown -ah 来使所有的作业都忽略 HUP 信号。
用 disown -rh 来使正在运行的作业忽略 HUP 信号。
当使用过 disown 之后,会将把目标作业从作业列表中移除,我们将不能再使用 jobs 来查看它,但是依然能够用 ps -ef 查找到它。
但是还有一个问题,这种方法的操作对象是作业,如果我们在运行命令时在结尾加了"&"来使它成为一个作业并在后台运行,那么就万事大吉了,我们可以通过 jobs 命令来得到所有作业的列表。但是如果并没有把当前命令作为作业来运行,如何才能得到它的作业号呢?答案就是用 CTRL-z(按住Ctrl键的同时按住z键)了!
CTRL-z 的用途就是将当前进程挂起(Suspend),然后我们就可以用 jobs 命令来查询它的作业号,再用 bg jobspec 来将它放入后台并继续运行。 需要注意的是,如果挂起会影响当前进程的运行结果,请慎用此方法。
# 将程序放入后台,暂停执行(挂起进程)
[root@DigMouse ~]# ping www.baidu.com > /dev/null
^Z
[1]+ Stopped ping www.baidu.com > /dev/null
[root@DigMouse ~]# jobs
[1]+ Stopped ping www.baidu.com > /dev/null
# 执行后台的作业
[root@DigMouse ~]# bg %1
[1]+ ping www.baidu.com > /dev/null &
[root@DigMouse ~]# jobs
[1]+ Running ping www.baidu.com > /dev/null &
# 此时 jobs 还是能看到 ping 后台任务的。
[root@DigMouse ~]# ps -ef | grep ping
root 22256 21736 0 21:32 pts/0 00:00:00 ping www.baidu.com
root 22273 21736 0 21:35 pts/0 00:00:00 grep ping
[root@DigMouse ~]# disown -h %1
# 需要断开链接后,重新登录
[root@DigMouse ~]# ps -ef | grep ping
root 22256 1 0 21:32 ? 00:00:00 ping www.baidu.com
root 22390 22381 0 21:50 pts/1 00:00:00 grep ping
5. screen 命令(未做测试)¶
此命令非常强大。如果非常多的命令都需要忽略 HUP 命令,screen 可以解决这一问题。screen 提供了 ANSI/VT100 的终端模拟器,使它能够在一个真实终端下运行多个全屏的伪终端。
- 用 screen -dmS session name 来建立一个处于断开模式下的会话(并指定其会话名)。
- 用 screen -list 来列出所有会话。
- 用 screen -r session name 来重新连接指定会话。
- 用快捷键 CTRL-a d 来暂时断开当前会话。
此时 bash 是 screen 的子进程,而 screen 是 init(PID为1)的子进程。那么当 ssh 断开连接时,HUP 信号自然不会影响到 screen 下面的子进程了。
disown, & 和 nohup 的区别¶
总结来说:
&: 将进程置于后台,使 Shell 不用等待它的结束而继续接受用户输入(stdin)。
disown: 将进程从 jobs 列表中移除, 但依然与 Shell 有连接
nohup: 将进程与父 Shell 完全脱离,且子进程不会接受 NOHUP 信号,并不能用 fg 或者 jobs 命令找到它。