如果我们关闭启动它的终端,linux 是否会终止后台进程?

Does linux kill background processes if we close the terminal from which it has started?

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

./app_name &

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

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

#include<stdio.h>
main()
{
    while(1);
}

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

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

我的问题是:

据我所知,在这两种情况下,进程都应该被终止。为了避免这种情况,您必须像下面这样发出 nohup:

> nohup ./my_app &

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

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

为了完全理解发生了什么,您需要稍微了解一下 unix 内部结构。

当你运行这样的命令时

./app_name &

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

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

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

nohup - run a command immune to hangups, with output to a non-tty

最后,这就是您需要做的。

nohup app_name & 2> /dev/null;

当您关闭终端时,shell 将 SIGHUP 发送到所有后台进程 – 并杀死它们。这可以通过多种方式抑制,最显着的是:

nohup

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

$ nohup app &

否认

disown 告诉 shell 不要发送 SIGHUP

$ app &
$ disown

是否依赖于linux的版本?

这取决于您的 shell。以上至少适用于 bash.

谁应该杀死工作?

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


内核什么时候发送SIGHUP?

内核发送SIGHUPcontrolling process:

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

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

  • 前台进程组,当控制进程终止时;
  • ,当它成为孤儿并且停止成员时。

Controlling process is the session leader that established the connection to the controlling terminal.

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

  • 内核发送 SIGHUP 到 shell 当真实或伪终端是 disconnected/closed;
  • 内核在 shell 终止时将 SIGHUP 发送到前台进程组;
  • 内核将 SIGHUP 发送到包含已停止进程的孤立进程组。

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


bash什么时候发送SIGHUP

BashSIGHUP 发送到 所有 作业(前景和背景):

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

查看更多详情here

备注:

  • bash 不会发送SIGHUP到使用disown;
  • 从工作列表中删除的工作
  • 进程开始使用 nohup 忽略 SIGHUP.

更多详情here


其他 shell 呢?

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


Telnet 或 SSH

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

  1. 客户端被杀死;
  2. 服务器检测到客户端连接已关闭;
  3. 服务器关闭 pty 的主端;
  4. 内核检测到master pty关闭并发送SIGHUPbash;
  5. bash 接收 SIGHUP,向所有作业发送 SIGHUP 并终止;
  6. 每个作业接收 SIGHUP 并终止。

问题

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

当服务器(telnetddropbear)关闭 pty 的主端时,似乎出现了 竞争条件

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

bash检测到EOF时,它默认立即终止而不发送SIGHUP。后台作业仍然是 运行ning!


解决方案

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

  • 确保 bash 作为 login shell 启动。 huponexit works 仅用于登录 shells,据我所知。

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

    例如:

    telnetd -l /bin/login
    
  • 启用 huponexit 选项。

    例如:

    shopt -s huponexit
    

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


为什么会发生竞争?

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

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

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

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


参考

  • "Job Control" 官方 Glibc 手册中的部分。
  • "The Linux Programming Interface" 书的第 34 章 "Process Groups, Sessions, and Job Control"。