linux后台执行

来源:岁月联盟 编辑:exp 时间:2011-10-31

 

最近被很多人问,想退出secureCRT后,能够继续跑自己的进程,应该怎么实现,可以说这个方法不少,我就随便说说吧。

 

     为什么会有这样的需求?作为系统管理员,经常遇到这样的问题,用telnet/ssh 登录了远程的Linux 服务器,需要运行了一些耗时较长的任务,例如批量ping一些网段之类, 有时候却由于网络的不稳定导致任务中途失败,或者需要中途离开,总不会在等它结束吧,如果你退出SSH登陆的话,那么你的任务也会被终止了,岂不是白费精 力了?如何让命令或者任务在后台自己的运行,可以有很多方式实现,向大家都不陌生了,例如nohup,setsid和screen等等,我就简单说说吧。

 

    在我们通过SSH登陆服务器后,一般来说,所做的操作或者命令的输入都是属sshd下的shell的子进程,例如打开个SSH终端,输入ping www.163.com >>output.txt &,然后查看进程情况:

$ ps -ef|grep ping

sszheng 27491 27467 0 10:20 pts/0    00:00:00 ping www.163.com

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.163.com &

如果没有重定向输入和输出的话,标准输出和标准错误缺省会被重定向到nohup.out 文件中。一般像示例一样,加上"&"来将命令同时放入后台运行,也可用">filename 2>&1"来更改缺省的重定向文件名。退出shell后,ping会继续运行,直到命令执行结束。

$ ps -ef |grep ping      

sszheng   5377 5311 0 16:51 pts/1    00:00:00 ping www.163.com

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.163.com

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.163.com & >>163.txt

 

$ ps -ef |grep ping

 

sszheng   5377     1 0 16:51 ?        00:00:00 ping www.163.com

sszheng   5395     1 0 16:56 ?        00:00:00 ping www.163.com

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.163.com &)

 

$ ps -ef |grep ping

sszheng   5377     1 0 16:51 ?        00:00:00 ping www.163.com

sszheng   5395     1 0 16:56 ?        00:00:00 ping www.163.com

sszheng   5401     1 0 17:03 pts/0    00:00:00 ping www.163.com

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来将它放入后台并继续运行。需要注意的是,如果挂起会影响当前进程的运行结果,就不推荐大家使用了。

 

 

 

就说到这了,说错了,请指点。

 

teddy.xiong MAIL: cug@live.cn