Чи fork () негайно копіює всю купу процесів в Linux?


30

fork()Системний виклик клонує дочірній процес від запущеного процесу. Два процеси однакові, за винятком їх PID.

Природно, якщо процеси просто зчитуються з їх купи, а не пишуться на них, копіювання купи було б величезною тратою пам’яті.

Чи скопійовано весь купі процесу? Чи оптимізовано таким чином, що лише написання запускає купі копію?

Відповіді:


19

Цілісність з fork()реалізуються з допомогою ММАПА / копіювання при записі.

Це не тільки впливає на купу, але й спільні бібліотеки, стеки, області BSS.

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

Вам буде важко знайти в сучасній ОС приклад операції, коли ядро ​​виконує друковану копію (виняток становлять драйвери пристроїв) - використовувати функціонал VM просто набагато, набагато простіше та ефективніше.

Навіть execve(), по суті, "будь-ласка, перегляньте бінарне / ld.so / whatnot, за яким слід виконати" - і VM обробляє фактичне завантаження процесу в оперативну пам'ять та виконання. Локальні неініціалізовані змінні в кінцевому підсумку перетворюються на "нульову сторінку" - спеціальна сторінка для копіювання на записі, що містить лише нульове читання, що містить нулі, локальні ініціалізовані змінні в кінцевому підсумку перетворюються на копіювання (копіювання під час запису, знову) від самого бінарного файлу, тощо.


Одне помітне виняток - процеси Java. Шукайте "fork java memory", і ви знайдете десятки проблем, що стосуються великого сервера JVM або вбудованого JVM, намагаючись виконати невелику команду оболонки і нещасно забиваються на виняток "Не можна виділити пам'ять" (це лише випадкові посилання, ця проблема є системною до середовищ Java). Ця відповідь ТАК звинувачує збирач сміття та компілятор JVM у збереженні пам'яті процесів від спільного використання.
WhiteWinterWolf

24

Ядро Linux реалізує функцію Copy-on-Write, коли fork()викликається. Коли виконується системний виклик, сторінки, якими ділиться батько та дитина, позначаються лише для читання.

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


1
+1 Дякую! 1. Чи можете ви надати посилання? 2. Чи купірується купа цілком або по частинах?
Адам Матан

4
2. - На сторінках :) У ядрі дуже мало розуміння, що таке "купа" - для ядра, це лише купа закритих приватних сторінок, якими алокатори libc обробляють як завгодно.
qdot

Це справді форк-бомба обов’язково? Мені здається, що замість форсування поточного процесу цей код створить більше екземплярів тієї самої програми, які виконуються із запуску, а не з наступної інструкції після fork()виклику.
sherrellbc

@mmk FYI, я був дуже здивований вашою "Цікавою стороною." Я використовував /proc/self/pagemapдля визначення віртуальної адреси до відображення фізичної сторінки для тесту. Як я і очікував, якщо онук і лише онук напишуть спільну сторінку, то батьків і оригінальна дитина продовжують ділитися нею. Лише онук закінчує приватну копію.
Селада

@Celada Хм. Я десь читав це, і я не пам’ятаю версію ядра, на яку він посилався (напевно, старішу?), Тож це може бути вже не дійсним.
mmk

10

Linux робить функцію Copy-on-Write. По мірі forkстворення нового процесу виділені сторінки позначаються як прочитані і розподіляються між батьком і дитиною. Коли будь-яка з них намагається змінити сторінку, генерується помилка сторінки, що призводить до її копіювання та коригування таблиці сторінок відповідно.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.