管理linux的后台运行程序

怎么在退出SSH后,让当前程序继续执行呢?方法有三个:nohup, setsid, ctrl-z + bg + disown。

在我们通过SSH登陆服务器后,一般来说,所做的操作或者命令的输入都是属sshd下的shell的子进程,例如打开个SSH终端,输入ping www.esojourn.org >>output.txt &,然后查看进程情况:
$ ps -ef|grep ping
sszheng 27491 27467 0 10:20 pts/0    00:00:00 ping www.esojourn.org
sszheng 27535 27467 0 11:40 pts/0    00:00:00 grep ping

    很显然它是shell的子进程,命令由一个子shell在后台执行,当前shell( 27467)立即取得控制等候用户输入,所以我的grep就可以使用了。后台命令和当前shell的执行是并行的,他们没有互相的依赖、等待关系,所以是异步的并行。 现在问题来了,如果ssh退出了,bash结束了,那么这个工作过程如何呢?后台执行的能否继续下去?

    这里涉及到两个问题,就是退出ssh后,在我们exit执行的shell时候,会不会向我们后台的jobs发送SIGHUP信号呢?
如果发送了 SIGHUP信号,那么所有该shell下运行的进程都会被终止,也就是所希望的后台执行没有实现。在shell的options中,有 huponexit这个选项,意思就是退出shell时候,是否发送这个SIGHUP 信号?
$ shopt
cdable_vars     off
cdspell         off
checkhash       off
checkwinsize    off
cmdhist         on
dotglob         off
execfail        off
expand_aliases on
extdebug        off
extglob         off
extquote        on
failglob        off
force_fignore   on
gnu_errfmt      off
histappend      off
histreedit      off
histverify      off
hostcomplete    on
huponexit       off
interactive_comments    on
lithist         off
login_shell     on
mailwarn        off
no_empty_cmd_completion off
nocaseglob      off
nocasematch     off
nullglob        off
progcomp        on
promptvars      on
restricted_shell        off
shift_verbose   off
sourcepath      on
xpg_echo        off
    上面的默认选项中,huponexit       off,这个情况时候,当你退出shell时候,后台的程序还会继续运行,但是这个是全局选项,有时候我们往往希望退出shell后,shell发起的进程相应结束了,而不是一直运行,因为有时候你可能开了很多子进程,没有时间去一一关闭吧??往往这个选项是建议打开的。

huponexit打开后,所以后台进行的jobs,在shell退出后就会相应退出了,但是针对我们特定的任务时候,我们可以对它进行单独操作,可以有下面集中方法。
1、nohup
nohup的用途就是让提交的命令忽略 hangup 信号,使用方法:
$nohup ping www.esojourn.org &
如果没有重定向输入和输出的话,标准输出和标准错误缺省会被重定向到 nohup.out 文件中。一般像示例一样,加上”&” 来将命令同时放入后台运行,也可用”>filename 2>&1″ 来更改缺省的重定向文件名。退出shell后,ping会继续运行,直到命令执行结束。
$ ps -ef |grep ping      
sszheng   5377 5311 0 16:51 pts/1    00:00:00 ping www.esojourn.org
sszheng   5379 5311 0 16:51 pts/1    00:00:00 grep ping
退出shell后,重新登陆查看,ping进程依然在执行,只不过他的PPID变成了1,也就是被init所管理的孤儿进程了,稍后说一下孤儿进程。
$ ps -ef |grep ping
sszheng   5377     1 0 16:51 ?        00:00:00 ping www.esojourn.org
sszheng   5389 5383 0 16:52 pts/0    00:00:00 grep ping

2、setsid

    nohup是通过忽略 HUP信号来使进程避免中途被中断,也可以用另一种方法,进程是不属于接受 HUP 信号的终端的shell子进程,那么自然也就不会受到 HUP 信号的影响了,真是白猫黑猫,抓到老鼠就是好猫,呵呵,废话多了。

    shell提供了setsid这个方法,

$setsid ping www.esojourn.org & >>163.txt

$ ps -ef |grep ping

sszheng   5377     1 0 16:51 ?        00:00:00 ping www.esojourn.org
sszheng   5395     1 0 16:56 ?        00:00:00 ping www.esojourn.org
sszheng   5397 5383 0 16:57 pts/0    00:00:00 grep ping

大家应该注意到,上一个示例中,ping的父进程是5311,当它的父进程退出后,它才被init(PID=1)收养,而setsid直接把ping(pid=5395)给init了,那么就无所谓的shell退出影响了。

3、(&)

    再提一下关于subshell的使用,我们知道,将一个或多个命名包含在“()”中就能让这些命令在子 shell 中运行中,当我们将”&”也放入“()”内之后,我们就会发现所提交的作业并不在作业列表中,也就是说,是无法通过jobs 来查看的。看看下面的进程id就知道了:

$(ping www.esojourn.org &)

$ ps -ef |grep ping
sszheng   5377     1 0 16:51 ?        00:00:00 ping www.esojourn.org
sszheng   5395     1 0 16:56 ?        00:00:00 ping www.esojourn.org
sszheng   5401     1 0 17:03 pts/0    00:00:00 ping www.esojourn.org
sszheng   5403 5383 0 17:03 pts/0    00:00:00 grep ping
可以看到,执行的5401的父进程是init了,这样子也可以达到忽略hup信号的目的了。

    说到这里,相信大家都略明白后台执行的方法了,简单说下原理:bash进程终止后,init 进程会接管父进程留下的这些“孤儿进程”,所以PPID是1了,孤儿进程不是僵尸进程,下面是他们的概念和区别
僵尸进程 :一个子进程在其父进程还没有调用wait()或waitpid()的情况下退出。这个子进程就是僵尸进程。
孤儿进程 :一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。

简要说一下screen,它也是后台执行的一个重要工具,只不过它的功能远远不止后台执行而已。
    Screen是一个可以在多个进程之间多路复用一个物理终端的窗口管理器。Screen中有会话的概念,用户可以在一个screen会话中创建多个 screen窗口,在每一个screen窗口中就像操作一个真实的telnet/SSH连接窗口那样。在screen中创建一个新的窗口有这样几种方式:

1.直接在命令行键入screen命令
$ screen

    Screen将创建一个执行shell的全屏窗口。你可以执行任意shell程序,就像在ssh窗口中那样。在该窗口中键入exit退出该窗口,如果这是该screen会话的唯一窗口,该screen会话退出,否则screen自动切换到前一个窗口。

2.Screen命令后跟你要执行的程序。
$ screen vi test.sh

Screen创建一个执行vi test.sh的单窗口会话,退出vi将退出该窗口/会话。

3.以上两种方式都创建新的screen会话。我们还可以在一个已有screen会话中创建新的窗口。在当前screen窗口中键入C-a c ,即Ctrl键+a键,之后再按下c键,screen 在该会话内生成一个新的窗口并切换到该窗口。
具体用法就不说了,既然不属于ssh管理,那何来接受退出信号呢?请注意会话这个概念。

    有兴趣的童鞋们,还可以看看disown相关的东东,这个东东的需求是: 如果事先在命令前加上 nohup 或者 setsid 就可以避免 HUP 信号的影响。但是如果我们未加任何处理就已经提交了命令,该如何补救才能让它避免 HUP 信号的影响呢?
我们可以用如下方式来达成我们的目的。

    * 用disown -h jobspec 来使某个作业 忽略HUP信号。
    * 用disown -ah 来使所有的作业 都忽略HUP信号。
    * 用disown -rh 来使正在运行的作业 忽略HUP信号。

上面的方式的对象是作业,如果我们在运行命令时在结尾加了”&” 来使它成为一个作业并在后台运行,我们可以通过jobs 命令来得到所有作业的列表。但是如果并没有把当前命令作为作业来运行,如何才能得到它的作业号呢?答案就是用 CTRL-z(按住Ctrl键的同时按住z键)了!

CTRL-z 的用途就是将当前进程挂起(Suspend),然后我们就可以用jobs 命令来查询它的作业号,再用bg jobspec 来将它放入后台并继续运行。需要注意的是,如果挂起会影响当前进程的运行结果,就不推荐大家使用了。

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

Time limit is exhausted. Please reload CAPTCHA.