交互式 shell 在孤立进程组中应该做什么?

交互式 shell 在孤立进程组中应该做什么?

(根据中的建议在unix中重新发布https://stackoverflow.com/questions/13718394/what-should-interactive-shells-do-in-orphaned-process-groups

简单的问题是,如果 shell 位于不拥有 tty 的孤立进程组中,它应该做什么?但我建议阅读这个长问题,因为它很有趣。

这是一种有趣且令人兴奋的方法,可以使用您最喜欢的外壳将您的笔记本电脑变成便携式空间加热器(除非您是那些 tcsh 怪人之一):

#include <unistd.h>   
int main(void) {
    if (fork() == 0) {
        execl("/bin/bash", "/bin/bash", NULL);
    }
    return 0;
}

这会导致 bash 将 CPU 固定在 100%。 zsh 和 Fish 做同样的事情,而 ksh 和 tcsh 咕哝着一些关于作业控制的内容,然后就放弃了,这要好一点,但也好不了多少。哦,它是一个与平台无关的罪犯:OS X 和 Linux 都受到影响。

我的(可能是错误的)解释如下:子 shell 检测到它不在前台:tcgetpgrp(0) != getpgrp()。因此它试图阻止自己:killpg(getpgrp(), SIGTTIN)。但它的进程组是孤立的,因为它的父进程(C 程序)是领导者并且死亡,并且SIGTTIN发送到孤儿进程组只是被丢弃(否则没有任何东西可以再次启动它)。因此,子 shell 不会停止,但它仍然在后台,因此它会立即再次执行这一切。冲洗并重复。

我的问题是,命令行 shell 如何检测这种情况,以及它应该做什么?我有两种解决方案,但都不理想:

  1. 尝试向 pid 与我们的组 ID 匹配的进程发出信号。如果失败了ESRCH,就意味着我们可能是孤儿了。
  2. 尝试从 中非阻塞读取一个字节/dev/tty。如果失败了EIO,就意味着我们可能是孤儿了。

(我们跟踪此问题的问题是https://github.com/fish-shell/fish-shell/issues/422

感谢您的想法!

答案1

我同意你的分析,并且我同意听起来你必须检测你的进程组是否是孤立的。

tcsetattr如果进程组是孤立的,也意味着返回EIO(并且我们不会阻止/忽略 SIGTT。与read终端上的方式相比,这可能是一种侵入性较小的方式。

请注意,您可以使用以下命令重现它:

(bash<&1 &)

您需要重定向,否则在后台运行命令时 stdin 会重定向到 /dev/null 。

(bash<&1 & sleep 2)

给出更奇怪的行为,因为你最终会从终端读取两个 shell。他们会忽略SIGTTIN,并且新的进程不会检测到,一旦启动它就不再位于前台进程组中。

ksh93的解决方案还不错:在放弃之前最多只执行该循环 20 次(而不是无限次)。

相关内容