SKLCC’s 藏经阁

We are SKLCCers, we share our knowledge with each other.

如何在Linux后台运行一个程序

| Comments

From Andy at SuZhou sklcc.com

我们经常会遇到这么一种情况:ssh/telnet到远程的一台主机,然后执行了一个需要长时间运行的程序,但是由于网络原因导致连接不稳定,在当连接断了之后,我们在远端主机上运行的这个程序也会被终止。这时我们就希望能把它转至后台运行,并且当客户端失去连接之后也不会中断程序。

一般后台运行

要让一个程序在后台运行有好几种方法,首先就是ctrl+z,使用ctrl+z会让程序暂时挂起在后台,然后可以看到系统提示:

[1]+ Stopped /root/bin/rsync.sh

其中’[1]‘是后台作业号,我们可以通过使用bg命令来让它在后台运行:bg 1 # bg + 后台作业号,然后我们可以通过jobs命令来查看所有后台程序的运行状态

jobs
[1]+ Running /root/bin/rsync.sh &

如果想把它调回前台可以使用fg 1,当然这样之后就只能在控制台中等待这个程序运行结束。

总结一下就是:

ctrl+z 把程序挂起在后台(此时为挂起状态,在后台但是不会继续运行)
bg %jobnumber 把任务在后台运行起来
fg %jobnumber 把任务转到前台运行
jobs 查看当前后台所有的任务的状态
& 在命令之后加上这个符号表示让此命令运行在后台(但是命令的输出还是会从标准输出显示,也就是会出现在控制台)

客户端失连之后程序退出的原因

上面提到的方法只是能够让程序在后台运行,但是当客户端失去连接之后程序还是会被终止,原因是由于用以上方法在后台执行程序,程序的父进程还是当前终端的进程,所以当当前终端退出的时候(也就是客户端失去连接的时候),也就意味着程序的父进程退出了,此时系统会发送hangup型号给所有的子进程,子进程收到此消息之后也会退出程序,所以如果我们希望程序在客户端失去连接之后仍然能运行的话,那么只有两种方法:

  1. 让程序忽略hangup信号
  2. 让程序的父进程是init进程,而不是当前终端的进程

解决方案

忽略hangup信号

使用nohup命令可以实现让命令忽略hangup信号,在它的man-page中也有说明:

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

用法也很简单,只需要在原来的命令前面加上nohup就行了,例如:nohup ./test & 如果手动重定向标准输出的话,nohup命令会自动把命令的标准输出重定向到当前目录的nohup.out文件中

BTW,在重定向输出的时候,1表示标准输出,2表示错误输出,0表示标准输入,如果需要重定向一个程序的标准输出的话只需要./test 1>test.out &即可,如果需要让一个程序在后台运行并且不会有任何输出到终端的话,需要把错误输出也重定向,即./test 1>test.log 2>test.err &,如果不需要记录命令的输出信息的话,可以重定向到/dev/null文件,这是linux中的一个黑洞文件,任何输出到这个文件的信息都会被吞噬,即./test &>/dev/null &,这条命令也等于./test 1>/dev/null 2>&1,现在后者见的比较多,前者我在baidu和bing中都没怎么搜的到,但我记得是可以的。在后者中,先是把标准输出重定向到黑洞设备,然后把错误输出重定向到标准输出,这样所有输出都会被吃掉了。

父进程为init

使用setsid命令就可以让命令的父进程为init,在它的man-page的说明是:

setsid - run a program in a new session

至于它的使用方法也很简单,和nohup一样,只要在原来的命令之前加上setsid即可,例如:

1
2
3
4
$setsid ./test &  
$ps -ef | grep test  
sklcc 2105    1   0 12:58 ?   00:00:00 ./test &  
sklcc 2108    2090    0 12:58 pts/0   00:00:00 grep --color=auto test  

另外还有一个方法也可以实现同样的效果,就是加括号,例如(./test &)。除了以上提到的3种方法之外,还有一种方法就是写成service,关于如何写service,在下篇博客会进行介绍。

总结一下:

nohup 让程序忽略hangup信号从而让程序可以继续运行
setsid 让程序在一个新的session中运行,所以父进程就不会是当前终端
(shell) 与setsid一样的效果

Comments