nohup-长期运行进程

我们经常会碰到这样的问题,用 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)
posted @ 2019-01-25 10:22  dance_man  阅读(1769)  评论(0编辑  收藏  举报