Створення нового процесу

В операційній системі Unix створення процесів відбувається унікальним чином У більшості операційних систем для створення процесів використовується методпородженняпроцесів(spawn)  При цьому створюється новий процес у новому адресному просторі, в яке зчитується виконуваний файл, і після цього починається виконання процесу В ОС Unix використовується інший підхід, а саме розбивка зазначених вище операцій на дві функції: for k () і exe c () 8

8Под exec () будемо розуміти будь-яку функцію з сімейства exec * () У ядрі реалізований системний виклик execve () , Н а основі якого реалізовані бібліотечні е функції e xeclp() ,  execle(), execv( )      іexecvp()

На початку за допомогою функції fork () створюється породжений процес, який є копією поточного завдання Породжений процес відрізняється від батьківського тільки значенням ідентифікатора PID (який є унікальним в системі), значенням параметра PPID (ідентифікатор PID батьківського процесу, який встановлюється в значення PID породжує процесу), деякими ресурсами, такими як очікують на обробку сигнали (що не успадковуються), а також статистикою використання ресурсів Друга функція – exe c () – завантажує виконуваний файл в адресний простір процесу і починає виконувати його Комбінація функцій for k () і exe c () аналогічна тій одній функції створення процесу, яку надає більшість операційних систем

Копіювання при записі

Традиційно при виконанні функції fork () робився дублікат всіх ресурсів батьківського процесу і передавався породженому Такий підхід досить наївний і неефективний В операційній системі Linux виклик for k () реалізований з використанням механізму копіювання при запису (copy-on-write)сторінок памяті Технологія копіювання при запису (copy-on-write,COW)  дозволяє відкласти або взагалі запобігти копіювання даних Замість створення дубліката адресного простору процесу батьківський і породжений процеси можуть спільно використовувати одну і ту ж копію адресного простору Однак при цьому дані позначаються особливим чином, і якщо раптом один з процесів починає змінювати дані, то створюється дублікат даних, і кожен процес отримує унікальну копію даних Отже, дублікати ресурсів створюються тільки тоді, коли в ці ресурси здійснюється запис, а до того моменту вони використовуються спільно в режимі тільки для читання (read-only) Така техніка дозволяє затримати копіювання кожної сторінки памяті до того моменту, поки в цю сторінку памяті не здійснюватиметься запис У разі, якщо в сторінки памяті ніколи не робиться запис, як, наприклад, при виклику функції exe c () відразу після виклику for k (), то ці сторінки ніколи і не копіюються Єдині накладні витрати, які вносить виклик функції for k (), – це копіювання таблиць сторінок батьківського процесу і створення дескриптора породженого процесу Дана оптимізація запобігає непотрібне копіювання великої кількості даних (Розмір адресного простору часто може бути більш 10 Мбайт), так як процес після розгалуження в більшості випадків відразу ж починає виконувати новий виконуваний образ Ця оптимізація дуже важлива, тому шануй ідеологія операційної системи Unix передбачає швидке виконання процесів

Функція for k ()

В операційній системі Linux функція for k () реалізована через системний виклик clon e () Цей системний виклик може приймати як аргументи набір прапорів, що визначають, які ресурси мають бути загальними (якщо взагалі повинні) у батьківського і породженого процесів Далі в розділі Реалізація потоків в ядрі Linux про ці прапорах розказано більш докладно Бібліотечні виклики fork (), vfork () і clone d викликають системну функцію clon e () з відповідними прапорами У свою чергу системний виклик clon e () викликає функцію ядра  do_for k ()

Основну масу роботи по розгалуження процесу виконує функція do_f ork (), яка визначена у файлі kernel / forkс Ця функція, в свою чергу, викликає функцію copy_pracess () і запускає новий процес на виконання Нижче описана та цікава робота, яку виконує функція copy_process ()

• Викликається функція dup_task_struc t (), яка створює стек ядра, структури thread_inf o і task_struc t для нового процесу, причому всі значення зазначених структур даних ідентичні для породжує і породженого процесів На цьому етапі дескриптори батьківського та породженого процесів ідентичні

• Перевіряється, чи не відбудеться при створенні нового процесу переповнення ліміту на кількість процесів для даного користувача

• Тепер необхідно зробити породжений процес відмінним від батьківського При цьому різні поля дескриптора породженого процесу очищаються або встановлюються в початкові значення Велика кількість даних дескриптора процесу є спільно використовуваним

• Далі стан породженого процесу встановлюється в значення TASK_ UNINTERRUPTIBLE, щоб гарантувати, що породжений процес не буде виконуватися

• З функції copy_process () викликається функція copy_f lag s (), яка оновлює значення поля flag s структури tas k struct При цьому скидається прапор PF_SUPERPRIV, який визначає, має Чи процес права суперкористувача Прапор PF_FORKNOEXEC, який вказує на те, що процес не викликав функцію exec (), – встановлюється

• Викликається функція get_pi d (), яка призначає нове значення ідентифікатора PID для нового завдання

• Залежно від значень прапорів, переданих у функцію clone (), здійснюється копіювання або спільне використання відкритих файлів, інформації про файлову систему, обробників сигналів, адресного простору процесу і простору імен(namespace) Зазвичай ці ресурси спільно використовуються потоками одного процесу В іншому випадку вони будуть унікальними і будуть копіюватися на цьому етапі

• Відбувається поділ решти кванта часу між батьківським і породженим процесами (це більш докладно обговорюється в главі 4, Планування виконання процесів)

• Нарешті, відбувається остаточна зачистка структур даних і повертається вказівник на новий породжений процес

Далі відбувається повернення у функцію do_for k () Якщо повернення з функції copy_process () відбувається успішно, то новий породжений процес відновлює виконання Породжений процес навмисно запускається на виконання раніше родітельского9

9 Насправді зараз це работае т не так, як хотілося б, проте зусилля додаються до того, щоб породжений процес запускався на виконання першого

У звичайній ситуації, коли породжений процес відразу ж викликає функцію exe c (), це дозволяє уникнути накладних витрат, повязаних з тим, що якщо батьківський процес починає виконуватися першим, то він буде чекати на можливість запису в адресний простір за допомогою механізму копіювання при записі

Функція vfor k ()

Системний виклик vfork () дозволяє отримати той же ефект, що і системний виклик fork (), за винятком того, що записи таблиць сторінок батьківського процесу не копіюються Замість цього породжений процес запускається як окремий потік в адресному просторі батьківського процесу і батьківський процес блокується до того моменту, поки породжений процес не викличе функцію exec () або не завершиться Породженому процесу заборонена запис в адресний простір Така оптимізація була бажаною в старі часи 3BSD, коли реалізація системного виклику for k () НЕ базувалася на техніці копіювання сторінок памяті при запису Сьогодні, при використанні техніки копіювання сторінок памяті при запису і запуску породженого процесу перед батьківським, єдина перевага виклику vfork () – це відсутність копіювання таблиць сторінок батьківського процесу Якщо коли-небудь в операційній системі Linux буде реалізовано копіювання полів таблиць сторінок при запісі10, то взагалі не залишиться ніяких переваг Оскільки семантика функції vfork () досить ненадійна (що, наприклад, буде, якщо виклик exec () завершиться невдало), то було б здорово, якби системний виклик vfork () помер повільною і болісною смертю Цілком можна реалізувати системний виклик vfork () через звичайний виклик fork (), що дійсно мало місце в ядрах Linux до версії 22

Зараз системний виклик vfork () реалізований через спеціальний прапор в системному виклику clone (), як показано нижче

• При виконанні функції copy_proces s () поле vfork_don e структури task_struc t встановлюється в значення NULL

• При виконанні функції do_fvork (), якщо відповідний прапор установ льон, поле vfork_done встановлюється в нульове значення (починає вказувати на певну адресу)

• Після того як породжений процес вперше запущений, батьківський процес, замість того щоб повернутися з функції copy_process () до виконання, починає чекати, поки породжений процес не подасть йому сигнал через покажчик vfork_done

• При виконанні породженим процесом функції mm_release () (яка викликається, коли завдання закінчує роботу зі своїм адресним простором), якщо значення поля vfork_done не дорівнює NULL, батьківський процес отримує зазначений вище сигнал

• При поверненні в функцію do_fork () батьківський процес відновлює ви конання і виходить з цієї функції

10 Насправді вже зараз є латки для додавання такої функції в ОС Linux Хоча, швидше за все, можливість спільного використання таблиць сторінок в ядрах серії 26 реалізована не буде, така можливість може зявитися в майбутніх версіях

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

Джерело: Лав, Роберт Розробка ядра Linux, 2-е видання : Пер з англ – М: ТОВ «ІД Вільямс »2006 – 448 с : Ил – Парал тит англ

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*