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-скрипт