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

10

(Повторная публикация в Unix согласно предложению в /programming/13718394/what-should-interactive-shells-do-in-orphaned-process-groups )

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

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

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

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

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

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

  1. Попробуйте сообщить о процессе, чей pid совпадает с идентификатором нашей группы. Если это не помогает ESRCH, значит, мы, вероятно, осиротели.
  2. Попробуйте неблокирующее чтение одного байта из /dev/tty. Если это не помогает EIO, значит, мы, вероятно, осиротели.

(Наша проблема отслеживания это https://github.com/fish-shell/fish-shell/issues/422 )

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

ridiculous_fish
источник

Ответы:

4

Я согласен с вашим анализом и согласен с тем, что вы должны определить, является ли ваша группа процессов сиротой или нет.

tcsetattrтакже предназначен для возврата, EIOесли группа процессов осиротела (и мы не блокируем / игнорируем SIGTT OU . Это может быть менее навязчивым способом, чем readна терминале.

Обратите внимание, что вы можете воспроизвести его с помощью:

(bash<&1 &)

Вам нужно перенаправление, иначе stdin перенаправляется в / dev / null при запуске команды в фоновом режиме.

(bash<&1 & sleep 2)

Дает еще более странное поведение, потому что в итоге вы читаете две оболочки из терминала. Они игнорируют, SIGTTINа новый не обнаруживает, как только он запущен, он больше не входит в группу процессов переднего плана.

ksh93Решение не так уж и плохо: проходите этот цикл до 20 раз (а не бесконечно), прежде чем сдаться.

Стефан Шазелас
источник