前言

对于linux运维,我们都是使用ssh登录到服务器,如果我们运行的任务需要很长时间或不间断运行,在我们直接关闭终端窗口或网络不稳定的情况下,任务就会中断,当然这只对于普通程序,不包括如mysqld,httpd这样的守护进程。

 

原因分析

[root@DigMouse ~]# ping 51osos.com > /dev/null &
[1] 13678
[root@DigMouse ~]# pstree -H 13678
 init-+-NetworkManager
       |-sshd---sshd---bash-+-ping
                             |                    `-pstree

当用户注销(logout)或者网络断开时,终端会收到 HUP(hangup)信号从而关闭其所有子进程。

从上面的例子大家可以看出当前我们所处的 bash 是 sshd 的子进程,当 ssh 断开连接时,HUP 信号会影响到它下面的所有子进程,包括ping进程。

 

解决思路:

1.让进程运行在新的session(会话)里即不属于此终端的子进程。

2.可以让进程忽略HUP信号

具体实现方法如下

 

nohup命令

功能:不挂断地运行命令,忽略HUP信号。

语法:nohup command &

实例:

解决方法:

1.nohup命令
功能:不挂断地运行命令,忽略HUP信号。
语法:nohup command &

实例:

nohup ping 51osos.com > /dev/null &
// [1] 13683
//nohup: ignoring input and redirecting stderr to stdout
ls
// Desktop    Downloads  nohup.out
// Documents  Music      Public    Videos 
cat nohup.out
// PING cd447.gotoip.net (122.225.57.246) 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
ps -ef | grep ping
// root     13683 13655  0 09:33 pts/0    00:00:00 ping 51osos.com
// root     13687 13655  0 09:36 pts/0    00:00:00 grep ping

关闭此终端,打开另一个终端使用ps命令,仍然可以查看到ping进程。

无论是否将 nohup 命令的输出重定向到终端,输出都将附加到当前目录的 nohup.out 文件中。

如果当前目录的 nohup.out 文件不可写,输出重定向到 $HOME/nohup.out 文件中。

nohup command > command.out 2>&1 & 

上面的例子中nohup command输出的内容输出到了command.out文件中,错误内容输出到了标准输出。

 

setsid命令

功能:run a program in a new session在新的会话中运行程序

实例:

setsid ping 51osos.com > /dev/null 
ps -ef | grep ping
// root     13696     1  0 09:45 ?        00:00:00 ping 51osos.com
// root     13698 13655  0 09:46 pts/0    00:00:00 grep ping

从上例可以看出ping进程的PID是13696,进程的父ID(PPID)是init而不是当前终端的进程 ID,可与nohup比较。

 

将&也放入()内执行命令

将一个或多个命名包含在“()”中就能让这些命令在子 shell 中运行

(ping 51osos.com > /dev/null &)
ps -ef | grep ping
// root     13702     1  0 09:50 pts/0    00:00:00 ping 51osos.com
// root     13704 13655  0 09:50 pts/0    00:00:00 grep ping

进程的父ID(PPID)是init而不是当前终端的进程 ID,因而关闭终端无任何影响。

 

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 来将它放入后台并继续运行。

需要注意的是,如果挂起会影响当前进程的运行结果,请慎用此方法。

ping 51osos.com > /dev/null 
^Z
// [1]+  Stopped                 ping 51osos.com > /dev/null

jobs
// [1]+  Stopped                 ping 51osos.com > /dev/null

bg %1
// [1]+ ping 51osos.com > /dev/null &

jobs
// [1]+  Running                 ping 51osos.com > /dev/null &

disown -h %1
ps -ef | grep ping
// root     13716 13655  0 09:59 pts/0    00:00:00 ping 51osos.com
// root     13734 13655  0 10:01 pts/0    00:00:00 grep ping

此时jobs还是能看到ping后台任务的。

logout后在另一个终端测试 。

ps -ef | grep ping
// root     13716     1  0 09:59 ?        00:00:00 ping 51osos.com
// root     13754 13738  0 10:02 pts/1    00:00:00 grep ping

此时Jobs无ping后台任务,PPID变成了1 。

 

screen命令

安装

yum install screen*

 

创建一个新的窗口

安装完成后,直接敲命令screen就可以启动它。但是这样启动的screen会话没有名字,实际使用时推荐为每个screen会话取一个名字,方便分辨:

screen -S yumefx

screen启动后,会创建第一个窗口,也就是窗口No. 0,并在其中打开一个系统默认的shell,一般都会是bash。

所以你敲入命令screen之后,会立刻又返回到命令提示符,仿佛什么也没有发生似的,其实你已经进入Screen的世界了。

当然,也可以在screen命令之后加入你喜欢的参数,使之直接打开你指定的程序,例如:

screen vi yumefx.txt

screen创建一个执行vi yumefx.txt的单窗口会话,退出vi 将退出该窗口/会话。

 

查看窗口和窗口名称

打开多个窗口后,可以使用快捷键Ctrl+a w列出当前所有窗口。

如果使用文本终端,这个列表会列在屏幕左下角,如果使用X环境下的终端模拟器,这个列表会列在标题栏里。窗口列表的样子一般是这样:

0$ bash  1-$ bash  2*$ bash 

这个例子中我开启了三个窗口,其中*号表示当前位于窗口2,-号表示上一次切换窗口时位于窗口1。

Screen默认会为窗口命名为编号和窗口中运行程序名的组合,上面的例子中窗口都是默认名字。

练习了上面查看窗口的方法,你可能就希望各个窗口可以有不同的名字以方便区分了。

可以使用快捷键Ctrl+a A来为当前窗口重命名,按下快捷键后,Screen会允许你为当前窗口输入新的名字,回车确认。

 

会话分离与恢复

你可以不中断screen窗口中程序的运行而暂时断开(detach)screen会话,并在随后时间重新连接(attach)该会话,重新控制各窗口中运行的程序。

例如,我们打开一个screen窗口编辑/tmp/yumefx.txt文件:

screen vi /tmp/yumefx.txt

之后我们想暂时退出做点别的事情,比如出去散散步,那么在screen窗口键入Ctrl+a d,Screen会中断会话:

半个小时之后回来了,找到该screen会话:

screen -ls
// There is a screen on:
//         12865.pts-0.TS-DEV     (Detached)
// 1 Socket in /var/run/screen/S-root.

screen -r 12865

重新连接会话,一切都在。

当然,如果你在另一台机器上没有分离一个Screen会话,就无从恢复会话了。

这时可以使用下面命令强制将这个会话从它所在的终端分离,转移到新的终端上来:

screen -ls
// There is a screen on:
//         5011.yume     (Attached)
// 1 Socket in /var/run/screen/S-root.

screen -r 5011
// There is a screen on:
//         5011.yume     (Attached)
// There is no screen to be resume matching 5011.

screen -d
// [5011.yume detached.]

screen -r 5011 

 

清除dead 会话

如果由于某种原因其中一个会话死掉了(例如人为杀掉该会话),这时screen -list会显示该会话为dead状态。

使用screen -wipe命令清除该会话:

screen -ls
// There is a screen on:
//         5011.yume     (Dead ???)

screen -wipe
// There is a screen on:
//         5011.yume     (Removed)
// 1 Socked wiped out.

 

关闭或杀死窗口

正常情况下,当你退出一个窗口中最后一个程序(通常是bash)后,这个窗口就关闭了。

另一个关闭窗口的方法是使用Ctrl+a k,这个快捷键会杀死当前的窗口,同时也将杀死这个窗口中正在运行的进程。

如果一个Screen会话中最后一个窗口被关闭了,那么整个Screen会话也就退出了,screen进程会被终止。

除了依次退出/杀死当前Screen会话中所有窗口这种方法之外,还可以使用快捷键Ctrl+a : ,然后输入quit命令退出Screen会话。

需要注意的是,这样退出会杀死所有窗口并退出其中运行的所有程序。其实Ctrl+a :这个快捷键允许用户直接输入的命令有很多,包括分屏可以输入split等,这也是实现Screen功能的一个途径,不过个人认为还是快捷键比较方便些。

看到最后,我还是用screen吧。

转载自v1v1wangzhezhelin,感谢。

 


照自己的意愿一息尚存,也好过听从别人的安排,虚张声势的过着浅薄生活。

《我要快乐,不必正常》——珍妮特·温特森