Как поток управления в этой программе связан с системным вызовом fork ()? - PullRequest
0 голосов
/ 07 июля 2019

Из того, что я прочитал о fork() системном вызове

Использование системного вызова Fork for создает новый процесс, который называется дочерним процессом, который выполняется одновременно с родительским процессом

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

fork() возвращает 0 дочернему процессу

fork() возвращает идентификатор процесса вновь созданного дочернего процессав родительский процесс (положительное значение)

fork() возвращает отрицательное значение в случае сбоя при создании дочернего процесса

В этом фрагменте кода

void foo() { 
if (fork() == 0) 
    printf("Hello from Child!\n"); 
else 
    printf("Hello from Parent!\n"); 
} 

int main() { 
    foo(); 
    return 0; 
} 

Выходные данные

Hello from Parent!
Hello from Child!

Дочерний процесс был создан, когда элемент управления находился внутри условия if-else функции foo в основном процессе.

Так откуда (какая инструкция) начал выполняться дочерний процесс?

Как видно из вывода, Hello from Parent печатается, когда fork() возвращает 0.Итак, насколько я понимаю, Hello from Parent был фактически напечатан дочерним процессом

fork() вернул положительное значение родительскому процессу, а родительский процесс напечатал Hello from Child.Правильно ли мое понимание этого?

И из какой именно инструкции начал выполняться дочерний процесс?Вызов функции fork() был дан в разделе условий if-else.Таким образом, ребенок должен был начать казнь после этого if-else, но это не то, что происходит?

Ответы [ 2 ]

1 голос
/ 07 июля 2019

Давайте начнем с определения первичного заблуждения здесь:

Как видно из вывода, Hello из Parent печатается, когда fork () возвращает 0. Итак, из моего понимания Hello from Parent былофактически печатается дочерним процессом

Дочерний и родительский процессы - это два отдельных процесса, выполняющихся одновременно.Порядок этих двух выходных данных не является четко определенным, зависит от вашего ядра и других соображений синхронизации и не связан с тем фактом, что ваш код содержит блок if / else, записанный так, как он у вас. 1

Давайте перепишем ваш код в виде линейного потока «инструкций» в абстрактном смысле:

0: Function foo():
1:  Invoke system call fork(), no arguments, store result to $1
2:  If $1 is non-zero, jump to label #1.
3:  Invoke C function printf(), argument "Hello from Child!"
4:  Jump to label #2.
5: Label #1:
6:  Invoke C function printf(), argument "Hello from Parent!"
7: Label #2:
8: return control to calling function.

Как только ваша программа достигнет 1:, системный вызов вызывается,передача управления ядру.Ядро дублирует процесс, помещает PID дочернего элемента в возвращаемое значение fork в родительском процессе и помещает 0 в возвращаемое значение fork в дочернем процессе.На x86 возвращаемое значение сохраняется в регистре eax (rax для x64) как часть соглашения о вызовах системного вызова.

Один из этих двух процессов в конечном итоге будет запланирован для запуска ядром.В вашем случае дочерний процесс оказался первым запланированным.Ваш код пользовательского режима вернул управление из режима ядра, прочитал возвращаемое значение (из eax / rax, если на x86), которое было нулевым, и не перешел на метку # 1.Он напечатал Hello from Child!, а затем вернулся из функции (к вызывающей стороне foo, так как дочерний элемент получил копию стека родителя).

То же самое произошло с родителем, за исключением того, что родитель получилненулевое значение возвращается из системного вызова и печатается Hello from Parent!.Он был запущен по расписанию, и ваш код пользовательского режима получил управление от ядра в тот же момент, только с другим значением, возвращаемым системным вызовом.

1 Возможно также, чтодва выходных сигнала могут каким-то образом чередоваться, но это не так актуально для этого обсуждения и требует понимания того, как процессы Linux выполняют ввод / вывод.

1 голос
/ 07 июля 2019

Дочерний процесс - это второй процесс, который выполняется параллельно. Вы могли бы так же легко получить

Hello from Child!
Hello from Parent!

Например, если у вас открыто окно терминала и вы запускаете firefox &, который запускается «первым», окно терминала или окно браузера? Оба работают одновременно.

Фактически, Linux запускает дочерний процесс немного раньше, чем восстанавливает родительский процесс. Это связано с тем, что большое количество программ, которые вызывают fork(), сразу же имеют дочернюю exec() программу, которая освобождает родителя от необходимости делить всю свою память с дочерней. Это более эффективно, потому что разделяемая память копируется при записи.

...