首页 > 代码库 > 让命令提交后不受本地关闭终端窗口/网络断开连接的干扰

让命令提交后不受本地关闭终端窗口/网络断开连接的干扰

项目中操作场景:本地机器上通过ssh连接服务器进行相关操作。这里就出现一个问题,在我们本地程序中,向服务器发送一个命令是没有任何问题的,可是当本次程序意外关闭或者由于网络断开连接,这是我们通过本地程序在服务器上创建的进程也就会被杀死。所以问题就来了:如何让对应进程能无间断的执行,即通过本机程序在服务器上创建一个进程,当本地关闭ssh连接或者与服务器的网络断开之后,这个创建的进程要继续存在而不会被杀死。在IBM 上看到一遍文章,对这个问题的解决方法讲解的很详细,现在简要介绍下:

我们知道,当用户注销(logout)或者网络断开时,终端会收到 HUP(hangup)信号从而关闭其所有子进程。因此,我们的解决办法就有两种途径:要么让进程忽略 HUP 信号,要么让进程运行在新的会话里从而成为不属于此终端的子进程。

1.nohup

nohup 的用途就是让提交的命令忽略 hangup 信号。其帮助信息如下:

NOHUP(1)                        User Commands                        NOHUP(1)

NAME
       nohup - run a command immune to hangups, with output to a non-tty

SYNOPSIS
       nohup COMMAND [ARG]...
       nohup OPTION

DESCRIPTION
       Run COMMAND, ignoring hangup signals.

       --help display this help and exit

       --version
              output version information and exit
可见,nohup 的使用是十分方便的,只需在要处理的命令前加上 nohup 即可,标准输出和标准错误缺省会被重定向到 nohup.out 文件中。

2.setsid

setsid就是改变进程的父进程,让当前进程的父进程(终端)变为init进程(进程号为1),这样当终端关闭之后,当前进程就不会被杀死了。该命令的帮助信息如下:

SETSID(8)                 Linux Programmer’s Manual                 SETSID(8)

NAME
       setsid - run a program in a new session

SYNOPSIS
       setsid program [ arg ... ]

DESCRIPTION
       setsid runs a program in a new session.
3. 括号()与&

&代表后台运行(注意输出并没有被重定向);此外,我们知道,将一个或多个命名包含在“()”中就能让这些命令在子 shell 中运行中,从而扩展出很多有趣的功能,我们现在要讨论的就是其中之一。
当我们将"&"也放入“()”内之后,我们就会发现所提交的作业并不在作业列表中,也就是说,是无法通过jobs来查看的。
以这种方式运行程序,
新提交的进程的父 ID为1,并不是当前终端的进程 ID。因此并不属于当前终端的子进程,从而也就不会受到当前终端的 HUP 信号的影响了。

4. disown

如果我们未加任何处理就已经提交了命令,这时想加 nohup 或者 setsid 已经为时已晚,只能通过作业调度和 disown 来解决这个问题了。其帮助信息如下:

disown [-ar] [-h] [jobspec ...]
	Without options, each jobspec is  removed  from  the  table  of
	active  jobs.   If  the -h option is given, each jobspec is not
	removed from the table, but is marked so  that  SIGHUP  is  not
	sent  to the job if the shell receives a SIGHUP.  If no jobspec
	is present, and neither the -a nor the -r option  is  supplied,
	the  current  job  is  used.  If no jobspec is supplied, the -a
	option means to remove or mark all jobs; the -r option  without
	a  jobspec  argument  restricts operation to running jobs.  The
	return value is 0 unless a jobspec does  not  specify  a  valid
	job.
可以看出,我们可以用如下方式来达成我们的目的。

1) 用disown -h jobspec 来使某个作业忽略HUP信号。

2)用disown -ah 来使所有的作业都忽略HUP信号。

3) 用disown -rh 来使正在运行的作业忽略HUP信号。

需要注意的是,当使用过 disown 之后,会将把目标作业从作业列表中移除,我们将不能再使用jobs来查看它,但是依然能够用ps -ef查找到它。
但是还有一个问题,这种方法的操作对象是作业,如果我们在运行命令时在结尾加了"&"来使它成为一个作业并在后台运行,那么就万事大吉了,我们可以通过jobs命令来得到所有作业的列表。但是如果并没有把当前命令作为作业来运行,如何才能得到它的作业号呢?答案就是用 CTRL-z(按住Ctrl键的同时按住z键)了!
CTRL-z 的用途就是将当前进程挂起(Suspend),然后我们就可以用jobs命令来查询它的作业号,再用bg jobspec 来将它放入后台并继续运行。需要注意的是,如果挂起会影响当前进程的运行结果,慎用此方法。