声明:本文首发 简单教程,网址为 https://www.twle.cn/t/332#reply0
就在刚刚回家的路上,被前同事夺命三连 call 呼唤解决一个问题:为啥放在 crontab 里的命令放在 Shell 会进入假死状态?
那我就问了?什么是假死状态? 同事说,就是一直不会执行完毕,占着 Shell,不能做其它事情。
多次沟通后,才知道他写的这个是一个守护进程似的死循环程序,一旦启动除非发生意外,否则不会自己退出。
那肯定,很显然,会占着 Shell 啊。
nohup 和 &
我给的最简单的解决方案,就是按下组合键 CTRL+Z
。
其次给的是在执行的命令后添加一个 &
符号。 这个是最简单的后台执行模式,而且 &
号之前有无空格都没多大关系,之后有无空格也没关系。
如果只是告诉它这样,肯定是会出问题的,你能猜到么 ?
大家想到的第一个问题,肯定是退出 Shell 进程就会自动关闭。对的,没错,同事后来给我的答复是退出了进程还存在...囧,我一时不知道怎么解释,山高皇帝远,又没法远程和亲自查看。
但是,同事给我的第一个问题是:为啥一直输出日志啊?
我才想起来,忘记告诉它要 >/dev/null 2&>1
重定向日志。
后来又吧啦吧啦告诉它还可以使用 nohup
把要执行的命令扔到后台。
结果呢? 不说还好,一说人家就开问了,这两个命令有啥区别啊 ?
啊 ? 区别?
&
和nohup
都能把一个任务放在后台运行,但&
命令会随着退出远程 Shell 而自动停止任务,而nohup
则会一直继续运行,除非显示杀掉或者重启电脑
等等,这就完了?
我心慌了...只好回来继续查资料了。
Linux 程序生命周期里最重要的三个信号
来,考考大家,我们平时按的最多的两个组合键 CTRL+C
和 CTRL+Z
分别会产生什么信号 ?
当我们退出远程 Shell 时,又会产生什么信号 ?
可以说,Linux 下有很多信号,但真正常用的且能够影响一个程序的信号就只有那么几个,比如
信号 | 说明 |
---|---|
SIGINT | 发送给前台进程组中的所有进程。常用于终止正在运行的程序,一般由 CTRL+C 组合键触发 |
SIGTSTP | 发送给前台进程组中的所有进程,常用于挂起并暂停一个进程,一般由 CTRL+Z 组合键触发 |
SIGHUP | 当用户退出 Shell 时,由该 Shell 开启的所有进程都会接收到这个信号,默认动作为终止进程 |
虽然 SIGINT 和 SIGHUP 的默认动作都一样,但人家是两个不同的信号,一定要牢记,不然等下你会分不清楚 &
和 nohup
的区别的
&
的运行原理
经过上面的解释,想必你应该也清楚了,给任何命令加上 &
就会让该命令在后台执行
比如我们要在当前目录运行一个 Python3 服务器,命令一般如下
[yufei@www.twle.cn ~]$ python3 -mhttp.server 7777
Serving HTTP on 0.0.0.0 port 7777 (http://0.0.0.0:7777/) ...
这时候会发现,这个命令始终没有退出,我们没法继续在这个 Shell 上做其它事情,怎么办呢 ?
一种就是关闭当前 Shell,然后重新开启一个 Shell 执行相同的命令,并在命令的末尾添加 &
符号
[yufei@www.twle.cn ~]$ python3 -mhttp.server 7777 &
[1] 27203
这时候就会看到,我们的命令并没有占据 Shell 而是返回了一个进程号 [1] 27203
。[1]
表示任务 ID,而 27203
则表示进程号,我们可以使用 ps aux | grep python
查看下刚刚的命令是否仍在运行
[yufei@www.twle.cn ~]$ ps aux | grep python
luojianguo 27221 0.0 0.0 4286184 916 s007 S+ 10:04下午 0:00.01 grep python
luojianguo 27203 0.0 0.1 4263348 3384 s007 S 9:56下午 0:00.35 /usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/Resources/Python.app/Contents/MacOS/Python -mhttp.server 7777
看到 27203 了没有,对就是刚刚我们运行的命令。
至于任务 ID [1]
,在 Linux 中,每一个后台运行的程序,都叫一个任务,当在后台运行时,会分配一个任务 ID
我们可以使用 jobs
命令查看所有后台任务
[yufei@www.twle.cn ~]$ jobs
[1]+ Running python3 -mhttp.server 7777 &
这时候我们狂按下 CTRL+C
键会发现一定作用都没有,人家还是静静的运行着,从某些方面说 CTRL+C
发送的 SIGINT
是发送给所有前台进程的,后台运行的收不到也是自然的现象
然后,我们关闭 Shell,并重新打开 Shell,就会发现刚刚运行的后台任务也关闭了
[yufei@www.twle.cn ~]$ ps aux | grep python
luojianguo 27501 0.0 0.0 4295984 660 s000 U+ 10:11下午 0:00.00 grep python
因此,可以说 &
运行的后台任务,会接收 SIGHUP
的信号,也就是对 SIGHUP
信号不免疫。
注意,如果你在本地演示,会发现进程还在,因为,本地 Shell 即使全部退出,Shell 会把这些后台任务交给登陆用户的 shell 去打理
nohup 并不会把程序放到后台执行
注意,很多文章都没有解释清楚,包括上面我的说法也是错的
「 nohup 并不会把程序放在后台运行 」
比如,我们使用下面的命令
[yufei@www.twle.cn ~]$ nohup python3 -mhttp.server 7777
appending output to nohup.out
除了提示已经把标准输出和标准错误输出到 nohup.out 文件外,人家还占据着 Shell 呢
如果你这后按下 CTRL+C
键就会发现程序立刻就退出了
[yufei@www.twle.cn ~]$ nohup python3 -mhttp.server 7777
appending output to nohup.out
^C[yufei@www.twle.cn ~]$
使用 ps aux | grep python
就会看到刚刚运行的程序没了
[yufei@www.twle.cn ~]$ ps aux | grep python
luojianguo 27911 0.0 0.0 4258468 188 s001 U+ 10:42下午 0:00.00 grep python
nohup 的真正作用是让程序忽略 SIGHUP
信号
nohup 命令的作用就是让程序忽略 SIGHUP
信号,但这里有一个前提,就是进程首先是在后台运行的,而不是暂停或挂起的
我们演示下一下,使用下面的命令运行一个 nohup
[yufei@www.twle.cn ~]$ nohup python3 -mhttp.server 7777
appending output to nohup.out
然后按下使用 CTRL+Z
命令先挂起当前 nohup
的命令
[yufei@www.twle.cn ~]$ nohup python3 -mhttp.server 7777
appending output to nohup.out
[1]+ Stopped nohup python3 -mhttp.server 7777
可以看到我们的程序在后台是暂停的,但没有退出,如果我们使用 exit
命令,会提示有停止任务
[yufei@www.twle.cn ~]$ exit
logout
There are stopped jobs.
如果在此输入 exit
就会直接退出 Shell 并且会把所有的已经停止的程序关闭
[yufei@www.twle.cn ~]$ exit
logout
Saving session...
...copying shared history...
...saving history...truncating history files...
...completed.
[进程已完成]
然后我们重新开启一个 Shell 并使用 ps aux
命令,就会什么也看不到
[yufei@www.twle.cn ~]$ ps aux | grep python
luojianguo 28095 0.0 0.0 4295400 936 s000 S+ 10:57下午 0:00.01 grep python
结合 nohup 和 &
如果我们过结合两者,比如使用下面的命令
[yufei@www.twle.cn ~]$ nohup python3 -mhttp.server 7777 &
[1] 28097
[yufei@www.twle.cn ~]$ appending output to nohup.out
然后我们运行 exit
命令退出当前 Shell
[yufei@www.twle.cn ~]$ appending output to nohup.out
exit
logout
Saving session...
...copying shared history...
...saving history...truncating history files...
...completed.
打开一个新的 Shell,输入 ps aux | grep python
,就会发现之前的程序还存在
luojianguo 28171 0.0 0.0 4295984 668 s000 U+ 10:59下午 0:00.00 grep python
luojianguo 28097 0.0 0.3 4271284 12556 ?? S 10:58下午 0:00.26 /usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/Resources/Python.app/Contents/MacOS/Python -mhttp.server 7777
神奇吧
后记
真的很坑,我一直以为这两个命令都是用来把前台程序放到后台的,结果不是
- 只有
&
是把前台程序放到后台的 nohup
只是把让正在运行的程序忽略SIGHUP
信号而已
声明:本文首发 简单教程,网址为 https://www.twle.cn/t/332#reply0