Linux让进程后台运行且连接断开不影响(nohup、setsid、disown、screen)

2019-02-14 09:53:20   Linux

背景

由于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的任务转向前台运行

转载请注明来源:

更多:免费搭梯子(ss+outline)教程

viencoding.com版权所有,允许转载,但转载请注明出处和原文链接: https://viencoding.com/article/6
欢迎小伙伴们在下方评论区留言 ~ O(∩_∩)O
文章对我有帮助, 点此请博主吃包辣条 ~ O(∩_∩)O

猜你喜欢


评论

There are no comments yet.
未登录

登录后即可发表评论

登录或注册

标签

AdSense Anaconda Android apache API apt Auth AWS B-tree Bandwagon Blog bower brew bytes Caffe Catalina certbot cloudcone Composer conda CoreML CPU crontab CSS csv Cuda cv2 datetime Digitalocean DNS Docker Docker-Compose Eloquent Excel export Flask FTP GET Git GitHub GitLab Gmail GoDaddy Google GTM hash Homebrew Homestead HTML http HTTPS IDEA image imagemagick imagick imgick import InnoDB ios iou iPhone ISO8601 iTerm2 Java JavaScript JPG JS Keras Laravel Laravel-Admin lazyload Linux list Livewire lnmp load logs Lravel Mac Markdown matplotlib md5 mix MobileNet Mojave mongo MongoDB MySQL Namesilo Nginx Node npm numpy Nvidia Nvidia-Docker onevps OpenCV Openpose openpyxl oss Outline parse PayPal PHP php-fpm PhpStorm PHP扩展 PIL Pillow pip PNG POST Protobuf PyCharm pyenv pymongo Python Python,人工智能,机器学习,VOC,xml Queue Redis requests RGB Sanctum save selenium SEO Shadowsock Shadowsocks ShadowsocksR simplemde Spring Boot SQLServer ssd SSH ssl SSL证书 SSR str Sublime sudo swap Swift Tensorflow TensorflowLite Terminal Terminator timestamp Ubuntu urllib UTC v2ray Valet Validation Validator VienBlog virtualenvs VPN VPS Vultr Web Windows WordPress Xcode xlsx yaml YAPI YUV zip zmq zsh 下载图片 主从同步 云主机 云存储 云开发 云服务器 人工智能 优化 优惠码 伪原创 作弊与反作弊 免费ss账号 免费提现 切片 前端 加密 协议 博客 友链 双击事件 后台运行 后端 命令 国内镜像源 图标 图片操作 图片转换 域名 多身份认证 大小写转换 姿态检测 安卓模拟器 安装 定时任务 定时执行 密码 密钥 导出导入 小程序码 延迟加载 异常 微信 微信小程序 快捷方式 慢查询 懒加载 提现 搜索引擎 搬瓦工 搭梯子 教程 数据库 数据重复 文件上传 无法登录 日志 日期 时区 时间 时间戳 服务器 机器学习 权限 梯子 模拟浏览器 港版支付宝 漏洞 爬虫 生活服务 用户管理 病毒 登录 目标检测 科学上网 系统升级 索引 组件 组件开发 编辑器 自动付款 自定义组件 英文伪原创 计划任务 计算机视觉 订阅通知 认证 语法 读写分离 远程连接 配置文件 重定向 错误异常 错误提示 队列 阿里云 香港 香港手机号
亲情非友情链接