fork ()
создает новый процесс, и дочерний процесс начинает выполняться с текущего состояния родительского процесса.
Это то, что я знаю о fork ()
в Linux.
Соответственно, следующий код:
int main() {
printf("Hi");
fork();
return 0;
}
должен печатать «Hi» только один раз, как указано выше.
Но при выполнении вышеуказанного в Linux, скомпилированном с помощью gcc, он дважды печатает «Hi» .
Может ли кто-нибудь объяснить мне, что на самом деле происходит при использовании fork ()
, и правильно ли я понял работу fork ()
?
printf («Привет»);
на самом деле не выводит слово «Привет» сразу на ваш экран. Что он делает, так это заполняет буфер stdout
словом «Hi», которое затем отображается, когда буфер «очищен». В этом случае stdout
указывает на ваш монитор (предположительно). В этом случае буфер будет очищен, когда он будет заполнен, когда вы принудительно очистите его или (чаще всего), когда вы распечатаете символ новой строки ("\ n"). Поскольку буфер все еще заполнен, когда вызывается fork ()
, его наследуют и родительский, и дочерний процессы, и поэтому они оба будут выводить «Hi» при очистке буфера. Если вы вызовете fflush (stout);
перед вызовом fork, он должен работать:
int main() {
printf("Hi");
fflush(stdout);
fork();
return 0;
}
В качестве альтернативы, как я уже сказал, если вы включите новую строку в свой printf
, он также должен работать:
int main() {
printf("Hi\n");
fork();
return 0;
}
(Включая некоторые пояснения из комментария пользователя @Jack) Когда вы печатаете что-либо в стандартный вывод «Стандартный вывод» (обычно это монитор компьютера, хотя вы можете перенаправить его в файл), оно изначально сохраняется во временном буфере.
Обе стороны вилки наследуют не очищенный буфер, поэтому, когда каждая сторона вилки попадает в оператор return и завершается, он дважды сбрасывается.
Перед тем, как выполнить форк, вы должны fflush (stdout);
очистить буфер, чтобы потомок не унаследовал его.
stdout на экран (в отличие от того, когда вы перенаправляете его в файл) фактически буферизуется по концам строки, поэтому, если вы выполнили printf ("Hi \ n");
вы не было бы этой проблемы, потому что он очистил бы сам буфер.
Технический ответ:
при использовании fork () вам нужно чтобы убедиться, что exit () не вызывается дважды (отключение от main аналогично вызову exit ()). Дочерний элемент (или, реже, родитель) должен вместо этого вызвать _exit. Кроме того, не используйте stdio в дочернем элементе. Это просто напрашивается на неприятности.
В некоторых библиотеках есть функция fflushall (), которую можно вызвать перед fork (), которая делает stdio в дочернем элементе безопасным. В этом конкретном случае это также сделало бы exit () безопасным, но в общем случае это неверно.
В общем, очень небезопасно иметь открытые дескрипторы / объекты, используемые библиотеками по обе стороны от fork ().
Сюда входит стандартная библиотека C.
fork () делает два процесса из одного, и никакая библиотека не может обнаружить это. Следовательно, если оба процесса продолжают работать с одними и теми же дескрипторами файлов / сокетами и т. Д., Они теперь имеют разные состояния, но используют одни и те же дескрипторы файлов (технически у них есть копии, но одни и те же базовые файлы). Из-за этого случаются плохие вещи.
Примеры случаев, когда fork () вызывает эту проблему
Как исправить это в общем случае:
Либо
a) Сразу после fork () вызовите exec (), возможно, в том же двоичном файле (с необходимыми параметрами для выполнения той работы, которую вы намеревались сделать) . Это очень просто.
б) после разветвления не использовать существующие открытые дескрипторы или объекты библиотеки, которые от них зависят (открытие новых - нормально); завершите свою работу как можно быстрее, затем вызовите _exit () (не exit ()). Не возвращайтесь из подпрограммы, которая вызывает fork, так как это рискует вызвать деструкторы C ++ и т. Д., Которые могут плохо повлиять на файловые дескрипторы родительского процесса. Это в меру легко.
c) После разветвления как-нибудь очистите все объекты и приведите их все в нормальное состояние, прежде чем ребенок продолжит. например закрыть базовые файловые дескрипторы без сброса данных, которые находятся в буфере, который дублируется в родительском. Это сложно.
c) примерно то, что делает Apache.
printf()
выполняет буферизацию. Вы пробовали печатать в stderr
?