дочірні процеси

Що відбувається при завершенні процесу

А відбувається ось що.

1. Виконання всіх потоків в процесі припиняється

2. Всі User- і GDI-об'єкти, створені процесом, знищуються, а об'еюи ядра закриваються (якщо їх не іспользуетдругой процес).

3. Код завершення процесу змінюється зі значення STILL_ACTIVE на код, переданий ний в ExitProcess або TerminateProcess.

4. Об'єкт ядра "процес" переходить у вільний, або незайняте (signaled), відбутися яние (Детальніше на цю тему див главу 9) Інші потоки в системі можуть при зупинити своє виконання аж до завершення даного процесу.

5. Лічильник об'єкта ядра "процес" зменшується на 1

Пов'язаний з завешуваним процесом об'єкт ядра не звільняє, поки не будуть закриті посилання на нього і з інших процесів. У момент завершення процесу система автоматично зменшує лічильник користувачів цього об'єкта на 1, і об'єкт руйнується, як тільки його лічильник обнуляється. Крім того, закриття процесу не призводить до автоматичного завершення породжених їм процесів

По завершенні процесу його код і виділені йому ресурси видаляються з пам'яті. Однак область пам'яті, виділена системою для об'єкта ядра "процес", які не осво няється, поки лічильник числа його користувачів не досягне нуля А це вироби дет, коли всі інші процеси, які створили або відкрили описатели для нині-за койне процесу, повідомлять систему (викликом CloseHandle) про те, що посилання па цей процес їм більше не потрібні.

Описувачі завершеного процесссов вже м'яло на що придатні. Хіба що роди тельский процес, викликавши функцію GetExitCodeProcess, може проверігь, завершено чи процес, ідентифікований параметром hProcess, і, якщо так, визначити код завер шення:

BOOL GetExitCodeProcess (HANDLE hProcess, PDWORD pdwExitCode);

Код завершення повертається як значення типу DWORD, на яке вказує pdwExitCode. Якщо па момент виклику GetExitCodeProcess процес ще не завершився, в DWORD заноситься ідентифікатор STILL_ACTIVE (певний як 0x103) А якщо він знищений, функція повертає реальний код його завершення.

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

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

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

поштові скриньки (mailslots) і т. д, А один з найзручніших способів, що забезпечують спільний доступ до даних, - використання файлів, проектує ваних в пам'ять (memory-mapped files) (Детальніше на цю тему див. главу 17.)

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

// породжуємо дочірній процес

BOOL fSuccess = CreateProcess (. pi>;

// закривайте описатель потоку, як тільки необхідність в ньому відпадає!

// припиняємо виконання батьківського процесу,

// поки не завершиться дочірній процес

WaitForSingleObject (pi hProcess, INFINlTI);

// дочірній процес завершився; отримуємо код його завершення

// закривайте описатель процесу, як тільки необхідність в ньому відпадає!

У цьому фрагменті коду ми створили новий процес і, якщо це пройшло успішно, викликали функцію WaitForSingleQbject

DWORD WaitForSingleObject (HANDLE hObject, DWORD dwTimeOut);

Детальний розгляд даної функції ми відкладемо до глави 0, а зараз ог ранічімся одним міркуванням Функція затримує виконання коду до тих пір,

поки об'єкт, який визначається параметром bObject, що не перейде у вільний (незайняте) стан. Об'єкт "процес" переходить в такий стан при його завершенні З цього виклик WaitForSingleObject призупиняє виконання потоку батьківського процесу до завершення породженого їм процесу. Коли WaitForSingleObject поверне управління, Ви дізнаєтеся код завершення дочірнього процесу через функцію Get ExitCodeProcess.

Звернення до CloseHandle в наведеному вище фрагменті коду змушує систе му зменшити значення лічильників об'єктів "потік" і "процес" до нуля і тим самим звільнити пам'ять, займану цими об'єктами.

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

- і, до речі, навіть рекомендується робити - саме так, стане ясно з про стого прикладу. Припустимо, первинний потік дочірнього процесу породжує ще один потік, а сам після цього завершується. У цей момент система може вивільнити об'єкт "первинний потік" дочірнього процесу з пам'яті, якщо у батьківського про процесу немає описателя даного об'єкта. Але якщо батьківський процес має таким описателем, система не зможе видалити цей об'єкт з пам'яті до тих пір, поки і батьківський процес не закриє його описувач.

Запуск відокремлених дочірніх процесів

Що не кажи, але частіше додаток все-таки створює інші процеси як обособ лені (detached processes) Це означає, що після створення і запуску нового процесу батьківському процесу немає потреби з ним взаємодіяти або чекати, поки той закінчить роботу Саме так і діє Explorer: запускає для користувача нові процеси, а далі його вже не хвилює, що там з ними відбувається.

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

BOOL fSuccess = CreateProcess (. pi); if (fSuccess)