28

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

./app_name &

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

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

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

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

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

我的问题是:

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

4 回答 4

42

谁应该扼杀工作?

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


内核什么时候发送SIGHUP

内核发送SIGHUP控制进程

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

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

  • 前台进程组,当控制进程终止时;
  • 孤立的进程组,当它变成孤立的并且它已经停止了成员。

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

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

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

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


什么时候bashSIGHUP

Bash发送SIGHUP所有作业(前台和后台):

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

在此处查看更多详细信息。

笔记:

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

更多细节在这里


其他贝壳呢?

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


Telnet 或 SSH

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

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

问题

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

当服务器(或)关闭 pty 的主端时,似乎会发生竞争情况:telnetddropbear

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

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


解决方案

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

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

    登录 shell 由-l选项或前导连字符argv[0]启用。您可以配置telnetd为运行/bin/bash -l或更好地在登录 shell 模式下/bin/login调用。/bin/sh

    例如:

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

    例如:

    shopt -s huponexit
    

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


为什么会发生比赛?

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

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

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

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


参考

于 2015-09-25T16:00:02.753 回答
9

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

诺哈普

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

$ nohup app &

否认

disown告诉 shell 不要发送SIGHUP

$ app &
$ disown

它取决于linux的版本吗?

它取决于您的外壳。以上至少适用于bash

于 2015-09-25T11:15:08.367 回答
6

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

> nohup ./my_app &

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

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

于 2015-09-25T11:07:07.923 回答
5

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

当您运行这样的命令时

./app_name &

app_name发送到后台进程组。您可以在此处unix查看进程组

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

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

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

最后,这就是你需要这样做的方式。

nohup app_name & 2> /dev/null;

于 2015-09-25T11:14:35.847 回答