早在我年轻的时候,Unix就是新事物,创build一个在注销时不会被杀的进程是一个挑战。 我们使用nohup命令来保护HUP
信号中的持续进程。 如果我们不小心,当我们注销时,我们的进程就会被杀死,甚至closures我们启动它们的shell。
快进到今天,我发现我很惊讶,默认情况恰恰相反。 在Ubuntu和Red Hat系统上,我发现我几乎可以在后台放置任何进程,杀死父shell,注销,任何事情,只是继续下去。 我看到与Bash脚本,Python脚本和C程序相同的行为。 我从xterm或ssh会话中获得相同的行为。
例如,在xterm或ssh窗口中input:
while [ 1 ]; do date; sleep 10; done > /tmp/out &
现在从另一个窗口运行
tail -f /tmp/out
看它每10秒打印一次date,然后用Ctrl-Dclosures原来的父shell。 仍在运行。 注销并返回。仍在运行。
发送一个HUP
信号,它立即死亡。
我可以展示与Python脚本或C程序相同的行为。 睡或不要紧。 例如,这个丑陋的C程序行为相同:
#include <stdio.h> void main() { while(1) { printf("*\n"); fflush(stdout); int i, j, k = 0; for(i=0; i < 10000; i++) { for(j=0; j < 100000; j++) { k += i * j; } } } }
这完全违背了我年轻的方式。 我想我只是没有注意到它改变了? 任何历史学家都知道这是什么时候发生的? HUP
甚至可以用于这个目的吗?
如果确实这是事情的当前状态,我的问题是: 当用户注销或断开连接时,如何安排进程死掉?
我有一个黑客,涉及ppid
(父母pid)的变化,但肯定有一些比这更优雅。
我相信你正在寻找huponexit
shell选项。 你可以很容易地设置
$ shopt -s huponexit
来自bash手册页的一些细节:
收到SIGHUP后,shell会默认退出。 在退出之前,交互式shell将SIGHUP重新发送到所有正在运行或停止的作业。 停止的工作发送SIGCONT,以确保他们收到SIGHUP。 为防止shell将信号发送到特定的作业,应将其从作业表中删除(参见下面的SHELL BUILTIN COMMANDS),或者使用disown -h标记为不接收SIGHUP。
如果已经使用shopt设置了huponexit shell选项,则当交互式登录shell退出时,bash将向所有作业发送SIGHUP。
我仍然得到SIGHUP。 一个简单的测试方法:
#!/usr/bin/env sh echo "$$" trap "echo HUPPED $$ > /tmp/willithup" HUP sleep 1000
然后关闭终端仿真器。 现在回到你的问题:
看它每10秒打印一次日期,然后用Ctrl-D关闭原来的父shell。 仍在运行。 注销并返回。仍在运行。
当父母死亡时,这个过程不会得到HUP。 当它失去与控制终端的连接或者当它被明确地发送一个HUP时它获得一个HUP。 例如,当您注销SSH时会发生这种情况。
如果我们不小心,当我们注销时,我们的进程就会被杀死,甚至关闭我们启动它们的shell
对于第二个:壳本身可以发出一个HUP所有的孩子,当它退出。 但是,例如,bash默认情况下将huponexit
设置为false。 这很可能是已经改变的。 请注意,无论huponexit选项如何, 当它收到HUP时,shell也会向其所有子项发送一个HUP 。
用史蒂文斯的话来说:
一个会话可以有一个控制终端。 这通常是我们登录的终端设备(在终端登录的情况下)或伪终端设备(在网络登录的情况下)。
如果终端接口检测到调制解调器(或网络) 断开连接,则挂起信号被发送到控制进程(会话前导)。
为了进一步澄清, 最初的HUP不是由shell发送的。 它由终端驱动程序发送。 之后,贝壳“转发”给孩子们。 因此,终端驱动程序发送的HUP确实可以“级联”。 从TLPI:
当一个控制进程失去终端连接时,内核发送一个SIGHUP信号通知它这个事实 。 (SIGCONT信号也被发送,以确保过程在信号先前被停止的情况下重新启动。)通常,这可能发生在两种情况下:
- 当终端驱动程序检测到“断开”时,表示调制解调器或终端线路上的信号丢失。
- 在工作站上关闭终端窗口时。 发生这种情况是因为与终端窗口关联的伪终端的主侧的最后打开的文件描述符被关闭。