неожиданный SIGTTIN после `bash -ic» / bin / echo hello»‘ при написании сценариев bash

#bash #shell

#bash #оболочка

Вопрос:

Это двоичный файл the_binary :

 #include <stdio.h>   
int main(void){   
    char rbuf[10];   
    int ret;
    if((ret = read(0, rbuf, 10)) < 0){   
    }   
    printf("read content:%sn",rbuf);   
    return 0;   
 }
  

Это сценарий bash:

 #!/bin/bash
bash -ic "/bin/echo $$"
./the_binary
  

Попробуйте, вы заметите, что

test ./the_script.sh # clarify: test is the part of prompt, but not the commmand, please ignore it
hello
[1]    25407 suspended (tty input)  ./bash.sh
  

Насколько я знаю, bash -ic "/bin/echo hello" что-то сделал, чтобы заставить двоичный файл отправлять SIGTTIN сигнал процессу, выполняющему скрипт.

Но что произошло? Какова семантика bash -ic ?

Другие проблемные сценарии:

 #!/bin/bash
bash -ic "/bin/echo hello"
bash -ic "/bin/echo hello"
  

и,

 #!/bin/bash
bash -ic "/bin/echo hello"
python -ic "print 'hello'" 
  

ОТРЕДАКТИРОВАНО: переименуйте двоичный файл, чтобы прояснить проблему

Комментарии:

1. Что test здесь? Это не стандартная команда.

2. bash -i делает оболочку интерактивной, даже если стандартный ввод не является терминалом; test выполняется ли его аргумент?

3. Я не следую вашим примерам. Похоже, что вы запускаете встроенный bash test с файлом в качестве аргумента, но это не приведет к запуску скрипта. Он просто вернет 0.

4. SIGTTIN происходит, когда программа, работающая в фоновом режиме, пытается выполнить чтение с терминала. Похоже, в вашем вопросе нет ничего, что запускает скрипт в фоновом режиме, но, возможно, это как-то связано с вашей версией test . Вам действительно следует избегать присвоения вашим собственным программам того же имени, что и стандартные команды Unix, такие как read and test .

5. извините, я использую здесь zsh, test это просто имя каталога. Я удалил его.

Ответ №1:

Это bash пытается сделать что-то умное (снова! Я до сих пор помню shellshock. И здесь (на китайском языке) еще один).

strace -f сценарий, и вы заметите, что SIGTTIN он отправляется bash, а не ядром. Найдите исходный код, и вы увидите это в jobs.c:

   if (shell_pgrp == 0)
{
  shell_pgrp = getpid ();
  setpgid (0, shell_pgrp);
  tcsetpgrp (shell_tty, shell_pgrp);
}

  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;
}
  

Почему второй (и третий, если вы его туда поместите) bash отправляет SIGTTIN , но не первый?

Потому что в первый раз shell_pgrp == terminal_pgrp (процесс, выполняющий сценарий). Затем интерактивный bash устанавливает себя в качестве группового процесса и группы процессов переднего плана. Затем он завершается.

Второй (и третий, если у вас есть) bash видит, что группа процессов — это процесс, выполняющий сценарий (это новая группа), но группа процессов переднего плана терминала по-прежнему является завершенным первым bash (исполнитель не выполняет никакого управления заданием, поскольку он выполняет сценарий неактивно).). Итак shell_pgrp != terminal_pgrp , и kill часть, описанная выше, выполняется.

man 4 tty_ioctl и tcsetpgrp если вам нужны некоторые документы.

zsh так не убивает.


Для этого скрипта:

 #!/bin/bash
bash -ic "which mvn"
python -ic "print('hello')"
  

Вы не получаете SIGTTIN . Вместо этого вы получаете a SIGTTOU , потому что Python пытается что-то вывести, пока это не группа процессов переднего плана (первый, завершенный bash). (И если вы действительно получаете a SIGTTIN , это потому python -i , что по определению считывается с терминала. SIGTTOU может не отправляться из ядра в зависимости от настроек вашего терминала.)