linux断开会话不中断进程,Linux让进程后台运行且连接断开不影响(nohup、setsid、disown、screen)...

背景

由于Linux的某些特性,例如可以多用户同时登陆、服务器运用广泛等,我们通常会用ssh去连接一台远程的Linux主机,或者在开发机上(本机)开多个terminal。而在我们运行一个耗时较长的任务时,如果因为网络原因,或其他未知异常导致终端连接断开,那我们的任务随即也会被kill掉。这是因为当你连接断开的时候,终端会收到一个SIGHUP信号,进而终端当前进程下的所有子进程,所以与之相关的任务将全被kill。而我们很多情况下想要的是在意外发生的时候,任务继续跑而不受父进程的影响。

元凶:SIGHUP 信号

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

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

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

会话期(session):一个或多个进程组的集合,有唯一一个会话期首进程(session leader)。会话期ID为首进程的ID。

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

根据POSIX.1定义:

挂断信号(SIGHUP)默认的动作是终止程序。

当终端接口检测到网络连接断开,将挂断信号发送给控制进程(会话期首进程)。

如果会话期首进程终止,则该信号发送到该会话期前台进程组。

一个进程退出导致一个孤儿进程组中产生时,如果任意一个孤儿进程组进程处于STOP状态,发送SIGHUP和SIGCONT信号到该进程组中所有进程。

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

解决方案

未雨绸缪派

nohup

通过背景分析,我们很容易想到,如果捂住终端的眼睛(让终端忽略SIGHUP信号),是不是就不会干掉子进程了?nohup就是干这样一件事情,其用法也很简单,命令前加上nohup即可:

nohup command &

示例:

➜ ~ nohup python -m SimpleHTTPServer 8421 &

[1] 7533

appending output to nohup.out

➜ ~ ps -ef | grep 7533

501 7533 6838 0 9:39AM ttys003 0:00.06 python -m SimpleHTTPServer 8421

501 7613 6838 0 9:39AM ttys003 0:00.00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn 7533

➜ ~ ls -al nohup.out

-rw------- 1 vien staff 0 Aug 30 09:39 nohup.out

说明:

1.后缀&是让其后台运行,但注意,后台运行不代表不受SIGHUP信号影响,连接断开的话依然会终止任务。

2.我们可以看到下面的appending output to nohup.out,然后在最后我列出了这个文件,可以看到就是刚刚创建的。任务所有的输出都将输出到这个文件中,当然也可以自定输出路径、文件名、内容等。

3.然后中间我列出了这个进程,可以观察到,其父进程ID与当前窗口下的进程ID是相同的6838 ,也就是说这个任务还是隶属于当前进程,这将与下面的方法有所不同。

重定向输出位置:nohup command > vienout.log 2>&1 &

由于使用nohup时,会自动将输出写入nohup.out文件中,如果文件很大的话,nohup.out就会不停的增大,我们可以利用Linux下一个特殊的文件/dev/null来解决这个问题,这个文件就相当于一个黑洞,任何输出到这个文件的东西都将消失

只保留输出错误信息 nohup command >/dev/null 2>err.log &

所有信息都不要 nohup command >/dev/null 2>&1 &

这里解释一下后面的2>&1 。 这涉及到Linux的重定向,其中0、1、2分别是标准输入、标准输出、标准错误输出,用来指定需要重定向的标准输入输出。默认情况下是标出输出,也就是1 。例如我们而上文提到的 2>&1 是 将错误信息重定向到标准输出。

setsid

上面讲的忽略SIGHUP信号,那如果我们给它(任务)换个爹(父进程)呢?这时候我们就可以用到setsid ,用户与上一个一样简单:

setsid command

示例:

➜ ~ setsid ping google.com

...(省略输出)

➜ ~ ps -ef | grep ping

moma 6186 1 0 09:55 ? 00:00:00 ping google.com

moma 8681 7988 0 09:59 pts/37 00:00:00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn ping

说明:

1.我们可以看到这里任务的父进程ID变成了1,也就是init进程,这样它爹就不会轻易挂掉了。

2.之所以省略了若干输出,是因为输出太多了,没有重定向导入/dev/null或者别的地方,可想而知,人家亲爹不要,换个了有钱的干爹,你再骂他说:“你个龟儿子,不听老子话,就不给你零花钱”,当然不会鸟你了,也就是你的Ctrl+C之类的命令都是不可用的,你签字(回车)的那一刻,这儿子就归别人了。

说明:setsid命令在Mac系统下是不存在的

() + &

江湖中传闻一个关于 subshell 的小技巧。我们知道,将一个或多个命名包含在“()”中就能让这些命令在子 shell 中运行中,从而扩展出很多有趣的功能。

当我们将"&"也放入“()”内之后,我们就会发现所提交的作业并不在作业列表中,也就是说,是无法通过jobs来查看的。

(ping command &)

这里就不做示例,通过上面两个例子这个很容易就是用了。而且这个基本跟第二个一样,也是换爹。

亡羊补牢派

刚刚讲的那些都是任务开始前搞事情,那么万一我们脑子一抽,手一抖,没有任何安全措施就一下子回了车呢?莫慌,还有这种操作。

disown

使某个作业忽略SIGHUP信号

disown jobspec

示例:

➜ ~ python -m SimpleHTTPServer 8877 &

[1] 20815

➜ ~ Serving HTTP on 0.0.0.0 port 8877 ...

➜ ~ jobs

[1] + running python -m SimpleHTTPServer 8877

➜ ~ disown %1

➜ ~ jobs

➜ ~

可以看到进程ID 20815前面有一个数字,加一个%即可

说明:这个命令是对作业(job)的操作,所以呢,首先你得把它变成job,很简单,如果你在运行前命令后面加了& 那恭喜你,现在他就是一个job,如果没有呢,也不要急,按下Ctrl+z 就可以把它挂到后台,成为一个job,此时我们可以通过jobs命令来查看所有的job。

关于job还有一些操作,bg %n是将job后台运行,fg %n是将job拿到前台运行,kill %n是杀掉这个job

需要注意的是,当使用过 disown 之后,会将把目标作业从作业列表中移除,我们将不能再使用jobs来查看它,但是依然能够用ps -ef查找到它。

不知道什么反正很吊派

screen 提供了 ANSI/VT100 的终端模拟器,使它能够在一个真实终端下运行多个全屏的伪终端。

screen

建立一个新session

screen

列出所有session

screen -ls

重新连接指定session

screen -r screen_pid

用快捷键CTRL + ad 来暂时断开当前session,CTRL + d 结束当前session。

示例:

➜ ~ screen

(此时跳转到新窗口,按Ctrl+ad暂时断开)

➜ ~ screen -ls

There is a screen on:

31665.pts-37.MBP (2017年08月30日 10时59分49秒) (Attached)

1 Socket in /var/run/screen/S-moma.

➜ ~ screen -r 31665

(恢复到screen_pid为31665的session)

➜ ~ ping google.com &

[1] 2184

(Ctrl+ad离开此session,然后查看进程树)

➜ ~ pstree -H 2184

systemd─┬─ModemManager─┬─{gdbus}

│ └─{gmain}

├─NetworkManager─┬─dhclient

│ ├─dnsmasq

│ ├─{gdbus}

...

├─rtkit-daemon───2*[{rtkit-daemon}]

├─screen───zsh───ping

├─sshd───sshd───sshd───zsh─┬─pstree

│ └─3*[python]

├─2*[systemd───(sd-pam)]

...

(此时我们再连接回刚刚的session可以看到还是在运行的)

说明:

1.通过上面的进程树,我们可以很容易观察到,screen───zsh───ping是直接挂在systemd下的,这也就是不会受SIGHUP信号影响的原因,这跟setsid的原理是一样的,前文提到是init进程实在Linux下的,当前是systemd是因为在实在Mac系统下操作的。

2.还有就是这个命令如果机器上没有的话需要安装,Ubuntu的话是sudo apt-get install screen

3.这个命令是十分强大的,这里只是简单介绍,还有很多神奇的操作,想要玩爽还得自己用man screen去看

参考和引用:

Linux 后台运行

我们常常会用终端连接Linux服务器,然后在运行类似Tomcat 、Web Logic等 web容器的时候希望退出终端依然可以运行。

我们可以通过 nohup command & 来使程序后台运

以Tomcat为例nohup ./startup.sh &

然后在shell中提示了nohup成功后:

nohup: ignoring input and appending output to ‘nohup.out’

然后按键盘任意键,回到shell输入命令窗口,然后在shell中输入

exit

退出终端,这时候,你的Tomcat就作为后台服务挂在Linux上了。

注意:不要执行完nohup直接点击关闭程序关闭终端,这样会干掉该命令的session,导致nohup对应的进程被通知一起被干掉,从而导致后台运行失败。

上文提到过nohup成功后的提示:nohup: ignoring input and appending output to ‘nohup.out’

默认情况下nohup的作业的所有输出会被重定向到 nohup.out这个文件中,当然,你也可以指定输出:

nohup command > vienout.txt 2>&1 &

由于使用nohup时,会自动将输出写入nohup.out文件中,如果文件很大的话,nohup.out就会不停的增大,我们可以利用Linux下一个特殊的文件/dev/null来解决这个问题,这个文件就相当于一个黑洞,任何输出到这个文件的东西都将消失

只保留输出错误信息 nohup command >/dev/null 2>log &

所有信息都不要 nohup command >/dev/null 2>&1 &

这里解释一下后面的2>&1 。 这涉及到Linux的重定向,其中0、1、2分别是标准输入、标准输出、标准错误输出,用来指定需要重定向的标准输入输出。默认情况下是标出输出,也就是1 。例如我们而上文提到的 2>&1 是 将错误信息重定向到标准输出。

还有就是如果不想让程序输出,Linux下有一个/dev/null的特殊文件,就像一个黑洞,所有输出到这个文件的信息全部会消失,如果你不需要输出日志,这样做就不会导致输出日志文件越来越大,占用存储空间的问题了

关于Linux,新手想继续了解一些知识的话,推荐看一下《鸟哥的Linux私房菜》,讲的简单易懂,适合入门和作为工具书平时查阅

附:

ctrl+c #结束当前任务

ctrl+z #挂起当前任务

jobs -l #查看任务,返回任务编号 和 进程号

bg %n #编号n的任务转向后台运行,实际上bg n 也可以

fg %n #编号n的任务转向前台运行

转载请注明来源:

viencoding.com版权所有,允许转载,但转载请注明出处和原文链接: https://viencoding.com/article/6

欢迎小伙伴们在下方评论区留言 ~ O(∩_∩)O

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值