在linux中,经常会碰到这样的问题,用ssh登录了远程的Linux服务器,运行了一些耗时较长的任务,结果却由于网络等的不稳定导致任务中途失败。这是由于在用户注销(logout)或者网络断开时,终端会收到 HUP(hangup)信号从而关闭其所有子进程。
解决办法有两种:
进程忽略HUP信号,如 nohup、screen
让进程运行在新的会话里执行,从而不属于此终端的子进程,如 setsid、(cmd &)
nohup
不挂断地运行命令,即退出了终端仍然会在后台运行,常配合&使用。
# 语法:nohup Command [ Arg … ] [ & ]
无论是否将 nohup 命令的输出重定向到终端,输出都将附加到当前目录的 nohup.out 文件中。
1)如果当前目录的 nohup.out 文件不可写,输出重定向到 $HOME/nohup.out 文件中。
2)如果没有文件能创建或打开以用于追加,那么 Command 参数指定的命令不可调用。
退出状态,则该命令返回下列出口值:
126 可以查找但不能调用 Command 参数指定的命令。
127 nohup 命令发生错误或不能查找由 Command 参数指定的命令。
否则,nohup 命令的退出状态是 Command 参数指定命令的退出状态。
nohup 命令,会将标准输出和标准错误默认重写向到nohup.out中,所以可以在命令之后添加上">filename 2>&1",例如把输出重新定向到/mimvp-nohup.log文件中。
nohup ~/mimvp-shell.sh > ~/weijing-nohup.log 2>&1 &
& 和 ()
在后台运行,但有一个问题ssh窗口关闭或网络断开,此后台程序也会停止运行。由于当用户注销(logout)或者网络断开时,终端会收到 HUP(hangup)信号从而关闭其所有子进程。所以让进程忽略HUP信号或者将对应脚本程序设置为不属于此终端的子进程。
一般 nohup 和 & 两者一起配合使用,例如:
nohup command &
&可以结合括号()一起使用,产生一个新的子shell并在此子shell中将任务放置到后台运行,从而不受当前shell终端的HUP信号影响,此用法类似与 setsid,且通过 jobs -l无法查看,即提交的作业不在作业列表里。
使用格式:(COMMAND [ARG]... &) ps -ef | grep weijing
ps -ef |grep weijinghomer 10955 1 0 18:04 pts/5 00:00:00 sh weijing-shell.shhomer 10956 10955 0 18:04 pts/5 00:00:00 ping weijing.cohomer 10992 10957 0 18:04 pts/24 00:00:00 grep --color=auto weijing
可见,其父进程id=1,并非当前的终端id,从而不受当前终端影响,关闭并重启终端后,命令会仍然在后台运行,非常类似于 setsid,而通常的使用方式为:
nohup ./filename.sh > filename.log 2>&1 &nohup ./filename.sh &> filename.log &
nohup 优于 & 的三点理由:
1) nohup保障进程不会被hangup信号异常中断;
2) 将任务放置到后台运行,不占用当前的终端;
3) 将错误输出也打印到log中,默认>只有标准输出,错误输出没有。
jobs
使用格式:jobs [-lnprs] [jobspec ...] or jobs -x command [args]
jobs 用于查看当前进程,且只看当前终端生效的,关闭终端后在另一个终端jobs已经无法看到后台跑的程序了,那时需利用ps
控制进程(disown 中经常使用)
查看当前终端下的后台进程:
直接执行:jobs -l
切换到前台,将查看到的某个后台进程放回到前台:
直接输入:fg {jobid} // 这里的{jobid}是通过jobs命令中看到的进程前[]中的数字
切换到后台,将当前正在前台运行的进程放到后台运行:
先敲下快捷键:ctrl +z 暂停当前正在运行的进程
再执行:bg
终止当前正在前台运行的进程:
直接敲下快捷键:ctrl +c
应用实例:
/usr/bin/Xvfb :7 -ac -screen 0 1280x1024x8 > /dev/null 2>&1 &export DISPLAY=:7nohup java -jar selenium-server-standalone-3.8.0.jar -port 8888 &# 运行查看日志nohup java -jar selenium-server-standalone-3.8.0.jar -port 8888 &[1] 10020nohup: ignoring input and appending output to ‘nohup.out’ll-rw-r--r-- 1 homer homer 3730403 12月 24 23:18 chromedriver_linux64_v2.34.zip-rw-rw-r-- 1 homer homer 90 12月 26 22:26 composer.json-rw-rw-r-- 1 homer homer 41340 12月 26 22:31 composer.lock-rwxr-xr-x 1 homer homer 1855013 12月 26 22:25 composer.phar*-rw-rw-r-- 1 homer homer 2259579 1月 2 12:10 geckodriver-v0.19.0-linux64.tar.gz-rw-r--r-- 1 homer homer 2301226 12月 25 22:56 geckodriver-v0.19.1-linux64.tar.gz-rw------- 1 homer homer 22448 1月 2 16:52 nohup.out-rw-rw-r-- 1 homer homer 22844105 1月 2 11:53 selenium-server-standalone-3.8.0.jar-rw-rw-r-- 1 homer homer 22844868 12月 28 13:31 selenium-server-standalone-3.8.1.jardrwxrwxr-x 12 homer homer 4096 12月 26 22:31 vendor/jobs -l[1]+ 10020 Running nohup java -jar selenium-server-standalone-3.8.0.jar -port 8888 &
日志可见,自动在当前目录下,创建了日志文件 nohup.out
关闭当前终端,然后再打开终端,查看 nohup 是否仍然在运行(没有被挂断)
ps -ef | grep javahomer 10020 1 0 16:59 ? 00:00:04 java -jar selenium-server-standalone-3.8.0.jar -port 8888
确认了,nohup 和 & 配合使用,关闭终端后再重启终端,java 仍然一直在后台运行,没有被挂断
setsid
setsid 是让提交的命令归属一个新会话,即新开一个终端,其用法与nohup类似。
$ setsid java -jar selenium-server-standalone-3.8.0.jar -port 8888 &[2] 10269$ 17:23:40.735 INFO - Selenium build info: version: '3.8.0', revision: '924c4067df'
不同的是 setsid 是新开一个终端会话,nohup是在当前的终端会话
例如:对比下进程父ID(PPID)
ps -ef | grep javahomer 10020 9859 0 17:05 pts/1 00:00:02 java -jar selenium-server-standalone-3.8.0.jar -port 8888homer 10270 1 22 17:23 ? 00:00:01 java -jar selenium-server-standalone-3.8.0.jar -port 8888homer 10291 9859 0 17:23 pts/1 00:00:00 grep --color=auto java
通过对比可看出,使用setsid提交的命令父进程id为1,并不是当前终端进程id;而 nohup提交的命令父进程id为当前终端id=9859
disown
使用格式:disown [-h] [-ar] [jobspec ...]
如果未加任何处理就已经提交了命令,可使用disown补救,为没有使用nohup与setsid的进程加上忽略HUP信号的功能,例如提交的命令可用&放入后台运行
如果执行的命令想在前台和后台切换,也可以使用disown进行控制切换。
应用实例:
# 创建一个ping脚本进行运行模拟vim mimvp-shell.sh#!/bin/bashping mimvp.com# 一般后台运行sh mimvp-shell.sh &[1] 12189jobs -l[1]+ 12189 Running sh mimvp-shell.sh &# 当前终端关闭后,此进程会随之关闭,终止
disown 后台运行
sh weijing-shell.sh &[1] 12357jobs -l[1]+ 12357 Running sh weijing-shell.sh &disown -h %1jobs -l[1]+ 12357 Running sh weijingp-shell.sh &$$ ps -ef | grep weijinghomer 12357 12279 0 19:15 pts/24 00:00:00 sh weijing-shell.shhomer 12358 12357 0 19:15 pts/24 00:00:00 ping weijingp.comhomer 12385 12279 0 19:18 pts/24 00:00:00 grep --color=auto mimvp# # 如上,执行 disown -h %1 后,通过 jobs -l 查看进程号前后几乎没有变化,ps 查看进程号也几乎没有变化# # 接着,关闭终端,并重启终端后,通过 ps 查看进程 sh mimvp-shell.shps -ef | grep mimvphomer 12357 1 0 19:15 ? 00:00:00 sh mimvp-shell.shhomer 12358 12357 0 19:15 ? 00:00:00 ping mimvp.comhomer 12534 12498 0 19:22 pts/5 00:00:00 grep --color=auto weijing# # 发现进程 sh mimvp-shell.sh 仍然在运行,且其父进程id=1# # 即执行 disown -h %1 命令后,让作业号为1的进程,转为了后台nohup进程,即成功实现了亡羊补牢# # 实现了功能:后台命令 —> nohup 后台运行
disown
sh weijing-shell.shPING weijing.co (47.95.6.112) 56(84) bytes of data.64 bytes from 47.95.6.112: icmp_seq=1 ttl=53 time=8.87 ms64 bytes from 47.95.6.112: icmp_seq=2 ttl=53 time=8.94 ms64 bytes from 47.95.6.112: icmp_seq=3 ttl=53 time=11.9 ms^Z (Ctrl + z 挂起前台进程)[1]+ Stopped sh mimvp-shell.shbg 1 (bg 1 切换到后台进程;fg 切换到前台进程)[1]+ sh mimvp-shell.sh &64 bytes from 47.95.6.112: icmp_seq=4 ttl=53 time=9.07 msdisown -h %1jobs -l[1]+ 12800 Running sh mimvp-shell.sh &# 说明:1) 一开始在前台运行命令 sh mimvp-shell.sh2) Ctrl + z 挂起前台进程3) bg 1 (bg 1 切换到后台进程; fg 切换到前台进程)4) disown -h %1 后台进程挂起5) 关闭终端,重启终端后,仍然在后台执行ps -ef | grep weijinghomer 12800 1 0 19:30 ? 00:00:00 sh weijing-shell.shhomer 12801 12800 0 19:30 ? 00:00:00 ping weijing.cohomer 12971 12927 0 19:37 pts/24 00:00:00 grep --color=auto weijing# 实现了功能: 前台命令 —> 后台命令 —> nohup 后台运行# 补充说明:jobs -l 查看当前作业一般接-l,用于显示其作业号bg %作业号,将作业在后台运行,例如 bg 1, bg %1fg %作业号,将作业在前面处理,例如 fg 1, fg %1CTRL-z,将当前程序挂起




