Понимание fork() C на простом примере

#include <stdio.h>
int num = 0;
int main(int argc, char*argv[]){
    int pid;
    pid = fork();
    printf("%d", num);  
    if(pid == 0){       /*child*/
        num = 1;
    }else if(pid > 0){  /*parent*/
        num = 2;
    }
    printf("%d", num);
}

У меня проблемы с пониманием, почему возможные выходные данные будут 0102, 0012, 0201 или 0021.

Вот что я (думаю) он должен производить. Он попадает в первый оператор printf, и независимо от того, какой дочерний или родительский оператор выполняется первым, num не был изменен, поэтому сначала 0. THEN next равен либо 1, либо 2, затем выполняется следующий процесс, поэтому он снова начинается с 0 (скопировано из родителя), а затем снова либо 1, либо 2. Таким образом, возможные результаты должны быть:

0101 or 0102 or 0201 or 0202


person Locke McDonnell    schedule 07.03.2013    source источник
comment
что делает точка с запятой после argc. Это синтаксическая ошибка.   -  person Kaunteya    schedule 07.03.2013
comment
int main(int argc, char*argv[]) это должно быть основным   -  person    schedule 07.03.2013
comment
Вы должны включить #include <unistd.h>. Это правильный заголовочный файл для fork().   -  person Deanie    schedule 17.05.2015


Ответы (2)


Как в родительском, так и в дочернем элементе число равно 0 для первого printf. И в родительском, и в дочернем случае печатается 0, за которым следует другое значение. В родительском процессе другое значение равно 2. В дочернем процессе другое значение равно 1.

Однако важно отметить, что, хотя каждый процесс имеет принудительный порядок, согласно которому ноль должен быть напечатан перед другим числом, нет никаких ограничений на печать двух процессов относительно друг друга.

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

Чего не произойдет, так это печати 1 или 2 более одного раза. Хотя после возврата fork у нас есть два процесса, запущенных концептуально одновременно, и время их событий относительно друг друга не указано, порядок событий в каждом процессе четко определен. Каждый процесс установит num либо в 1, либо в 2, прежде чем распечатать его снова, и поскольку fork определено так, чтобы возвращать 0 в дочернем элементе и pid дочернего элемента в родительском, каждый из них будет устанавливать разные значения.

На самом деле есть еще один разумный выход: 00. Если fork не может создать новый процесс, возвращается -1. В этом случае программа напечатает 0, if и else if с ошибкой, потому что -1 не 0 и не больше 0, число не изменилось, и программа снова напечатает 0.

Если вы хотите узнать больше об определении порядка эффектов в программах на C, ключевыми словами для поиска являются «точки последовательности». В этой программе это довольно просто (не считая того факта, что у нас одновременно запущены две копии), но иногда это может быть менее очевидно.

person Samuel Edwin Ward    schedule 07.03.2013
comment
.. Теперь объясните взаимное исключение?! - person Ed Heal; 07.03.2013
comment
В родительском процессе другое значение равно 2. Хорошо, потому что pid > 0, если это родительский процесс. В дочернем процессе другое значение равно 0, что?! Когда мы попадаем в дочерний процесс, pid будет равен 0, таким образом, число изменится на 1. - person Locke McDonnell; 07.03.2013
comment
Итак, как я мог получить вывод, подобный 0012? или 0021? - person Locke McDonnell; 07.03.2013
comment
@SamuelEdwinWard - Ваше объяснение было невероятно хорошим. Я предполагал, что следующий вопрос Локка будет о взаимном исключении. По-своему я пытался сделать вам комплимент, поэтому +1 - person Ed Heal; 07.03.2013
comment
@LockeMcDonnell, представьте, если оба процесса по очереди выполняют одну строку кода. Это только один из многих возможных вариантов, но именно он даст 0012 или 0021. Один печатает 0, затем другой печатает 0, затем каждый печатает свой номер. - person Samuel Edwin Ward; 07.03.2013
comment
Поскольку это принятый ответ, я хотел бы, чтобы он также кратко, но четко объяснил, почему 0101 и 0202, упомянутые в вопросе, невозможны. - person hyde; 07.03.2013

Это не проблема с fork(). Это printf(), так как printf() находится в буфере. Обычно буфер сбрасывается, когда он встречает в конце символ новой строки '\n'. Однако, поскольку вы пропустили это, содержимое буфера остается и не сбрасывается. В конце концов, оба процесса (исходный и дочерний) будут иметь выходной буфер со значениями 0 или 1. Когда он в конце концов сбрасывается, вы увидите это в обоих процессах.

добавьте fflush(stdout); после printf() и попробуйте.

person Community    schedule 07.03.2013
comment
Выходной поток printf обычно буферизуется, но это не обязательно. В любом случае нельзя гарантировать относительный порядок вызовов fflush между процессами. Также нельзя гарантировать, что fflush приведет только к одному вызову write при буферизации ввода, хотя это почти наверняка произойдет с двумя символами в буфере. - person Samuel Edwin Ward; 07.03.2013
comment
Если буфер FILE имеет всего два байта, я уверен, что это приведет к одному системному вызову записи на любой libc, обычно используемой на ПК. Таким образом, хотя это и не гарантируется каким-либо стандартом, все же, вероятно, гарантируется всем существующим кодом, делающим это. - person hyde; 07.03.2013
comment
Таким образом, на практике без добавления сброса выходные данные будут либо 0102, либо 0201. OTOH, в малонагруженной многоядерной системе, если добавить сброс, после первой печати он будет достаточно медленным, чтобы другой процесс тоже мог это сделать, поэтому можно было бы предсказать, что тогда вывод часто будет 0012 или 0021. Вывод: используйте синхронизацию, если это имеет значение. - person hyde; 07.03.2013
comment
@hyde, я согласен, что в реальном мире, вероятно, будет один звонок для записи; Я чуть не пропустил эту часть. Хотя теперь, когда я об этом думаю, этот вызов записи может вернуть 1. - person Samuel Edwin Ward; 07.03.2013