Подтвердить что ты не робот

Что должны делать интерактивные оболочки в сиротских группах процессов?

Короткий вопрос: что должна делать оболочка, если она находится в сиротской группе процессов, которая не владеет tty? Но я рекомендую прочитать длинный вопрос, потому что это забавно.

Вот забавный и увлекательный способ превратить ваш ноутбук в переносной обогреватель, используя вашу любимую оболочку (если вы не один из тех странных tcsh):

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

Это приводит к тому, что bash привязывает процессор к 100%. zsh и рыбы делают то же самое, в то время как ksh и tcsh борются с чем-то о контроле над работой, а затем кили, что немного лучше, но не намного. О, и это агностик-атакующий платформа: OS X и Linux затронуты.

Мое (потенциально неправильное) объяснение выглядит следующим образом: дочерняя оболочка обнаруживает, что она не находится на переднем плане: tcgetpgrp(0) != getpgrp(). Поэтому он пытается остановить себя: killpg(getpgrp(), SIGTTIN). Но его группа процессов осиротела, потому что ее родитель (программа C) был лидером и умер, а SIGTTIN, отправленный в сиротскую группу процессов, просто отброшен (иначе ничто не могло начать его снова). Поэтому дочерняя оболочка не останавливается, но она все еще находится в фоновом режиме, поэтому она делает все снова, сразу. Промыть и повторить.

Мой вопрос в том, как оболочка командной строки обнаруживает этот сценарий и что ему нужно делать? Моя мысль заключается в том, что оболочка пытается read из stdin и просто выходит, если чтение дает EIO.

Спасибо за ваши мысли!

Изменить: я попытался выполнить чтение() на /dev/tty с нулевой длиной, и это удалось, что плохо. Чтобы получить EIO, я действительно должен быть готов прочитать некоторые данные из /dev/tty.

Изменить: Еще одна мысль, которая у меня была, была kill(getpgrp(), 0). Если группа процессов осиротела, я полагаю, что это всегда будет терпеть неудачу. Однако это может также потерпеть неудачу, потому что у меня нет разрешения на сигнализацию лидера сеанса.

Изменить: для тех, кто нашел это позже, то, что я закончил, описано в https://github.com/fish-shell/fish-shell/issues/422. Также, как будущее?

4b9b3361

Ответ 1

Вот что говорит strace:

--- SIGTTIN (Stopped (tty input)) @ 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
kill(0, SIGTTIN)                        = 0
--- SIGTTIN (Stopped (tty input)) @ 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
kill(0, SIGTTIN)                        = 0
[repeat...]

и вот почему, с 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;
    } 

Относительно того, что делать с этим... хорошо, что выше моих возможностей. Но я думал, что это полезная информация, и немного для комментария.