- •9.3. Створення процесів
- •Базові поняття процесів і потоків
- •1.1. Процеси і потоки в сучасних ос
- •1.2. Моделі процесів і потоків
- •1.3. Складові елементи процесів і потоків
- •2. Багатопотоковість та її реалізація
- •2.1. Поняття паралелізму
- •2.2. Види паралелізму
- •2.4. Способи реалізації моделі потоків
- •2.3. Переваги і недоліки багато потоковості
- •3. Стани процесів і потоків
- •4. Опис процесів і потоків
- •4.1. Керуючі блоки процесів і потоків
- •4.2. Образи процесу і потоку
- •5. Перемикання контексту й обробка переривань
- •5.1. Організація перемикання контексту
- •5.2. Обробка переривань
- •6. Створення і завершення процесів і потоків
- •6.1. Створення процесів
- •6.2. Ієрархія процесів
- •6.3. Керування адресним простором під час створення процесів
- •6.4. Особливості завершення процесів
- •6.5. Синхронне й асинхронне виконання процесів
- •6.6. Створення і завершення потоків
- •7. Керування процесами в unix і Linux
- •7.1. Образ процесу
- •7.2. Ідентифікаційна інформація та атрибути безпеки процесу
- •7.3. Керуючий блок процесу
- •7.5. Завершення процесу
- •7.7. Запуск програми
- •7.8. Очікування завершення процесу
- •7.9. Сигнали
- •8. Керування потоками в Linux
- •8.1. Базова підтримка багато потоковості
- •8.2. Особливості нової реалізації багато потоковості в ядрі Linux
- •8.3. Потоки ядра Linux
- •8.4. Програмний інтерфейс керування потоками posix
- •9. Керування процесами у Windows хр
- •9.1. Складові елементи процесу
- •9.2. Структури даних процесу
- •9.3. Створення процесів
- •9.4. Завершення процесів
- •9.5. Процеси і ресурси. Таблиця об’єктів процесу
- •9.6. Програмний інтерфейс керування процесами Win32 арі
- •10. Керування потоками у Windows хр
- •10.1. Складові елементи потоку
- •10.2. Структури даних потоку
- •10.3. Створення потоків
- •10.4. Особливості програмного інтерфейсу потоків
- •Висновки
- •Контрольні запитання та завдання
7.5. Завершення процесу
Для завершення процесу в UNIX-системах використовують системний виклик
_exit( ). Розглянемо реалізацію цього системного виклику в Linux. Під час його виконання відбуваються такі дії.
1. Стан процесу стає TASK Z0MBIE (зомбі, про цей стан див. нижче).
2. Процес повідомляє предків і нащадків про те, що він завершився (за допомогою спеціальних сигналів).
3. Звільняються ресурси, виділені під час виклику fork( ).
4.Планувальника повідомляють про те, що контекст можна перемикати.
У прикладних програмах для завершення процесу можна використати безпосередньо системний виклик _exit( ) або його пакувальник - бібліотечну функцію exit( ). Ця функція закриває всі потоки процесу, коректно вивільняє всі ресурси і викликає _exit( ) для фактичного його завершення:
Зазначимо, що краще не викликати exit( ) тоді, коли процес може використовувати ресурси разом з іншими процесами (наприклад, він є процесом-нащадком, який має предка, причому нащадок успадкував у предка дескриптори ресурсів). Причина полягає в тому, що в цьому разі спроба звільнити ресурси в нащадку призведе до того, що вони виявляться звільненими й у предка. Для завершення таких процесів потрібно використати _exit( ).
7.7. Запуск програми
Запуск нових програм в UNIX відокремлений від створення процесів і реалізований за допомогою системних викликів сімейства еxeс( ). На практиці звичайно реалізують один виклик (у Linux це — execve( )),
У Linux підтримують концепцію оброблювачів двійкових форматів (binary format handlers). Формати виконуваних файлів заздалегідь реєструють в ядрі, при цьому їхній набір може динамічно змінюватися. З кожним форматом пов’язаний оброблювач — спеціальна структура даних linux_binfmt, що містить покажчики на процедури завантаження виконуваного файла (1oad_binary) і динамічної бібліотеки (load_shlib) для цього формату.
Тепер зупинимося на послідовності кроків виконання execve( ) у Linux. Вхідними параметрами цього виклику є рядок з ім’ям виконуваного файла програми, масив аргументів командного рядка (до яких у коді процесу можна буде дістати доступ за допомогою масиву argv) і масив змінних оточення (що є парами ім’я=значення). Виклик execve( ) відбувається так.
1. Відкривають виконуваний файл програми.
2. Використовуючи вміст цього файла, ініціалізують спеціальну структуру даних bprm, що містить інформацію, необхідну для виконання файла, зокрема відомості про його двійковий формат.
3. Виконують дії для забезпечення безпеки виконання коду.
4. На базі параметрів виклику формують командний рядок і набір змінних оточення програми (це спеціальні поля структури bprm).
5. Для кожного зареєстрованого оброблювача бінарного формату викликають процедуру завантаження виконуваного файла через покажчик load_binary і перевіряють результат цього виклику:
а) у разі коректного завантаження оброблювач починає виконувати код нової програми;
б) якщо жоден з оброблювачів не зміг коректно завантажити код, повертають помилку.
Детальному розгляду процесу завантаження програмного коду в пам’ять і форматів виконуваних файлів буде присвячено окрема лекція.
Запуск виконуваних файлів у прикладних програмах
Розглянемо приклад запуску виконуваного файла за допомогою системного виклику execve( ). Синтаксис цього виклику такий:
Останнім елементом argv і envp має бути нульовий покажчик.
Повернення з виклику буде тільки тоді, коли програму не вдалося завантажити (наприклад, файл не знайдено). Тоді буде повернено - 1.
Наведемо приклад формування параметрів і виклику (зазначимо, що першим елементом argv задане ім’я виконуваної програми):
Реалізація технології fork+exec
Наведемо приклад коду, що реалізує технологію fork+exec:
