В программе 1 Hello world
печатается только один раз, но когда я удаляю \n
и запускаю его (программа 2), вывод печатается 8 раз. Может кто-нибудь, пожалуйста, объясните мне значение \n
здесь и как это влияет на fork()
?
Программа 1
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("hello world...\n");
fork();
fork();
fork();
}
Выход 1:
hello world...
Программа 2
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("hello world...");
fork();
fork();
fork();
}
Выход 2:
hello world... hello world...hello world...hello world...hello world...hello world...hello world...hello world...
./prog1 > prog1.out
) или канал (./prog1 | cat
). Приготовьтесь к тому, что ваш разум будет взорван :-) fork()
в некоторой степени специфичны и для Unix, поэтому может показаться, что это довольно актуально для unix.SE.Ответы:
При выводе в стандартный вывод с использованием функции библиотеки C.
printf()
вывод обычно буферизуется. Буфер не очищается до тех пор, пока вы не выведете новую строку, не вызоветеfflush(stdout)
или не закроете программу (но не через вызов_exit()
). Стандартный поток вывода по умолчанию буферизуется таким образом, когда он подключен к TTY.Когда вы разветвляете процесс в «Программе 2», дочерние процессы наследуют каждую часть родительского процесса, включая неразбавленный выходной буфер. Это эффективно копирует невыполненный буфер в каждый дочерний процесс.
Когда процесс завершается, буферы сбрасываются. Вы запускаете всего восемь процессов (включая исходный процесс), и незаполненный буфер будет сброшен при завершении каждого отдельного процесса.
Это восемь, потому что в каждом
fork()
вы получаете вдвое больше процессов, чем доfork()
(так как они безусловны), и у вас есть три из них (2 3 = 8).источник
main
с_exit(0)
просто сделать систему выхода вызова без промывочных буферов, а затем он будет напечатан в ноль раз без перевода строки. ( Реализация Системного вызова выхода () и как прийти _exit (0) (выход от системного вызова) позволяет мне получать любой контент стандартного вывода? ). Или вы можете перенаправить Program1 вcat
файл или перенаправить его в файл и посмотреть, как он будет напечатан 8 раз. (stdout по умолчанию полностью буферизован, когда это не TTY). Или добавьтеfflush(stdout)
к делу без новой строки до 2-гоfork()
...Это никак не влияет на вилку.
В первом случае вы получите 8 процессов, которым нечего писать, поскольку выходной буфер уже очищен (из-за
\n
).Во втором случае у вас все еще есть 8 процессов, каждый с буфером, содержащим «Hello world ...», и буфер записывается в конце процесса.
источник
@Kusalananda объяснил, почему вывод повторяется . Если вам интересно, почему вывод повторяется 8 раз, а не только 4 раза (базовая программа + 3 вилки):
источник
Важным фоном здесь является то, что стандарт
stdout
должен быть буферизован линией по умолчанию.Это приводит
\n
к сбросу вывода.Поскольку второй пример не содержит символ новой строки, выходные данные не сбрасываются и, поскольку
fork()
копирует весь процесс, он также копирует состояниеstdout
буфера.Теперь эти
fork()
вызовы в вашем примере создают в общей сложности 8 процессов - все они с копией состоянияstdout
буфера.По определению, все эти процессы вызывают
exit()
при возврате из,main()
а затемexit()
вызовы во всех активных потоках stdio . Это включает в себя, и в результате вы видите один и тот же контент восемь раз.fflush()
fclose()
stdout
Хорошей практикой является вызов
fflush()
всех потоков с ожидающим выводом перед вызовомfork()
или явное разрешение разветвленного дочернего вызова,_exit()
который только завершает процесс, не сбрасывая потоки stdio.Обратите внимание, что вызов
exec()
не очищает буферы stdio, поэтому нормально не заботиться о буферах stdio, если вы (после вызоваfork()
) вызываетеexec()
и (если это не удается) вызываете_exit()
.КСТАТИ: Чтобы понять, что неправильная буферизация может вызвать, вот старая ошибка в Linux, которая была недавно исправлена:
Стандарт требует
stderr
отменыstderr
буферизации по умолчанию, но Linux проигнорировал это и сделал буферизацию строки и (что еще хуже) полную буферизацию в случае, если stderr был перенаправлен через канал. Так что программы, написанные для UNIX, выводили вещи без перевода строки в Linux слишком поздно.См. Комментарий ниже, кажется, сейчас исправлено.
Вот что я делаю, чтобы обойти эту проблему Linux:
Этот код не наносит вреда на других платформах, так как вызов
fflush()
только что очищенного потока является пустым занятием.источник
setbuf()
Debian ( похожая на man7.org выглядит аналогично ) гласит: «Стандартный поток ошибок stderr по умолчанию всегда небуферизован». и простой тест, кажется, действует таким образом, независимо от того, идет ли вывод в файл, канал или терминал. Есть ли у вас какие-либо ссылки на то, что версия библиотеки C будет делать иначе?