交互式外壳在孤立的流程组中应该做什么?


10

(根据/programming/13718394/what-should-interactive-shells-do-in-orphaned-process-groups中的建议在UNIX中重新发布)

简短的问题是,如果外壳程序位于不拥有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都受影响。

我的解释(可能是错误的)如下:子外壳检测到它不在前台:tcgetpgrp(0) != getpgrp()。因此,它试图停止自身:killpg(getpgrp(), SIGTTIN)。但是它的进程组是孤立的,因为它的父级(C程序)是领导者并死亡,并且SIGTTIN发送给孤立进程组的操作只是被丢弃(否则什么也无法再次启动它)。因此,子外壳并没有停止,但是它仍然在后台,因此马上就可以再次执行所有操作。冲洗并重复。

我的问题是,命令行外壳程序如何检测到这种情况?正确的做法是什么?我有两个解决方案,都不是理想的:

  1. 尝试通知其pid与我们的组ID匹配的进程。如果使用失败ESRCH,则表明我们很可能成为孤儿。
  2. 尝试从中无阻塞读取一个字节/dev/tty。如果使用失败EIO,则表明我们很可能成为孤儿。

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

感谢您的想法!

Answers:


4

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

tcsetattrEIO如果进程组是孤立的,它也将返回(并且我们不会阻止/忽略SIGTT OU。这可能比read终端上的a侵入性要小。

请注意,您可以使用以下方法重现它:

(bash<&1 &)

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

(bash<&1 & sleep 2)

甚至提供了更奇怪的行为,因为您最终从终端读取了两个外壳。SIGTTIN一旦开始,它们就不再在前台进程组中了,而他们正在忽略,而新的并没有检测到。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.