[更新][转载][整理]解决SSH退出登陆后保持进程继续在后台运行

zjking的几个思考写的很好啊忍不住想记下来,侵删

问题描述:

远程ssh用tar打包一个文件夹或者unrar解压一个大文件,即使加了&,退出ssh登录时解压进程中断。

ps:直接exit的话会提示zsh: you have running jobs.,强行退出会有zsh: warning: 1 jobs SIGHUPed提示。

关于SIGNUP信号

直接转一段wiki

If the process receiving SIGHUP is a Unix shell, then as part of job control it will often intercept the signal and ensure that all stopped processes are continued before sending the signal to child processes (more precisely, process groups, represented internally by the shell as a “job”), which by default terminates them.

This can be circumvented in two ways. Firstly, the Single UNIX Specification describes a shell utility called nohup, which can be used as a wrapper to start a program and make it ignore SIGHUP by default. Secondly, child process groups can be “disowned“ by invoking disown with the job id, which removes the process group from the shell’s job table (so they will not be sent SIGHUP), or (optionally) keeps them in the job table but prevents them from receiving SIGHUP on shell termination.

解决方法:

目前有以下几种解决方案:

  1. nohup。使用nohup命令让程序在关闭窗口(切换SSH连接)的时候程序还能继续在后台运行。
  2. disown。注意 要在后台运行挂在后台的话会提示disown: warning: job is suspended, use 'kill -CONT -pid' to resume',不运行的话退出以后还是会杀掉进程,保持运行的话变成孤儿进程。
  3. 使用tmux的detach(<C-b> + D)来detach一个session,之后回去的时候tmux attach回到那个session
  4. screen。不过有tmux就别用screen了,byobu大概更好用一点但是习惯tmux了而且也不太喜欢F1,F2之类的快捷键

nohup命令说明:

用途:不挂断地运行命令。

语法:nohup Command [ Arg … ] [ & ]

描述:nohup 命令运行由 Command 参数和任何相关的 Arg 参数指定的命令,忽略所有挂断(SIGHUP)信号。在注销后使用 nohup 命令运行后台中的程序。要运行后台中的 nohup 命令,添加 & ( 表示“and”的符号)到命令的尾部。

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

退出状态:该命令返回下列出口值:

  • 126 可以查找但不能调用 Command 参数指定的命令。
  • 127 nohup 命令发生错误或不能查找由 Command 参数指定的命令。

否则,nohup 命令的退出状态是 Command 参数指定命令的退出状态。

nohup命令及其输出文件

nohup命令:如果你正在运行一个进程,而且你觉得在退出帐户时该进程还不会结束,那么可以使用nohup命令。该命令可以在你退出帐户/关闭终端之后继续运行相应的进程。nohup就是不挂起的意思( no hang up)。

该命令的一般形式为:nohup command &

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

nohup command > myout.file 2>&1 &

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

结合重定向知识:
为了不让一些执行信息输出到前台(控制台),我们还会加上刚才提到的>/dev/null 2>&1命令来丢弃所有的输出:

1
# nohup java -jar xxxx.jar >/dev/null 2>&1 &

思考

  • 问题1为什么ssh一关闭,程序就不再运行了?

元凶:SIGHUP 信号
让我们来看看为什么关掉窗口/断开连接会使得正在运行的程序死掉。

在Linux/Unix中,有这样几个概念:

进程组(process group):一个或多个进程的集合,每一个进程组有唯一一个进程组ID,即进程组长进程的ID。

会话期(session):一个或多个进程组的集合,有唯一一个会话期首进程(session leader)。会话期ID为首进程的ID。
会话期可以有一个单独的控制终端(controlling terminal)。与控制终端连接的会话期首进程叫做控制进程(controlling process)。当前与终端交互的进程称为前台进程组。其余进程组称为后台进程组。

根据POSIX.1定义:
挂断信号(SIGHUP)默认的动作是终止程序。
当终端接口检测到网络连接断开,将挂断信号发送给控制进程(会话期首进程)。
如果会话期首进程终止,则该信号发送到该会话期前台进程组。
一个进程退出导致一个孤儿进程组中产生时,如果任意一个孤儿进程组进程处于STOP状态,发送SIGHUP和SIGCONT信号到该进程组中所有进程。(关于孤儿进程参照:http://blog.csdn.net/hmsiwtv/article/details/7901711

结论:因此当网络断开或终端窗口关闭后,也就是SSH断开以后,控制进程收到SIGHUP信号退出,会导致该会话期内其他进程退出。

简而言之: 就是ssh 打开以后,bash等都是他的子程序,一旦ssh关闭,系统将所有相关进程杀掉!! 导致一旦ssh关闭,执行中的任务就取消了

例子:
我们来看一个例子。打开两个SSH终端窗口,在其中一个运行top命令。

1
[root@tivf09 root]# top

在另一个终端窗口,找到top的进程ID为5180,其父进程ID为5128,即登录shell。

1
2
3
[root@tivf09 root]# ps -ef|grep top
root 5180 5128 0 01:03 pts/0 00:00:02 top
root 5857 3672 0 01:12 pts/2 00:00:00 grep top

使用pstree命令可以更清楚地看到这个关系:

1
2
[root@tivf09 root]# pstree -H 5180|grep top
|-sshd-+-sshd---bash---top

使用ps-xj命令可以看到,登录shell(PID 5128)和top在同一个会话期,shell为会话期首进程,所在进程组PGID为5128,top所在进程组PGID为5180,为前台进程组。

1
2
3
4
[root@tivf09 root]# ps -xj|grep 5128
5126 5128 5128 5128 pts/0 5180 S 0 0:00 -bash
5128 5180 5180 5128 pts/0 5180 S 0 0:50 top
3672 18095 18094 3672 pts/2 18094 S 0 0:00 grep 5128

关闭第一个SSH窗口,在另一个窗口中可以看到top也被杀掉了。

1
2
[root@tivf09 root]# ps -ef|grep 5128
root 18699 3672 0 04:35 pts/2 00:00:00 grep 5128
  • 问题2 为什么守护程序就算ssh 打开的,就算关闭ssh也不会影响其运行?

因为他们的程序特殊,比如httpd –k start运行这个以后, 他不属于sshd这个进程组 而是单独的进程组,所以就算关闭了ssh,和他也没有任何关系!

1
2
3
4
[root@CentOS5-4 ~]# pstree |grep http
|-httpd
[root@CentOS5-4 ~]# pstree |grep top
|-sshd-+-sshd---bash---top

结论:守护进程的启动命令本身就是特殊的,和一般命令不同的,比如mysqld_safe 这样的命令 一旦使用了 就是守护进程运行。所以想把一般程序改造为守护程序是不可能,

  • 问题3 使用后台运行命令& 能否将程序摆脱ssh进程组控制呢 也就是ssh关闭,后台程序继续运行?

我们做一个试验:

1
find / -name '*http*'&

利用ctrl+d 注销以后 再进入系统 会不会看见这个命令再运行?
答案是 :命令被中止了!!

因为他依然属于这个ssh进程组 就算加了&也无法摆脱!!

1
2
[root@CentOS5-4 ~]# pstree |grep find
|-sshd-+-sshd---bash---find

结论就是: 只要是ssh 打开执行的一般命令,不是守护程序,无论加不加&,一旦关闭ssh,系统就会用SIGHUP终止

  • 问题4 nohup能解决的问题

但是为了能够再注销以后 依然能后台运行,那么我们就可以使用nohup这个命令,我们现在开始查找find / -name ‘*http*’&,并且希望在后台运行,
那么就使用nohup:nohup find / -name "*httpd*"

此时默认地程序运行的输出信息放到当前文件夹的nohup.out 文件中去
加不加&并不会影响这个命令 只是让程序 前台或者后台运行而已

  • 延伸:Linux命令nohup+screen命令

如果想在关闭ssh连接后刚才启动的程序继续运行怎么办,可以使用nohup。但是如果要求第二天来的时候,一开ssh,还能查看到昨天运行的程序的状态,然后继续工作,这时nohup是不行了,需要使用screen来达到这个目的。

Update

disown的用途

引用一下wiki - Job control

The disown command can be used to remove jobs from the job table, so that when the session ends the child process groups are not sent SIGHUP, nor does the shell wait for them to terminate. They thus become orphan processes, and may be terminated by the operating system, though more often this is used so the processes are adopted by init (the kernel sets their parent process to init) and continue executing as daemons.

disown一个程序以后它的sid不会变,但是session leader关了以后不会发SIGHUP给它,所以挂后台可以继续运行。

ps 程序不能有到终端的输出,否则还是会直接挂断程序(比如top),可以重定向到文件,退出ssh登录时就不会挂断(重点是在后台跑)

信号量

kill -l可以查看所有信号量,可以用kill -CONT pid继续进程。

更新2种可以挂后台的方法

  • setsid设置进程,sid就是自己的pid
  • 使用subshell, e.g. (python sleep.py > /dev/null &)