Linux/VPS技巧:让进程在后台稳定运行的几种方法

前言

我们经常会碰到这样的问题,用 tel­net/​ssh 登录了远程的 Linux 服务器,运行了一些耗时较长的任务, 结果却由于网络的不稳定导致任务中途失败。如何让命令提交后不受本地关闭终端窗口 / 网络断开连接的干扰呢?下面举了一些例子, 您可以针对不同的场景选择不同的方式来处理这个问题。

在了解命令之前,先普及一下 :HUP (hangup) 名称的由来:在 Unix 的早期版本中,每个终端都会通过 mo­dem 和系统通讯。当用户 lo­gout 时,mo­dem 就会挂断(hang up)电话。 同理,当 mo­dem 断开连接时,就会给终端发送 hangup 信号来通知其关闭所有子进程。

教程

nohup/setsid/&

场景:如果只是临时有一个命令需要长时间运行,什么方法能最简便的保证它在后台稳定运行呢?

1.nohup命令

nohup 命令可以将程序以忽略挂起信号的方式运行起来,被运行的程序的输出信息将不会显示到终端。

无论是否将 nohup 命令的输出重定向到终端,输出都将附加到当前目录的 nohup.out 文件中。如果当前目录的 nohup.out 文件不可写,输出重定向到 $HOME/nohup.out 文件中。如果没有文件能创建或打开以用于追加,那么 command 参数指定的命令不可调用。如果标准错误是一个终端,那么把指定的命令写给标准错误的所有输出作为标准输出重定向到相同的文件描述符。

语法

nohup(选项)(参数)

选项

--help:在线帮助;
--version:显示版本信息。

参数

程序及选项:要运行的程序及选项。

实例

使用 nohup 命令提交作业,如果使用 no­hup 命令提交作业,那么在缺省情况下该作业的所有输出都被重定向到一个名为 no­hup.out 的文件中,除非另外指定了输出文件:

nohup command > myout.file 2>&1 &

在上面的例子中,输出被重定向到 myout.file 文件中。

2.setsid命令

setsid 就是 “set ses­sion id” 的意思。表示该命令运行的进程是一个新的 session。因此其父进程不属于当前终端。实际上 set­sid 运行的进程,其父进程 id (ppid) 为 1 (init 进程的 id)。

如:setsid minion server :9001 /mnt/test/ > /var/log/minio_test.log &

注意:setsid 输出重定向必须手动指定。

语法

setsid(选项)(参数)

选项

-c, --ctty   将控制终端设置为当前控制终端
-f, --fork   总是 fork
-w, --wait   等待程序退出,并使用相同的返回

参数

程序及选项:要运行的程序及选项。

实例

setsid 的使用非常方便,也只需在要处理的命令前加上 setsid 即可

setsid ping www.moewah.com

3.&

& 代表后台运行程序。如果终端退出,则该进程会结束。通常配合 nohup 和 setsid 命令配合使用。

这里还有一个关于 sub­shell 的小技巧。我们知道,将一个或多个命名包含在 () 中就能让这些命令在子 shell 中运行中,从而扩展出很多有趣的功能,我们现在要讨论的就是其中之一。

当我们将 & 也放入 () 内之后,我们就会发现所提交的作业并不在作业列表中,也就是说,是无法通过 jobs 来查看的。让我们来看看为什么这样就能躲过 HUP 信号的影响吧。

subshell 示例

[root@moewah ~]# (ping www.moewah.com &)
[root@moewah ~]# ps -ef |grep www.moewah.com
root     16270     1  0 14:13 pts/4    00:00:00 ping www.moewah.com
root     16278 15362  0 14:13 pts/4    00:00:00 grep www.moewah.com
[root@moewah ~]#

从上例中可以看出,新提交的进程的父 ID(PPID)为 1(init 进程的 PID),并不是当前终端的进程 ID。因此并不属于当前终端的子进程,从而也就不会受到当前终端的 HUP (hangup) 信号的影响了。

screen命令

Screen 是一款由 GNU 计划开发的用于命令行终端切换的自由软件。用户可以通过该软件同时连接多个本地或远程的命令行会话,并在其间自由切换。GNU Screen 可以看作是窗口管理器的命令行界面版本。它提供了统一的管理多个会话的界面和相应的功能。

场景

我们已经知道了如何让进程免受 HUP (hangup) 信号的影响,但是如果有大量的命令需要在稳定的后台里运行,如何避免对每条命令都做这样的操作呢?这时候 Screen 命令就派上用场了。

语法

screen [-AmRvx -ls -wipe][-d <作业名称>][-h <行数>][-r <作业名称>][-s ][-S <作业名称>]

选项

-A  将所有的视窗都调整为目前终端机的大小。
-d <作业名称>  将指定的screen作业离线。
-h <行数>  指定视窗的缓冲区行数。
-m  即使目前已在作业中的screen作业,仍强制建立新的screen作业。
-r <作业名称>  恢复离线的screen作业。
-R  先试图恢复离线的作业。若找不到离线的作业,即建立新的screen作业。
-s  指定建立新视窗时,所要执行的shell。
-S <作业名称>  指定screen作业的名称。
-v  显示版本信息。
-x  恢复之前离线的screen作业。
-ls或--list  显示目前所有的screen作业。
-wipe  检查目前所有的screen作业,并删除已经无法使用的screen作业。

参数

screen -S yourname -> 新建一个叫yourname的session
screen -ls -> 列出当前所有的session
screen -r yourname -> 回到yourname这个session
screen -d yourname -> 远程detach某个session
screen -d -r yourname -> 结束当前session并回到yourname这个session

实例

创建一个 screen 会话:

screen -S xx  #xx为创建会话的名称

隐藏并保留当前会话窗口:

按Ctrl+A,再按"D"键

如果怕中途掉线或者要离开的话,可以使用。

恢复会话窗口:

screen -r xx #恢复名字为xx的会话

如果在恢复会话的时候忘记了或者没有设定会话名称我们就要执行:

screen -ls

他会列出你所有的会话列表,然后使用:

screen -r 会话名称

来恢复会话窗口。

关闭会话窗口:

exit

screen 的好处就是不会因为远程的操作因网络问题中断掉。

总结

现在几种方法已经介绍完毕,我们可以根据不同的场景来选择不同的方案。nohup 和 setsid 无疑是临时需要时最方便的方法,而 screen 则是在大批量操作时不二的选择了。