Как действительно разветвляет () возврат для дочернего процесса

Я знаю, что ветвление () возвращается по-другому для дочерних и родительских процессов, но я не могу найти информацию о том, как это происходит. Как дочерний процесс получает возвращаемое значение 0 от ветвления? И каково различие в отношении стека вызовов? Насколько я понимаю для родителя это проходит примерно так:

родительский процесс - вызывает ветвление-> system_call - ветвление вызовов->, ветвление выполняется - возвращается к-> system_call - возвращается к-> родительский процесс.

Что происходит в дочернем процессе?

12
задан ybakos 2 October 2015 в 21:58
поделиться

4 ответа

% man fork

ВОЗВРАЩАЕМЫЕ ЗНАЧЕНИЯ

 После успешного завершения fork () возвращает значение 0 дочернему процессу 
и возвращает идентификатор процесса дочернего процесса родительскому {{1 }}процесс. В противном случае родительскому процессу возвращается значение -1, дочерний процесс 
не создается, а для глобальной переменной [errno] [1] устанавливается значение indi - 
, указывающее на ошибку. { {1}} 

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

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

Самая интересная часть источника с явным ответом на ваш вопрос находится в самом конце самого определения fork () -

if (error == 0) {
    td->td_retval[0] = p2->p_pid;
    td->td_retval[1] = 0;
}

"td", по-видимому, содержит список возвращаемых значений для разных потоков. Я не совсем уверен, как работает этот механизм (почему нет двух отдельных «потоковых» структур). Если ошибка (возвращенная из fork1, «настоящая» функция разветвления) равна 0 (нет ошибки), тогда возьмите «первый» (родительский) поток и установите для него возвращаемое значение PID p2 (нового процесса). Если это «второй» поток (в p2), то установите возвращаемое значение на 0.

23
ответ дан 2 December 2019 в 03:38
поделиться

Системный вызов fork () возвращается дважды (если он не завершился ошибкой).

  • Один из возвратов находится в дочернем процессе, и там возвращаемое значение равно 0.

  • Другой возврат - в родительском процессе, и там возвращаемое значение ненулевое (либо отрицательное, если вилка завершилась неудачно, или ненулевое значение, указывающее PID дочернего элемента).

Основные различия между родительским и дочерним процессами заключаются в следующем:

  • Это отдельные процессы
  • Значение PID отличается
  • Значение PPID (родительский PID) отличается

Другие более неясные различия перечислены в стандарте POSIX .

В каком-то смысле Как на самом деле не ваша проблема. Для достижения результата требуется операционная система. Однако o / s клонирует родительский процесс, создавая второй дочерний процесс, который является почти точной копией родительского, устанавливая атрибуты, которые должны отличаться от правильных новых значений, и обычно помечает страницы данных как CoW (копировать на write) или эквивалент, так что когда один процесс изменяет значение, он получает отдельную копию страницы, чтобы не мешать другому. Это не похоже на устаревший (по крайней мере, для меня - нестандартный для POSIX) системный вызов vfork () , от которого было бы разумно отказаться, даже если он доступен в вашей системе. Каждый процесс продолжается после fork () , как если бы функция возвращалась - поэтому (как я уже сказал вверху) системный вызов fork () возвращается дважды, по одному разу в каждом из двух процессов. которые являются почти идентичными клонами друг друга.

7
ответ дан 2 December 2019 в 03:38
поделиться

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

0
ответ дан 2 December 2019 в 03:38
поделиться

Ответ Стивена Шланскера довольно хорош, но просто добавлю еще немного деталей:

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

Место для возвращаемого значения определяется ABI, чтобы обеспечить совместимость кода. Если я пишу ASM-код для своего процессора x86-64 и обращаюсь к среде исполнения C, я знаю, что возвращаемое значение будет отображаться в регистре RAX.

Сложив эти две вещи вместе, можно сделать логический вывод, что вызов int pid = fork() приводит к двум контекстам, в каждом из которых следующей выполняемой инструкцией является та, которая перемещает значение RAX (возвращаемое значение из вызова fork) в локальную переменную pid. Конечно, только один процесс может выполняться одновременно на одном процессоре, поэтому порядок, в котором происходят эти "возвраты", будет определяться планировщиком.

3
ответ дан 2 December 2019 в 03:38
поделиться
Другие вопросы по тегам:

Похожие вопросы: