Странное поведение fork() в C

Рейтинг: 1Ответов: 1Опубликовано: 08.03.2023

Всем здравствуйте, мне нужно создать N потоков, которые будут выполнять определённый код, поэтому я написал это:

#include <stdio.h>
#include <unistd.h>

#define N 2

void create_threads() {
    int count = 0, result;
    printf("Создание дополнительного потока (#1)...\n");
    while ((result = fork()) != 0) {
        count++;
        if (result == -1)
            printf("Не удалось создать дополнительный поток (#%d)!\n", count);
        else if (count == N)
            return;
        printf("Создание дополнительного потока (#%d)...\n", count + 1);
    }
    // Код дополнительного потока
}
int main() {
    printf("main()\n");
    create_threads();
}

Но с этим кодом происходит что-то странное... Вот вывод в консоль:

main()
Создание дополнительного потока (#1)...
Создание дополнительного потока (#2)...
main()
Создание дополнительного потока (#1)...
main()
Создание дополнительного потока (#1)...
Создание дополнительного потока (#2)...

Почему функция main() вызывается ещё два раза? Почему при втором вызове создался только один поток? Как это исправить?

P.s. Компилятор GCC 7.4.0, аргументы компиляции:

-Wall -std=gnu99 -O2 -o a.out source_file.c

UDP: Изменил количество создаваемых потоков с 2 на 4 (#define N 4), всё равно странный вывод, но видна какая-та последовательность:

main()
Создание дополнительного потока (#1)...
main()
Создание дополнительного потока (#1)...
Создание дополнительного потока (#2)...
main()
Создание дополнительного потока (#1)...
Создание дополнительного потока (#2)...
Создание дополнительного потока (#3)...
main()
Создание дополнительного потока (#1)...
Создание дополнительного потока (#2)...
Создание дополнительного потока (#3)...
Создание дополнительного потока (#4)...
main()
Создание дополнительного потока (#1)...
Создание дополнительного потока (#2)...
Создание дополнительного потока (#3)...
Создание дополнительного потока (#4)...

UDP 2: По совету из комментариев добавил во все сообщения результат функции getpid():

#include <stdio.h>
#include <unistd.h>

#define N 2

void create_threads() {
    int count = 0, result;
    printf("[%d] Создание дополнительного потока (#1)...\n", getpid());
    while ((result = fork()) != 0) {
        count++;
        if (result == -1)
            printf("[%d] Не удалось создать дополнительный поток (#%d)!\n", getpid(), count);
        else if (count == N)
            return;
        printf("[%d] Создание дополнительного потока (#%d)...\n", getpid(), count + 1);
    }
    // Код дополнительного потока
}
int main() {
    printf("[%d] main()\n", getpid());
    create_threads();
}

Вывод:

[31574] main()
[31574] Создание дополнительного потока (#1)...
[31574] main()
[31574] Создание дополнительного потока (#1)...
[31574] Создание дополнительного потока (#2)...
[31574] main()
[31574] Создание дополнительного потока (#1)...
[31574] Создание дополнительного потока (#2)...

Ответы

▲ 2Принят

Видимо печать проводится в pipe или файл, поэтому stdout становится не linebuffered, т.е. реальный вывод из буфера при столь малом количестве печати происходит при закрытии stdout в конце работы процесса.

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

Для исправления достаточно вызывать fflush(stdout) перед fork().