2015-12-01 2 views
-1

Я изучаю функцию fork в C и учился на прошлых экзаменах и столкнулся с одним вопросом, который довольно интересен.Расположение функции fork в C и ее влияние на выход

Смотрите ниже код:

int main(void) { 

printf("Hello "); 
fork(); 
printf("Hello "); 
fork(); 
printf("Hello \n"); 

pause(); 
return 0; 
} 

Выходной результат

Hello Hello Hello 
Hello Hello Hello 
Hello Hello Hello 
Hello Hello Hello 

Если я изменить мой код

fork(); 
    fork(); 
    printf("Hello "); 
    printf("Hello "); 
    printf("Hello \n"); 

В результате выход остается прежним. Почему расположение функции вилки не влияет на сам вывод. Вилка ждет завершения printf() перед выполнением? Это должно объяснить вывод, потому что в первый раз printf() выведет первую строку Hello, тогда он будет разветвлен, и еще 1 строка Hello будет напечатана. Затем вторая вилка разветвит предыдущие 2 строки и произведет еще две строки Hello.

Однако вопрос немного меняет код, и весь результат резко меняется.

printf("Hello \n"); 
fork(); 
printf("Hello "); 
fork(); 
printf("Hello \n"); 

Это приводит к выходу будучи

Hello 
Hello Hello 
Hello Hello 
Hello Hello 
Hello Hello 

Я не понимаю, почему просто добавив «\ п» приведет к 5 строк вывода вместо 4. На этот раз, если я поменяю расположение функции вилки вверху, как и раньше, выход тоже изменяется. Кажется, что функция fork не влияла на первую строку printf на этот раз. Может ли кто-нибудь объяснить мне, как именно функция fork выводит материал?

+3

Было бы намного яснее, если бы строки не были одинаковыми ... – BadZen

ответ

2

Вы столкнулись с особым побочным эффектом буферизованного ввода-вывода с использованием printf(). Обычно printf() будет на самом деле печатать текст на консоль, если есть новая строка, иначе он будет буферизовать этот текст внутри ожидающего следующей новой строки.

Что произошло в вашем случае, так это то, что первые два вызова printf() не вошли в консоль, но остались буферизованы. Когда вы разветвляли вашу программу, этот внутренний буфер тоже разветвлялся, а это означало, что его содержимое также было скопировано в новую программу. Таким образом, все копии программы имели все три копии «привет», когда они сделали окончательный printf().

Если вместо этого вы вынуждены текст на консоль с fflush(0) вы должны получить поведение вы ожидали (вид):

int main(void) { 
    printf("Hello "); fflush(0); 
    fork(); 
    printf("Hello "); fflush(0); 
    fork(); 
    printf("Hello \n"); fflush(0); 
    pause(); 
    return 0; 
} 

приводит к

Hello Hello Hello 
Hello Hello 
Hello 
Hello 

или

Hello Hello Hello Hello 
Hello 
Hello 
Hello 

вы также можете получить другие комбинации ...

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

4

Проблема не в заказе, который не имеет значения, поскольку мы имеем дело с fork(), и у вас нет гарантий относительно параллелизма двух одновременных потоков.

Проблема заключается в том, что программа печатает количество Hello, что неверно в соответствии с семантикой программы. Если вы думаете об этом

  • первый printf выполняется только по родительским процессом
  • второй printf выполняется родительский процесс и дочерний процесс
  • третьего printf выполняется путем родительского процесса, дочерний процесс, и два соответствующих детей родителя и первого ребенка

Это дает общее количество 7 printf, в то время как вы получаете 12 или 11. Проблема заключается в том, что вывод буферизируется на основе строки, когда вы вызываете fork() с чем-то внутри буфера stdout, тогда происходят странные вещи. Если вы измените код на:

#include <unistd.h> 

int main(void) { 
    printf("(1:%u)\n", getpid()); 
    fork(); 
    printf("(2:%u)\n", getpid()); 
    fork(); 
    printf("(3:%u)\n", getpid()); 
    pause(); 
    return 0; 
} 

Вы получите:

cuboid:Dev jack$ ./a.out 
(1:4307) 
(2:4307) 
(3:4307) 
(2:4308) 
(3:4309) 
(3:4308) 
(3:4310) 

Что является правильным, так как теперь мы принуждая флеш (с \n) при каждом вызове printf и ИДП соответствует к тому, что мы видели раньше: 1, 2, 4 звонка.

Если вы вынуждаете вровень перед вызовом fork() в коде, например:

int main(void) { 
    printf("1Hello "); 
    fflush(stdout); 
    fork(); 
    printf("2Hello "); 
    fflush(stdout); 
    fork(); 
    printf("3Hello \n"); 
    pause(); 
    return 0; 
} 

Вы получите:

cuboid:Dev jack$ ./a.out 
1Hello 2Hello 3Hello 
2Hello 3Hello 
3Hello 
3Hello 

что дает тот же правильный результат, и вы можете видеть, что родительский процесс пропуска через все 3 линии, первый ребенок только через 2-й и 3-й, а последние два ребенка - только через последний.