如果我们关闭它启动的终端,linux 会杀死后台进程吗?

Posted

技术标签:

【中文标题】如果我们关闭它启动的终端,linux 会杀死后台进程吗?【英文标题】:Does linux kill background processes if we close the terminal from which it has started? 【发布时间】:2015-12-23 04:46:48 【问题描述】:

我有一个嵌入式系统,我在上面执行telnet,然后在后台运行一个应用程序:

./app_name &

现在如果我关闭我的终端并从其他终端执行telnet,如果我检查然后我可以看到这个进程仍在运行。

为了检查这一点,我编写了一个小程序:

#include<stdio.h>
main()

    while(1);

我在本地 linux 电脑上后台运行了这个程序,然后我关闭了终端。

现在,当我从其他终端检查这个进程时,我发现这个进程也被杀死了。

我的问题是:

为什么同一类型的进程有未定义的行为? 它依赖于哪个? 是否依赖于 Linux 版本?

【问题讨论】:

【参考方案1】:

AFAIK 在这两种情况下都应该终止进程。为了避免这种情况,您必须发出如下的 nohup:

> nohup ./my_app &

这样您的流程将继续执行。可能telnet部分是由于一个类似这个的BUG:

https://bugzilla.redhat.com/show_bug.cgi?id=89653

【讨论】:

【参考方案2】:

为了完全了解正在发生的事情,您需要稍微了解一下unix 内部结构。

当你运行这样的命令时

./app_name &amp;

app_name 被发送到后台进程组。你可以查看unix进程组here

当您通过正常退出关闭bash 时,它会触发SIGHUP 挂断信号到其所有作业。关于unix作业控制的一些信息是here。

为了在您退出 bash 时保持应用运行,您需要使用 nohup 实用程序使您的应用免受挂断信号的影响。

nohup - 运行不受挂断影响的命令,输出到非 tty

最后你需要这样做。

nohup app_name &amp; 2&gt; /dev/null;

【讨论】:

Bash 并不总是(曾经?)在正常退出的情况下向所有作业发送 SIGHUP。它会从它运行的终端转发 SIGHUP。请参阅我对这个问题的回答:serverfault.com/questions/117152/…。奇怪的是,至少在 CentOS7.1 中,即使使用 shopt -s huponexit,我还没有看到 bash 发送 SIGHUP。当 bash 以/bin/sh 运行时我也没有看到它,我也没有看到它与/bin/csh 一起运行。 如果我从 Java ProcessBuilder 运行后台进程,后台 linux 进程是否会通过此信号终止?【参考方案3】:

当你关闭终端时,shell 会向所有后台进程发送SIGHUP——这会杀死它们。这可以通过多种方式抑制,最明显的是:

nohup

当您使用nohup 运行程序时,它会捕获SIGHUP 并重定向程序输出。

$ nohup app &

否认

disown 告诉 shell 不要发送SIGHUP

$ app &
$ disown

是否依赖于linux版本?

这取决于你的外壳。以上至少适用于 bash

【讨论】:

注意,shell 通常仅在收到 SIGHUP 本身时才发送 SIGHUP(请参阅我的回答)。据我了解,OP 的问题是 shell 要么 not 接收或不传播SIGHUP【参考方案4】:

谁应该杀死工作?

通常,前台和后台作业在不同情况下被内核或shell发送的SIGHUP杀死。


内核什么时候发送SIGHUP

内核SIGHUP发送到controlling process

对于真实(硬件)终端:当在终端驱动程序中检测到断开连接时,例如在调制解调器线路上挂断; for pseudoterminal (pty):当最后一个引用 pty 主端的描述符关闭时,例如当你关闭终端窗口时。

内核SIGHUP发送到其他进程组:

前台进程组,当控制进程终止时; 到orphaned process group,当它成为孤立的并停止成员时。

控制进程是与控制终端建立连接的会话领导者。

通常,控制进程是您的 shell。所以,总结一下:

当真实或伪终端断开/关闭时,内核将SIGHUP 发送到外壳; 当shell终止时内核发送SIGHUP到前台进程组; 如果包含已停止的进程,内核会将SIGHUP 发送到孤立的进程组。

请注意,如果内核SIGHUP 发送到后台进程组,如果它不包含停止的进程。


bash 何时发送SIGHUP

BashSIGHUP 发送到所有 个作业(前台和后台):

当它接收到SIGHUP,并且它是一个交互式shell(并且作业控制支持在编译时启用); 当它退出时,它是一个交互式登录 shell,并设置了huponexit 选项(并且在编译时启用了作业控制支持)。

查看更多详情here。

注意事项:

bashSIGHUP发送到使用disown从工作列表中删除的工作; 使用nohup 启动的进程忽略 SIGHUP

更多详情here.


其他的shell呢?

通常,shell 传播SIGHUP。在正常退出时生成SIGHUP 不太常见。


Telnet 或 SSH

在 telnet 或 SSH 下,连接关闭时会发生以下情况(例如,当您在 PC 上关闭 telnet 窗口时):

    客户端被杀死; 服务器检测到客户端连接已关闭; 服务器关闭 pty 的 master 端; 内核检测到master pty已关闭并发送SIGHUPbashbash 接收SIGHUP,将SIGHUP 发送到所有作业并终止; 每个作业都会收到SIGHUP 并终止。

问题

我可以使用busyboxdropbear SSH 服务器中的bashtelnetd 重现您的问题:有时,后台作业不会收到SIGHUP(并且不会终止)当客户端连接关闭时。

当服务器(telnetddropbear)关闭 pty 的主端时,似乎发生了竞态条件

    通常,bash 接收到SIGHUP 并立即终止后台作业(如预期的那样)并终止; 但有时,bash 在处理 SIGHUP 之前 在 pty 的从属端检测到 EOF

bash 检测到EOF 时,默认情况下它会立即终止而不发送SIGHUP。后台作业仍在运行!


解决方案

也可以配置bash在正常退出时发送SIGHUP(包括EOF):

确保bash 作为登录shell 启动。 huponexit works 仅用于登录外壳,AFAIK。

通过-l 选项或argv[0] 中的leading hyphen 启用登录shell。您可以将telnetd 配置为运行/bin/bash -l 或更好的/bin/login,它在登录shell 模式下调用/bin/sh

例如:

telnetd -l /bin/登录

启用huponexit 选项。

例如:

shopt -s huponexit

每次在bash 会话中输入此内容,或将其添加到.bashrc/etc/profile


为什么会发生比赛?

bash 仅在安全时解除阻塞信号,并在某些代码段不能被信号处理程序安全中断时阻塞它们。

这样的临界区会不时调用中断点,如果在临界区执行时收到信号,它的处理程序会延迟到下一个中​​断点发生或临界区已退出。

您可以从源代码中的quit.h开始挖掘。

因此,在我们的例子中,bash 有时在处于临界区时会收到SIGHUPSIGHUP 处理程序执行被延迟,bash 读取 EOF 并在退出临界区或调用下一个中断点之前终止


参考

"Job Control" Glibc 官方手册中的部分。 《Linux 编程接口》一书的第 34 章“进程组、会话和作业控制”。

【讨论】:

完美,感谢您的回答 @Chirag,我在答案中添加了一个猜测,为什么比赛可能发生在bash @gavv 谢谢。但是我使用trap 做了一些本地测试。 Bash 接收 SIGCLD 和 SIGCONT 以及 SIGHUP。为什么?为什么我们需要Ensure that bash is started as login shell. @Amos SIGCLD 通常在子进程终止时发送。发送 SIGCONT 的一种情况是进程组成为孤立的进程组。不确定是不是这样,需要更多数据。 @Amos "为什么我们需要确保 bash 作为登录 shell 启动。?" -- huponexit 仅适用于登录 shell IIRC ***.com/a/21294799/3169754 “如果已使用 shopt 设置了 huponexit shell 选项,则当交互式登录 shell 退出时,bash 会向所有作业发送 SIGHUP。”我会更新答案。

以上是关于如果我们关闭它启动的终端,linux 会杀死后台进程吗?的主要内容,如果未能解决你的问题,请参考以下文章

Linux中怎么终止正在运行的后台程序

linux如何设置程序开机启动后台运行?

Linux下Tomcat的启动关闭杀死进程

[Linux]在终端启动程序关闭终端不退出的方法

Linux进程后台运行的几种方式

Linux下Tomcat的启动关闭杀死进程