fork () веток больше, чем ожидалось?

186

Рассмотрим следующий фрагмент кода:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main(void)
{
    int i;
    for(i = 0; i < 2; i++)
    {
        fork();
        printf(".");
    }
    return 0;
}

Эта программа выводит 8 точек. Как это может быть возможно? Не должно ли быть 6 точек вместо этого?

Николай Коваленко
источник
14
ideone.com/B9HXL
Антонио Перес

Ответы:

245

fork()Примитив часто тянется воображение. Пока вы не почувствуете это, вы должны проследить на бумаге, что такое каждая операция, и учесть количество процессов. Не забывайте, что fork () создает почти идеальную копию текущего процесса. Наиболее существенным отличием (для большинства целей) является то, что fork()возвращаемое значение отличается между родителем и ребенком. (Поскольку этот код игнорирует возвращаемое значение, это не имеет значения.)

Итак, во-первых, есть один процесс. Это создает второй процесс, который печатает точку и цикл. На второй итерации каждый создает другую копию, поэтому четыре процесса печатают точку и затем завершают работу. Таким образом, мы можем легко объяснить шесть точек, как вы ожидаете.

Однако, что printf()действительно делает, так это буферизирует вывод. Таким образом, первая точка, когда было только два процесса, не появляется при записи. Эти точки остаются в буфере, который дублируется в fork (). Только когда процесс собирается завершиться, появляется буферизованная точка. Четыре процесса печатают буферизованную точку, плюс новый дает 8 точек.

Если вы хотите избежать такого поведения, позвоните fflush(stdout);после printf().

wallyk
источник
12
Спасибо, я не знал, что буфер дублируется с помощью fork (). Это объясняет такое странное поведение.
Николай Коваленко
1
Разве это не должно давать 10 точек, а не 8? Так как 4 дочерних элемента второго поколения наследуют буферизованную точку, добавляют свою собственную, а затем сбрасывают при выходе, они будут печатать в общей сложности 8 точек, но тогда 2 процесса первого поколения будут по-прежнему иметь одну точку в каждой буферизированной и очищать их при выходе, давая в общей сложности 10.
psusi
12
@psusi Один из процессов второго поколения - это процесс первого поколения. fork()не создает 2, затем завершает работу, он только создает еще 1 процесс.
Изката
70

У вас есть незафиксированные буферы в выходных потоках . stdout буферизуется строкой, а буфер реплицируется вместе с остальной частью процесса. Когда программа завершается, незафиксированный буфер записывается дважды (один раз для каждого процесса). Оба используют

printf("a\n");

и

printf("a "); fflush(stdout);

не выставляйте проблему

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

thiton
источник
2

когда я = 0

Process_1: буферизованный текст = 1 точка

Process_2 (созданный Process_1): буферизованный текст = 1 точка

когда я = 1

Process_3 (созданный Process_1): наследует 1 буферизованную точку из Process_1 и печатает 1 точку самостоятельно. Всего Process_3 печатает 2 точки.

Process_4 (созданный Process_2): наследует 1 буферизованную точку из Process_2 и печатает 1 точку самостоятельно. Всего Process_4 печатает 2 точки.

Process_1: печатает 2 точки (одна буферизованная точка, когда i = 0, и другая точка, когда i = 1)

Process_2: печатает 2 точки (одна буферизованная точка при i = 0 и другая точка при i = 1)

Окончательный результат: 8 точек. :)

Tauseef
источник