Чому vfork () призначений для використання, коли дочірній процес викликає exec () або exit () відразу після створення?


11

Концепції операційної системи та APUE кажуть

З vfork () батьківський процес призупиняється, і дочірній процес використовує адресний простір батьків. Оскільки vfork () не використовує функцію копіювання під час запису, якщо дочірній процес змінить будь-які сторінки адресного простору батьків, змінені сторінки будуть видимими для батьків, коли він відновиться. Тому vfork () слід використовувати з обережністю, щоб переконатися, що дочірній процес не змінює адресний простір батьків.

vfork () призначений для використання, коли дочірній процес викликає exec () або exit () відразу після створення.

Як я зрозумію останнє речення?

Коли дочірній процес, створений vfork()дзвінками exec(), не exec()змінює адресний простір батьківського процесу, завантажуючи нову програму?

Коли дочірній процес, створений vfork()дзвінками exit(), exit()не змінює адресний простір батьківського процесу при припиненні дитини?

Я маю перевагу перед Linux.

Дякую.

Відповіді:


15

Коли дочірній процес, створений vfork()дзвінками exec(), не exec()змінює адресний простір батьківського процесу, завантажуючи нову програму?

Ні, exec()надає новий адресний простір для нової програми; він не змінює батьківський адресний простір. Дивись, наприклад , на обговорення execфункцій в POSIX , і в Linux execve()довідкову сторінку .

Коли дочірній процес, створений vfork (), викликає exit (), чи не вимикає exit () змінити адресний простір батьківського процесу при завершенні дитини?

Звичайна можливість exit()- вона запускає гачки виходу, встановлені запущеною програмою (включаючи її бібліотеки). vfork()є більш обмежувальним; Таким чином, в Linux він мандатує використання _exit()якого не викликає функції очищення бібліотеки C.

vfork()виявилося досить важко отримати право; він був видалений у поточних версіях стандарту POSIX, і його posix_spawn()слід використовувати замість нього.

Однак, якщо ви дійсно не знаєте, що ви робите, ви не повинні використовувати жодне vfork()або posix_spawn(); дотримуватися доброго старого fork()і exec().

Наведена вище сторінка на сторінці Linux надає більше контексту:

Однак у погані старі часи a fork(2) потрібно буде зробити повну копію простору даних даних абонента, часто без потреби, оскільки зазвичай exec(3)це робиться негайно . Таким чином, для більшої ефективності BSD ввів vfork() системний виклик, який не повністю копіював адресний простір батьківського процесу, але запозичив батьківську пам’ять і нитку управління, поки не відбувся виклик execve(2)або вихід. Батьківський процес було призупинено, поки дитина використовувала свої ресурси. Використання vfork()було складним: наприклад, не змінюючи дані в батьківському процесі, залежало від того, щоб знати, які змінні зберігалися в реєстрі.


Дякую. "exec () забезпечує новий адресний простір для нової програми;" Чи нормальна поведінка exec () для завантаження програми в адресний простір процесу? Я не знайшов у двох посиланнях, де це створює новий адресний простір, як правило, або особливо для vfork ().
Тім

1
Що смішно, vfork () виграє майже майже все інше зараз. Це смішно швидше, ніж fork (), коли у вас є гігабайт пам'яті для запису.
Джошуа

2
Не кажіть людям користуватися posix_spawn. Значно складніше написати правильний код, використовуючи posix_spawnзвичайний старий fork, і якщо ви спробуєте, ви можете наткнутися на цегляну стіну там, не виконуючи файлових дій або атрибутів, які роблять те, що вам потрібно зробити між forkі exec. І не гарантується ефективність, схожа на vfork, тому це навіть не вирішує проблему, яку люди хочуть вирішити.
zwol

1
@zwol: Це дійсно погана порада. Хоча posix_spawnможливо не вистачає потрібної вам функціональності (ви можете вирішити це за допомогою програми помічників-посередників, написаної на С або сценарію оболонки вбудований на cmdline), будь-яка спроба досягти того, що ви хочете, vforkвикликає небезпечне невизначене поведінку. Специфікація vforkне дозволяє викликати випадкові функції для встановлення стану для успадкування дитини раніше execve, а спроби зробити це можуть пошкодити стан батьків.
R .. GitHub СТОП ДОПОМОГА ВІД

1
@Joshua: Сучасна реалізація posix_spawnпрацює приблизно так само, як і vforkв більшості умов. Ті, де є різниця, мають тенденцію бути саме такими випадками, коли vforkвкрай небезпечно: де встановлені обробники сигналів, які posix_spawnповинні придушити запуск дитини перед exec.
R .. GitHub СТОП ДОПОМОГА ВІД

4

Під час виклику vfork()створюється новий процес, який новий процес запозичує зображення процесу батьківського процесу за винятком стека. Дочірньому процесу надається власна нова зірка стека, однак це не дозволяє виконувати returnфункцію, яка викликала vfork().

Поки дитина працює, батьківський процес блокується, оскільки дитина запозичила адресний простір батьків.

Незалежно від того, що ви робите, все, що тільки отримує доступ до стека, змінює лише приватний стек дитини. Якщо ви змінюєте глобальні дані, це змінює загальні дані і, таким чином, впливає на батьків.

Речі, що змінюють глобальні дані, наприклад:

  • виклик malloc () або free ()

  • використовуючи stdio

  • зміни параметрів сигналу

  • модифікація змінних, які не є локальними для функції, що викликала vfork().

  • ...

Після того, як ви телефонуєте _exit()(важливо, ніколи не дзвоніть exit()), дитина припиняється і контроль передається батькові.

Якщо ви викликаєте будь-яку функцію з exec*()сім'ї, створюється новий адресний простір з новим програмним кодом, новими даними та частиною стека від батьків (див. Нижче). Після того, як це буде готово, дитина більше не запозичує адресний простір у дитини, а використовує власний адресний простір.

Елемент управління повертається батьківському, оскільки його адресний простір більше не використовується іншим процесом.

Важливо: На Linux немає реальної vfork()реалізації. Linux, скоріше, реалізує vfork()на основі fork()концепції Copy on Write, запровадженої SunOS-4.0 у 1988 році. Для того, щоб змусити користувачів вважати, що вони користуються vfork(), Linux просто налаштовує спільні дані та призупиняє батьків, поки дитина не дзвонила _exit()чи одна з exec*()функцій.

Отже, Linux не отримує користі від того, що реальному vfork()не потрібно встановлювати опис адресного простору для дитини в ядрі. Це призводить до того, vfork()що це не швидше, ніж fork(). У системах, що реалізують реальне vfork(), зазвичай це в 3 рази швидше fork()та впливає на продуктивність оболонок, які використовують vfork()- ksh93, недавні Bourne Shellта csh.

Причина , чому ви ніколи не повинні викликати exit()з vfork()ед дитини є те , що exit()флеші STDIO в разі , якщо є дані промиватися від часу перед викликом vfork(). Це може спричинити дивні результати.

BTW: posix_spawn()реалізується поверх vfork(), тому vfork()його не збирається видаляти з ОС. Згадано, що Linux не використовує vfork()для posix_spawn().

Для стека є мало документації, ось що говорить сторінка Solaris man:

 The vfork() and vforkx() functions can normally be used  the
 same  way  as  fork() and forkx(), respectively. The calling
 procedure, however, should not return while running  in  the
 child's  context,  since the eventual return from vfork() or
 vforkx() in the parent would be to a  stack  frame  that  no
 longer  exists. 

Тож реалізація може робити все, що завгодно. Реалізація Solaris використовує спільну пам'ять для кадру стека виклику функції vfork(). Жодна реалізація не надає доступу до старших частин стека від батьків.


4
Ні бібліотека GNU C, ні бібліотека musl C не реалізовані posix_spawn()на вершині Linux vfork(). Вони обидва реалізують це поверх __clone().
JdeBP

1
@JdeBP: Ви знаєте vfork()просто дзвінки clone()так? Це буквально однолінійка в ядрі.
Джошуа

1
"Важливо: В Linux немає реальної реалізації vfork ()." <- Це неправда і не було правдою принаймні десятиліття. Якщо ваш показник оболонки не дотримується різниці в продуктивності між Linux vforkта forkLinux, він робить щось не так.
zwol

1
Друга половина цієї відповіді, що починається з "Важливо: В Linux немає реальної реалізації vfork ()" здебільшого або зовсім неправильна.
R .. GitHub СТОП ДОПОМОГА ВІД

1
Будь ласка, не пред'являйте претензій без підтвердження. Поточна оболонка Bourne може бути скомпільована з підтримкою vfork і без неї, тому навіть якщо ви вважаєте, що функції налагодження з Linux не можуть дати надійних результатів, ви можете порівняти час виконання виклику конфігурації з і з vfork в оболонці. Я використовую сценарій налаштування з 800 тестами. Що стосується Solaris, то обороту Борна, що використовує vfork, потрібно на 30% менше часу на системний процесор порівняно із випадком вилки. У Linux той самий тест призводить до менш ніж на 10% менше системного процесорного часу. У Solaris це не 3 рази, оскільки включено багато викликів компілятора.
schily
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.