2017-02-21 31 views
-1

Я новичок и пытаюсь научиться работать с функциями fork() и wait().Программа Forking/WaitingC. Какой должна быть моя работа? Является ли мой результат правильным?

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

Сейчас я получаю: B C B C A D E

Однако мой приятель говорит, что это должно быть: B C A D E A В C

И еще говорит, что это должно быть: B C C D E

Из-за ожидания() функции, я думал, что дочерние процессы должны были закончить до родителя. Вот почему я ожидаю, что результат завершится в «E».

Каковы были бы возможные выходы? Я не понимаю, когда я запускаю его, я получаю ABCABCADE. Не следует ли «А» печатать только один раз для первоначального дочернего процесса?

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

int main(void) { 
int pid; 

    pid= fork(); 
    if (pid == 0) { 
     fprintf(stdout, "A\n"); 
     pid= fork(); 
     if (pid==0) { 
      fprintf(stdout, "B\n"); 
      pid=fork(); 
      fprintf(stdout, "C\n"); 
     } 
     else { 
      wait(NULL); 
      fprintf(stdout, "D\n"); 
     } 
    } 
    else { 
     fprintf(stdout, "E\n"); 
     wait(NULL); 
    } 
    // your code goes here 
    return(0); 
} 
+0

Что вы написали, где «ваш код идет сюда»? В любом случае выход не определен. вы можете получить разные результаты на разных прогонах. Кроме того, вы можете получить разные результаты снова, если вы подключите вывод или перенаправляете вывод в файл. –

ответ

0

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

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

Но здесь есть и другой фактор: взаимодействие нескольких ручек в одном и том же открытом файле. Когда вы получите fork(), вы получите два потока stdout (оба ссылаются на одно и то же открытое описание файла), где раньше у вас был один. POSIX размещает some restrictions о том, как программы должны обрабатывать эту ситуацию. В случае, если стандартный вывод вашей программы буферизируется по строке, который по умолчанию используется при подключении к терминалу, поведение программы хорошо определено из-за строк новой строки в конце каждой строки для печати. Однако если stdout полностью забуферирован, как это возможно, когда он подключен к трубе, то перед форсированием необходимо определить fflush(stdout), чтобы определить поведение. Таким образом, это безопаснее, чем fflush() перед форкированием, гарантируя, что поведение программы определяется независимо от среды выполнения.

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

+0

Каковы будут некоторые возможные результаты? Я не понимаю, когда я запускаю его, я получаю ABCABCADE. Не следует ли «А» печатать только один раз для первоначального дочернего процесса? – SuperHippo

+0

@SuperHippo, это хороший момент, который требует обновления моего ответа, который вы сейчас увидите. Суть в том, что ваша программа демонстрирует не только неточное * поведение, но и полномасштабное un * определенное * поведение. –

+0

@SuperHippo, обновлено. –

1

Там нет никаких оснований, что E должен появиться в прошлом, потому что вы не wait() не сделал до после печати E.

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

Давайте добавим fflush(stdout); перед каждым fork(). Если мы это сделаем, мы избавимся от нескольких выходов A, и мы можем рассуждать обо всем остальном. Вот раз линия:

parent 
| 
| 
+------\ 
|  | 
"E" "A" 
|  | 
wait +------\ 
.  |  | 
.  wait "B" 
.  .  | 
.  .  +------\ 
.  .  |  | 
.  .  "C" "C" 
.  .  | 
.  |<----exit 
.  "D" 
.  | 
|<----exit 
| 

Вы можете видеть, что E может быть распечатан в любой момент, но D не будет печататься до тех пор, после того, как по крайней мере один C (левая одна).

Если поменять местами порядок

fprintf(stdout, "E\n"); 
    wait(NULL); 

вы могли убедиться, что E всегда приходит после D (который, в свою очередь, после того, как по крайней мере один C), но другой C все еще не мог быть последним, так как там нет упорядочивая связь с выходом этого процесса.

0

Выход не является полностью определенным, поэтому вы можете получить разные результаты на разных прогонах. Кроме того, если вы подключаете вывод или перенаправляете его в файл, вы получаете другой результат, если вы этого не сделаете; см. printf() anomaly after fork() для получения полной информации.

Вот пересмотр кода, который может принудительно удалить вывод (он использует POSIX стандарт <sys/wait.h> заголовок вместо нестандартного <wait.h> заголовка тоже.

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

static int flush = 0; 

static void print(const char *str) 
{ 
    printf("%s\n", str); 
    if (flush) 
     fflush(stdout); 
} 

int main(int argc, char **argv) 
{ 
    if (argc > 1) 
     flush = (argv[argc] == 0); 
    int pid = fork(); 
    if (pid == 0) 
    { 
     print("A"); 
     pid = fork(); 
     if (pid == 0) 
     { 
      print("B"); 
      pid = fork(); 
      print("C"); 
     } 
     else 
     { 
      wait(NULL); 
      print("D"); 
     } 
    } 
    else 
    { 
     print("E"); 
     wait(NULL); 
    } 
    return(0); 
} 

Чаще всего для меня, когда это не выполняются бесплатно (без перенаправления, без аргументов командной строки), то E появляется первым:

E 
A 
B 
C 
C 
D 

Когда конвейер cat, выход изменяется, но становится вариация на тему:

A 
B 
C 
A 
D 
A 
B 
C 
E 

и

A 
B 
C 
A 
B 
C 
A 
D 
E 

При запуске с аргументом, чтобы заставить флеши произойти, я получаю это последовательно:

E 
A 
B 
C 
D 
C 

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

YMMV.

Протестировано на Mac (macOS Sierra 10.12.3, GCC 6.3.0).

 Смежные вопросы

  • Нет связанных вопросов^_^