fork() и вывод данных
У меня есть простая программа:
int main()
{
std::cout << " Hello World";
fork();
}
После выполнения программы мой вывод выглядит следующим образом: Hello World Hello World. Почему это происходит, а не просто один раз Hello World? Я предполагаю, что дочерний процесс выполняется "за кулисами", и буфер вывода разделяется между процессами или что-то в этом роде. Это действительно так, или происходит что-то другое?
5 ответ(ов)
Дело обстоит не совсем так, как вы изначально думали. Выводной буфер не общий — когда вы вызываете fork, оба процесса получают свои копии одного и того же буфера. Так что, после вызова fork, оба процесса в конечном итоге сбрасывают буфер и выводят содержимое на экран отдельно.
Это происходит только потому, что cout — это буферизированный ввод/вывод. Если бы вы использовали cerr, который не буферизуется, вы бы увидели сообщение всего один раз, до вызова fork.
Стандартный вывод использует буферизованный ввод-вывод (IO). Когда вызывается fork(), стандартный вывод не очищается, и буферизованные данные копируются в дочерний процесс. Эти буферы очищаются при выходе из процесса, что приводит к появлению двух выводов, которые вы видите.
Если изменить программу на:
std::cout << " Hello World;" << std::endl;
то вы увидите только один вывод.
Проблема в том, что вы вызвали fork(), не сбросив все буферы перед этим.
cout.flush();
fork();
Когда вы вызываете fork(), операционная система копирует текущий процесс, включая его буферы. Если буферы не были сброшены, содержимое может быть дублировано в обоих процессах, что может привести к непредсказуемому поведению, особенно если один из процессов изменяет их содержимое. Поэтому всегда рекомендуется сбрасывать все буферы (например, с помощью flush()), чтобы избежать возможных проблем с выводом данных после выполнения fork().
Проблема заключается в том, что код для вывода "Hello World" выполняется только один раз, но буфер вывода не сбрасывается. Когда вы вызываете fork(), строка "Hello World" остается в буфере вывода. При завершении обеих программ их буферы будут сброшены, и вы увидите вывод дважды.
Чтобы продемонстрировать это проще всего, добавьте символ новой строки в конец вашей строки, что приведет к неявному сбросу буфера, или же явно сбросьте его с помощью std::cout.flush();. В этом случае вы увидите вывод только один раз.
Действительно, когда вы используете std::cout << " Hello World" << std::flush;, это выводит строку на стандартный вывод и сбрасывает буфер. Однако, важно понять, что при вызове fork(), дочерний процесс наследует текущее состояние процессов, включая буферы, используемые для вывода.
При вызове fork(), буфер, который использовался для std::cout, копируется в дочерний процесс. Это означает, что если вы записали что-то в std::cout, но еще не сбросили (не вызвали flush), то в дочернем процессе этот буфер будет содержать те же данные. Если вы вызываете fork() после того, как вызвали flush, дочерний процесс получит уже очищенный буфер, и вы не увидите дублирующегося вывода.
Если оба процесса (родительский и дочерний) вызывают flush или другие операции вывода, они могут вывести данные в интерлевированном или непредсказуемом порядке. Поэтому да, при использовании fork(), вы можете иметь "двойной" эффект вывода, если у вас было что-то записано в буфер, который еще не был сброшен. Чтобы избежать путаницы, рекомендуется всегда правильно использовать flush или обрабатывать вывод, чтобы избежать нежелательных эффектов в дочернем процессе.
Безопасно ли делать fork из потока?
Сон на миллисекунды
Как отключить индикатор прогресса в cURL?
Ошибка: версия `CXXABI_1.3.8` не найдена (требуется для ...)
Автоматический вход в Docker через Bash-скрипт