Почему выходные данные моей программы разветвления отличаются, когда я передаю их вывод? - PullRequest
8 голосов
/ 20 января 2011

Я смотрел на какой-то простой код на форке и решил попробовать его для себя. Я скомпилировал, а затем запустил его из Emacs, и получил другой результат, отличный от того, который был получен при запуске его в Bash.

#include <unistd.h>
#include <stdio.h>

int main() {
  if (fork() != 0) {
    printf("%d: X\n", getpid());
  }

  if (fork() != 0) {
    printf("%d: Y\n", getpid());
  }

  printf("%d: Z\n", getpid());
}

Я скомпилировал его с помощью gcc, а затем запустил a.out из Emacs, а также перенес его в cat и grep ., и получил это.

2055: X
2055: Y
2055: Z
2055: X
2058: Z
2057: Y
2057: Z
2059: Z

Это не правильно. Запустив его только из Bash я получаю (что я и ожидал)

2084: X
2084: Y
2084: Z
2085: Y
2085: Z
2087: Z
2086: Z

edit - пропущены некоторые новые строки

Что происходит?

Ответы [ 4 ]

11 голосов
/ 20 января 2011

Порядок, в котором различные процессы записывают свой вывод, совершенно непредсказуем. Поэтому единственным сюрпризом является то, что иногда оператор печати «X» иногда встречается дважды.

Я полагаю, это потому, что иногда на втором fork() строка вывода, включающая «X», находится в буфере вывода, и ее необходимо очистить. Таким образом, оба процесса в конечном итоге распечатать его. Поскольку getpid() уже был вызван и преобразован в строку, они покажут тот же pid.

Мне удалось воспроизвести несколько строк "X", но если я добавлю fflush(stdout); непосредственно перед второй fork(), я всегда вижу только одну строку "X" и всегда в общей сложности 7 строк.

8 голосов
/ 20 января 2011

Мне кажется, я знаю, что происходит.Буферизация stdio будет отличаться, когда вывод tty и когда это труба или файл.Дочерние процессы наследуют родительские буферы.Когда они сброшены, вы можете получить двойной выход.

Если вы добавите

fflush(stdout);

сразу после каждого printf() вызова, вы поймете, что я имею в виду.

Интересно то, что в случае стандартного вывода tty устройство отличается.Возможно, библиотека знает, что это значит, и сбрасывается после каждого переноса строки, или что-то в этом роде.

6 голосов
/ 20 января 2011

Итак, я думаю, вы задаетесь вопросом, почему вы получаете больше, чем один «Х»?

Это потому, что буферизованный вывод очищается дважды.

Когда вы передаете вывод программы, библиотека stdio распознает, что ваш вывод не является терминалом, и переключается на буферизацию блоков вместо буферизации строк. Следовательно, когда процесс разветвляется, еще нет выходных данных, поэтому теперь и родительский, и дочерний выходы имеют ожидающий вывод.

3 голосов
/ 20 января 2011

Если вы использовали stdout вообще перед разветвлением, вы должны позвонить fflush(stdout) до fork() (и аналогично для любых других выходных данных FILE s, которые вы используете). Невыполнение этого требования приводит к неопределенному поведению . Эффект, который вы видите, приходит от stdout, являющегося линейной буферизацией , когда он подключен к терминалу, но полной буферизацией , когда он подключен к каналу. Это не обязательно, но рекомендуется стандартами (POSIX).

...