Завершення процесу

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

Зазвичай знищення процесу відбувається тоді, коли процес викликає системний виклик exi t () явно або неявно при виході з головної функції програми (компілятор мови С поміщає виклик функції exi t () після повернення з функції main ()) Процес також може бути завершений мимоволі Це відбувається, коли процес отримує сигнал або виникає виняткова ситуація, яку про-

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

• Встановлюється прапор PF_EXITING в поле flag s структури tas k struct

• Викликається функція del_timer_syn c (), щоб видалити всі таймери ядра

Після виходу з цієї функції гарантується, що немає ніяких очікують таймерів і ніякої обробник таймера не виконується

• Якщо включена можливість обліку системних ресурсів, зайнятих процесами (BSD process accounting), то викликається функція acct_proces s () для запису інформації про облік ресурсів, які використовувалися процесом

• Викликається функція __ exit_mm () для звільнення структури mm_struct, зайнятої процесом Якщо ця структура не використовується більше ні одним процесом (іншими словами, не є розділяється), то вона звільняється зовсім

• Викликається функція exit_sem () Якщо процес знаходиться в черзі очікування на звільнення семафора підсистеми IPC, то в цій функції процес видаляється з цієї черги

• Викликаються функції __ exit_file s (), __ exit_f s (), exit_namespace () і exit_signal s () для зменшення лічильника посилань на обєкти, які відповідають файловим дескрипторах, даними по файловій системі, простору імен і обробникам сигналів відповідно Якщо лічильник посилань какоголибо обєкта досягає значення, рівного нулю, то відповідний обєкт більше не використовується ніяким процесом і віддаляється

• Встановлюється код завершення завдання, який зберігається в полі exitcod e структури tas k struct Значення цього коду передається як аргумент функції exi t () або задається тим механізмом ядра, через якого процес завершується

• Викликається функція exitnoti f у (), яка відправляє сигнали батьківському процесу завершується завдання і призначає новий батьківський процес (reparent) для всіх породжених що завершується завданням процесів, цим процесом стає або який-небудь один потік з групи потоків завершується процесу, або процес init Стан завершується процесу встановлюється в значення TASK_ZOMBIE

• Викликається функція schedule () для перемикання на новий процес (див главу 4, Планування виконання процесів) Оскільки процес в стані TASK_ZOMBIE ніколи не планується на виконання, цей код є останнім, який виконується що завершується процесом

Вихідний код функції do_exit () описаний у файлі kernel / exitс

До цього моменту звільнені всі обєкти, зайняті завданням (якщо вони використовуються тільки цим завданням) Завдання більше не може виконуватися (справді, у неї більше немає адресного простору, в якому вона може виконуватися), а крім того, стан завдання – TASK_ZOMBIE Єдині області памяті, які тепер займає процес, – це стек режиму ядра і слябової обєкт, відповідно містять структури thread_in f о і task_struct

Завдання завершено настільки, наскільки залишається можливість передати необхідну інформацію батьківському процесу

Видалення дескриптора процесу

Після повернення з функції do_exi t () дескриптор завершеного процесу все ще існує в системі, але процес знаходиться в стані TASK_ZOMBIE і не може виконуватися Як вже розповідалося вище, це дозволяє системі отримати інформацію про породженому процесі після його завершення Отже, завершення процесу і видалення його дескриптора відбуваються в різні моменти часу Після того як батьківський процес отримав інформацію про завершеному породженому процесі, структура task_struc t породженого процесу звільняється

Сімейство функцій wai t () реалізовано через єдиний (і досить складний) системний виклик wait4 () Стандартну поведінку цієї функції – призупинити виконання викликає завдання до тих пір, поки один з її породжених процесів не завершиться При цьому повертається ідентифікатор PID завершеного породженого процесу На додаток до цього, в дану функцію передається покажчик на область памяті, яка після повернення з функції міститиме код завершення завершився породженого процесу

Коли приходить час остаточно звільнити дескриптор процесу, викликається функція release_tas k (), яка виконує зазначені нижче операції

• Викликається функція free_ui d () для декремента лічильника посилань на інформацію про користувача процесу В системі Linux підтримується кеш з інформацією про кожного користувача, зокрема скільки процесів і відкритих файлів має користувач Якщо лічильник посилань досягає значення нуль, то користувач більше не має запущених процесів і відкритих файлів, в результаті кеш знищується

• Викликається функція unhash_process () для видалення процесу з хеш-таблиці ідентифікаторів процесів pidhash і видалення завдання зі списку завдань

• Якщо завдання було в стані трасування (ptrace), то батьківським для неї знову призначається первісний батьківський процес і завдання видаляється зі списку завдань, які перебувають у стані трасування (pirate)  даним процесом

• Зрештою викликається функція put_task_struc t () для звільнення сторінок памяті, що містять стек ядра процесу та структуру thread_infо, a також звільняється слябової кеш, що містить структуру  task_struct

На даному етапі дескриптор процесу, а також всі ресурси, які належали тільки цьому процесу, звільнені

Дилема безпритульного процесу

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

небудь один потік з групи потоків завершився батьківського процес са, або процес init При виконанні функції do_exi t () викликається функція notify_paren t (), яка в свою чергу викликає forget_origina l paren t () для здійснення перепризначення батьківського процесу (reparent), як показано нижче

struct task_struct * р, * reaper = father

struct list_head *list

if (father-&gtexit_signal = -1)

reaper = prev_thread(reaper)

else

reaper = child_reaper

if (reaper == father)

reaper = child_reaper

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

list_for_each(list,&ampfather-&gtchildren){

р = list_entry (list, struct task_struct, sibling)

reparent_thread(p, reaper, child_reaper)

}

list_for_each (list, sfather-&gtptrace children) {

p = list_entry(list, struct task:_struct, ptrace_list)

reparent_thread(p, reaper, child_reaper)

}

У цьому програмному коді організовано цикл за двома списками: за списком породжених процесів child list і по списку породжених процесів, що знаходяться в стані трасування іншими процесами ptraced child list Основна причина, по якій використовується саме два списки, досить цікава (ця нова особливість зявилася в ядрах серії 26) Коли завдання знаходиться в стані ptrace,  для неї тимчасово призначається батьківським той процес, який здійснює налагодження (debugging) Коли завершується істинний батьківський процес для такого завдання, то для такої дочірньої завдання також потрібно здійснити перепризначення батьківського процесу У ядрах більш ранніх версій це призводило до необхідностіорганізації циклу за всі завданням системи для пошуку породжених процесів Рішення проблеми, як було зазначено вище, – це підтримка окремого списку для породжених процесів, які перебувають у стані трасування, що уменишает число операцій пошуку: відбувається перехід від пошуку породжених процесів по всьому списку завдань до пошуку тільки по двох списками з досить малим числом елементів

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

Процес Ini t періодично викликає функцію wai t () для всіх своїх породжених процесів і, відповідно, видаляє всі зомбі-процеси, призначені йому

Резюме

У цій главі розглянута важлива абстракція операційної системи – процес Тут описані загальні властивості процесів, їх призначення, а також представлено порівняння процесів і потоків Крім того, описується, як операційна система Linux зберігає і представляє інформацію, яка відноситься до процесів (структури task_struc t і thread_infо), як створюються процеси (виклики clone () і fork ()), яким чином нові виконувані образи завантажуються в адресне простір (сімейство викликів exec ()), ієрархія процесів, яким чином батьківський процес збирає інформацію про своїх нащадків (сімейство функцій wait ()) і як зрештою процес завершується (Мимоволі або за допомогою виклику exi t ())

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

У наступному розділі розповідається про планування виконання процесів – витонченою і цікавою функції ядра, завдяки якій ядро ​​приймає рішення, які процеси повинні виконуватися, в який час і в какомпорядке

Джерело: Лав, Роберт Розробка ядра 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>

*

*