如果我们关闭它启动的终端,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 &
app_name
被发送到后台进程组。你可以查看unix
进程组here
当您通过正常退出关闭bash
时,它会触发SIGHUP
挂断信号到其所有作业。关于unix
作业控制的一些信息是here。
为了在您退出 bash
时保持应用运行,您需要使用 nohup
实用程序使您的应用免受挂断信号的影响。
nohup - 运行不受挂断影响的命令,输出到非 tty
最后你需要这样做。
nohup app_name & 2> /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:
内核将SIGHUP
发送到其他进程组:
控制进程是与控制终端建立连接的会话领导者。
通常,控制进程是您的 shell。所以,总结一下:
当真实或伪终端断开/关闭时,内核将SIGHUP
发送到外壳;
当shell终止时内核发送SIGHUP
到前台进程组;
如果包含已停止的进程,内核会将SIGHUP
发送到孤立的进程组。
请注意,如果内核不将SIGHUP
发送到后台进程组,如果它不包含停止的进程。
bash
何时发送SIGHUP
?
Bash 将SIGHUP
发送到所有 个作业(前台和后台):
SIGHUP
,并且它是一个交互式shell(并且作业控制支持在编译时启用);
当它退出时,它是一个交互式登录 shell,并设置了huponexit
选项(并且在编译时启用了作业控制支持)。
查看更多详情here。
注意事项:
bash
不将SIGHUP
发送到使用disown
从工作列表中删除的工作;
使用nohup
启动的进程忽略 SIGHUP
。
更多详情here.
其他的shell呢?
通常,shell 传播SIGHUP
。在正常退出时生成SIGHUP
不太常见。
Telnet 或 SSH
在 telnet 或 SSH 下,连接关闭时会发生以下情况(例如,当您在 PC 上关闭 telnet
窗口时):
-
客户端被杀死;
服务器检测到客户端连接已关闭;
服务器关闭 pty 的 master 端;
内核检测到master pty已关闭并发送
SIGHUP
到bash
;
bash
接收SIGHUP
,将SIGHUP
发送到所有作业并终止;
每个作业都会收到SIGHUP
并终止。
问题
我可以使用busybox
或dropbear
SSH 服务器中的bash
和telnetd
重现您的问题:有时,后台作业不会收到SIGHUP
(并且不会终止)当客户端连接关闭时。
当服务器(telnetd
或 dropbear
)关闭 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
有时在处于临界区时会收到SIGHUP
。 SIGHUP
处理程序执行被延迟,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 会杀死后台进程吗?的主要内容,如果未能解决你的问题,请参考以下文章