
- •Глава 3
- •Раздел 3 Управление процессами и потоками
- •3.1. Базовые понятия процессов и потоков
- •3.1.2. Модели процессов и потоков
- •3.1.3. Составные элементы процессов и потоков
- •3.2. Многопотоковость и ее реализация
- •3.2.1. Понятие параллелизма
- •3.2.2. Виды параллелизма
- •3.2.3. Преимущества и недостатки многопотоковости
- •3.2.4. Способы реализации модели потоков
- •3.2.5 Состояния процессов и потоков
- •3.3 Описание процессов и потоков
- •3.3.1. Управляющие блоки процессов и потоков
- •3.3.2. Образы процесса и потока
- •3.4. Переключение контекста и обработка прерываний
- •3.4.1. Организация переключения контекста
- •3.4.2. Обработка прерываний
- •3.5 Создание и завершение процессов и потоков
- •3.5.1 Создание процессов
- •3.5.2. Иерархия процессов
- •3.5.3. Управление адресным пространством во время создания процессов
- •3.5.4. Особенности завершения процессов
- •3.5.5. Синхронное и асинхронное выполнение процессов
- •3.5.6. Создание и завершение потоков
- •3.6 Управление потоками в Linux
- •3.6.1. Базовая поддержка многотопотоковости
- •3.6.2. Особенности новой реализации много поточности в ядре Linux
- •3.6.3. Потоки ядра Linux
- •3.7 Управление процессами в Windows хр
- •3.7.1. Составные элементы процесса
- •3.7.2. Структуры данных процесса
- •3.7.3. Создание процессов
- •3.7.4. Завершение процессов
- •3.7.5. Процессы и ресурсы. Таблица объектов процесса
- •3.8 Управление потоками в Windows хр
- •3.8.1. Составные элементы потока
- •3.8.2. Структуры данных потока
- •3.8.3. Создание потоков
3.5.3. Управление адресным пространством во время создания процессов
Поскольку основным элементом процесса есть защищенное адресное пространство, очень важно решить проблему его распределения во время создания нового процесса.
Рассмотрим два разных подхода.
-
Системные вызовы fork() и ехес()
В первом подходе адресное пространство потомка создают как точную копию адресного пространства предка. Такая операция реализована системным вызовом, который в POSIX-системах называют fork().
В этом случае копируется не только адресное пространство, а и счетчик команд главного потока процесса, поэтому после вызова fork() предок и потомок будут выполнять одну и ту же инструкцию. Разработчик должен определить, в котором с двух процессов находится управление. Это можно сделать на основании отличий между кодами возвращения fогк() для предка и потомка.
Когда создание нового процесса происходит путем дублирования адресного пространства предка, и тогда возникает потребность в специальных средствах для загрузки программного кода в адресное пространство процесса. Такие средства реализует системный вызов, который в POSIX -системах называют ехес(). Как параметр для вызова ехес() надо указывать весь путь к выполняемому файла программы, который будет загружено в память.
В системах с поддержкой fork() для того чтобы запускать на выполнение программы, после вызова fork() нужно немедленно вызвать ехес() (это называют технологией fork +ехес).
-
Запуск приложения одним системным вызовом
Второй подход не разделяет дублирования адресного пространства и загрузку кода - эти этапы здесь объединенные в один. В данном случае системный вызов запускает на выполнение заданное приложение (обычно для этого ему нужно указать весь путь к выполняемому файла этого приложения).
Можно выделить два этапа выполнения такого системного вызова:
-
выделение памяти под адресное пространство нового процесса (ни одна информация при этом из адресного пространства предка не копируется);
-
загрузка выполняемого кода из указанного файла в выделенное адресное пространство.
Подход с использованием fork() и ехес() гибче , так как он дает возможность в случае необходимости ограничиться каким-то одним этапом запуска применения. Современные ОС преимущественно реализуют некоторую комбинацию первого и второго подходов.
Технология копирования во время записи
Если во время создания процесса с помощью вызова fork() каждый раз раньше времени выделять память и копировать у нее данные адресного пространства предка (стек, область глобальных сменных, программный код), то это будет довольно трудоемкой задачам, особенно для процессов, которые занимают много места в памяти.
Для решения этой проблемы в современных ОС используют технологию копирования во время записи (сору-on-write). При этом во время вызова fork() данные из памяти предка в память потомка не копируют, вместе с тем соответствующие участки памяти отображают в адресное пространство - как потомка, так и предка, но при этом обозначают их как защищенные от записи.
Как только потомок или предок попробует изменить какую-то из названных выше участков памяти, т.е. что-то записать у нее, возникает аппаратное прерывание. Система реагирует на него, выделяет память под новую область, открытую для записи соответствующему процессу, и изменяет адресное пространство для обеих процессов (например, для второго процесса соответствующий участок памяти приоткрывается для записи). Следует отметить, что в случае использования этой технологии участка памяти, которые не изменяются (например, те, что содержат код программы), могут вообще никогда не копироваться.
Во многих современных системах копирования во время записи является основной технологией управления адресным пространством в случае создания процессов.