25

简短的问题是,如果 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 都受到影响。

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

我的问题是,命令行 shell 如何检测到这种情况,它应该做什么?我的想法是外壳尝试read从标准输入,如果读取给它 EIO 就退出。

谢谢你的想法!

编辑:我尝试在 /dev/tty 上执行零长度读取(),并且成功了,这很糟糕。为了获得 EIO,我实际上必须准备从 /dev/tty 读取一些数据。

编辑:我的另一个想法是kill(getpgrp(), 0)。如果进程组是孤立的,那么我相信这将永远失败。但是,它也可能会失败,因为我无权向会话负责人发出信号。

编辑:对于以后发现此问题的任何人,我最终所做的都在https://github.com/fish-shell/fish-shell/issues/422中进行了描述。还有,未来怎么样?

4

1 回答 1

3

这是 strace 所说的正在发生的事情:

--- SIGTTIN(停止(tty 输入))@ 0 (0) ---
rt_sigaction(SIGTTIN, {SIG_IGN, [], SA_RESTORER, 0x7fd5f6989d80}, {SIG_DFL, [], SA_RESTORER, 0x7fd5f6989d80}, 8) = 0
ioctl(255, TIOCGPGRP, [9954]) = 0
rt_sigaction(SIGTTIN, {SIG_DFL, [], SA_RESTORER, 0x7fd5f6989d80}, {SIG_IGN, [], SA_RESTORER, 0x7fd5f6989d80}, 8) = 0
杀死(0,SIGTTIN)= 0
--- SIGTTIN(停止(tty 输入))@ 0 (0) ---
rt_sigaction(SIGTTIN, {SIG_IGN, [], SA_RESTORER, 0x7fd5f6989d80}, {SIG_DFL, [], SA_RESTORER, 0x7fd5f6989d80}, 8) = 0
ioctl(255, TIOCGPGRP, [9954]) = 0
rt_sigaction(SIGTTIN, {SIG_DFL, [], SA_RESTORER, 0x7fd5f6989d80}, {SIG_IGN, [], SA_RESTORER, 0x7fd5f6989d80}, 8) = 0
杀死(0,SIGTTIN)= 0
[重复...]

这就是为什么,来自jobs.c,bash 4.2:

  while ((terminal_pgrp = tcgetpgrp (shell_tty)) != -1)
    {
      if (shell_pgrp != terminal_pgrp)
        {
          SigHandler *ottin;

          ottin = set_signal_handler(SIGTTIN, SIG_DFL);
          kill (0, SIGTTIN);
          set_signal_handler (SIGTTIN, ottin);
          continue;
        } 
      break;
    } 

至于怎么办……这超出了我的能力范围。但是,我认为这是有用的信息,有点需要评论。

于 2013-01-05T15:34:26.003 回答