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

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

./app_name &

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

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

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

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

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

我的问题是:

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

【问题讨论】:

    标签: linux shell telnet job-control


    【解决方案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 窗口时):

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

      问题

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

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

      1. 通常,bash 接收到SIGHUP 并立即终止后台作业(如预期的那样)并终止;
      2. 但有时,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 stackoverflow.com/a/21294799/3169754 “如果已使用 shopt 设置了 huponexit shell 选项,则当交互式登录 shell 退出时,bash 会向所有作业发送 SIGHUP。”我会更新答案。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-14
      • 2015-01-11
      • 2021-07-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多