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 точок?



Відповіді:


245

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

Отже, спочатку є один процес. Це створює другий процес, який друкує крапку і цикл. Під час своєї другої ітерації кожен створює ще одну копію, тому є чотири процеси, надрукуючи крапку, а потім вийдіть. Тож ми можемо легко нарахувати шість крапок, як ви очікуєте.

Однак те, що printf()насправді робить, є захистом його результатів. Тож перша точка з того часу, коли було лише два процеси, не з’являється при написанні. Ці точки залишаються в буфері, який дублюється на fork (). Лише поки процес не вийде, з'являється буферизована точка. Чотири процеси друку буферизованої точки, плюс нова дає 8 крапок.

Якщо ви хотіли уникнути такої поведінки, телефонуйте fflush(stdout);після printf().


12
Дякую, я не знав, що буфер дублюється з fork (). Це пояснює таку дивну поведінку.
Микола Коваленко

1
Чи не повинно це дати 10 крапок, а не 8? Оскільки діти 4-го покоління успадковують буферизовану крапку, додають власну, а потім вимикаються на виході, вони надрукують загалом 8 крапок, але тоді в двох процесах першого покоління все одно буде по одній точці кожен буфер, і вимийте їх на виході, даючи в цілому 10.
psusi

12
@psusi Один з процесів другого покоління - це процес першого покоління. fork()не створює 2, а потім виходить, він створює лише ще 1 процес.
Ізката

70

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

printf("a\n");

і

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

не виставляйте проблеми.

У вашому першому прикладі ви створюєте чотири процеси, які мають по дві точки у своєму буфері вихідного потоку. Коли кожен потік припиняється, він промиває свій буфер, утворюючи вісім крапок.


2

коли i = 0

Процес_1: буферний текст = 1 крапка

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

коли i = 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 крапок. :)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.