Почему это не неопределенно, если vfork () вызывает exec family? - PullRequest
1 голос
/ 14 августа 2011

Согласно справочной странице vfork(), поведение не определено, если vfork() изменяет какие-либо данные, кроме pid_t , перед тем как он вызовет _exit или exec семейство системных вызовов.

ByЯ понимаю, что если дочерний процесс, созданный vfork(), вызывает exec(), то он может изменять любые данные, и поведение по-прежнему не определено.

Мои вопросы:

  1. Также известно, что дочерний процесс использует родительское адресное пространство, так почему же, если дочерний процесс перезаписывает свое собственное и родительское изображение с помощью exec, поведение не определено?

  2. Чтопроисходит с родителем, если ребенок вызывает exec и после этого он возвращается?Родитель начинает использовать новую копию, созданную потомком с помощью exec?

Ответы [ 6 ]

5 голосов
/ 14 августа 2011

Вызов exec заменяет все адресное пространство ребенка новым адресным пространством.Любое общее адресное пространство будет полностью заменено вызовом.

Функция vfork существует только в качестве оптимизации.Для некоторых операционных систем fork очень дорого, потому что дочерний процесс может потенциально изменить любую страницу, отображаемую в памяти, поэтому каждая отдельная страница должна быть модифицирована для копирования при записи (или, фактически, фактически скопирована!), Чтобы не изменятьсоответствующие страницы родителей.Очень распространенная последовательность - это fork, за которой сразу следует exec, что заставляет эти системы переназначать все страницы, чтобы отбросить их на долю секунды позже.Вместо того, чтобы пытаться изменить все сопоставления, vfork позволяет вам оставить сопоставления в неопределенном состоянии в дочернем процессе при условии, что вы все равно не будете их использовать.

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

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

Таким образом,Короткий ответ на ваш вопрос заключается в том, что если бы vfork был спроектирован таким образом, его нельзя было бы использовать по назначению.

4 голосов
/ 14 августа 2011

Я думаю, что ваше неправильное понимание ключа - это то, что делает exec: оно не "перезаписывает память" новым процессом.Скорее он отбрасывает всю свою виртуальную память (будь то ранее частные сопоставления, общие сопоставления или что-то еще) и создает совершенно новое виртуальное адресное пространство для идентификатора вызывающего процесса, соответствующего новому образу процесса (исполняемый файл).Это не имеет отношения к адресному пространству родителя, за исключением того, что счетчик ссылок в структурах управления памятью уменьшен (он был увеличен на vfork).

4 голосов
/ 14 августа 2011

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

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

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

Дочерний процесс не может «вернуться» из успешного exec. После успешного exec создается новое адресное пространство и начинается процесс, начинающийся с main.

vfork потенциально может приостановить действия родителя до тех пор, пока ребенок не выполнит exec или exit. В этом смысле дочерний элемент может как бы возвращаться из exec, потому что выполнение родительского процесса возобновит его, если он был остановлен. Но адресное пространство родительского процесса остается нетронутым даже в общей ситуации, потому что в случае exec или exit будет просто на одну ссылку меньше исходное адресное пространство (родительского).

1 голос
/ 16 августа 2011

vfork МОЖЕТ на самом деле не запускать разветвленный процесс в отдельном адресном пространстве, поэтому он ведет себя больше как «поток» (кроме случаев, когда нет параллельного выполнения или отдельного стека). Это означает, что вы ничего не должны делать с ребенком, кроме exec или _exit.

Некоторые ядра (uclinux? ELKS?), Которые поддерживают vfork, не поддерживают fork - например, в системах без MMU поддержка fork () практически невозможна (даже путем копирования страниц). Каждый процесс должен быть запущен независимо, так как все они совместно используют адресное пространство.

Таким образом, vfork может быть правильно реализован на них, а fork - нет.

1 голос
/ 14 августа 2011

Я думаю, что это базовый момент путаницы: обычно fork создает новое адресное пространство, дублируя родительский элемент, а exec заменяет адресное пространство вызывающего абонента новым, загруженным из исполняемого файла на диске.Итак, если vfork не не дублирует родительское адресное пространство, то как получается, что вызов exec после vfork не разрушает родительское адресное пространство, оставляя родителю некуда возобновить выполнение?

Ответ заключается в том, что это сделает vfork бесполезным, поэтому ядро ​​этого избегает.Когда exec вызывается с дочерней стороны vfork, он создает новое адресное пространство, загружает туда исполняемый файл и оставляет только вызывающее адресное пространство.Дочерний процесс затем переключается на новое адресное пространство, и родительский процесс возобновляет выполнение в своем неизмененном исходном адресном пространстве.

Вся опасность vfork возникает из дочернего временно выполняется в адресном пространстве родителя, пока не вызовет exec или _exit.Любые побочные эффекты того, что делает ребенок, остаются и влияют на родителей, возможно, катастрофически.Если вы не находитесь в системе, где vfork является просто псевдонимом для fork, в этом случае они не будут привязаны.Таким образом, вы не можете рассчитывать ни на одно поведение, и вам следует избегать каких-либо действий в отношении ребенка.

1 голос
/ 14 августа 2011

vfork было изобретено в качестве оптимизации для fork + exec. Идея заключалась в том, что «если вы планируете позвонить fork(), а затем exec(...)», используйте vfork, и мы сделаем все от нас зависящее, чтобы воспользоваться этим и ускорить процесс ».

Ограничение состоит в том, чтобы предоставить разработчикам максимальную гибкость, включая произвольные сюрпризы, если вы делаете что-то кроме exec.

Ребенок не может "вызвать exec, а затем вернуться". Семейство exec не возвращает . Он заменяет все изображение. Так что вторая часть вашего вопроса не отвечает.

...