
- •Глава 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.4. Особенности завершения процессов
Рассмотрим три варианта завершения процессов.
-
Процесс корректно завершается самостоятельно после выполнения своей задачи (для интерактивных процессов это нередко происходит по инициативе пользователя, который, например, воспользовался соответствующим пунктом меню). Для этого код процесса должны выполнить системный вызов завершения процесса.
Такой вызов в POSIX-системах называют ехіt(). Он может возвратить процессу, который его вызвал, или ОС код возвращения, который дает возможность оценить результат выполнения процесса.
-
Процесс аварийно завершается через ошибку. Такой выход может быть предусмотрен программистом (во время обработки ошибки принимается решение о том, что дальше продолжать выполнение программы невозможно), а может быть следствием генерирования прерывания (деление на нуль, доступа к защищенной области памяти и т.п.).
-
Процесс завершается другим процессом или ядром системы. Например, перед окончанием работы ОС ядро прекращает выполнение всех процессов. Процесс может прекратить выполнение другого процесса с помощью системного вызова, который в РOSIX-системах называют kill().
После того как процесс завершит свою работу, память, отведенная под его адресное пространство, освобождается и может быть использованная для других нужд. Все потоки этого процесса тоже прекращают работу. Если у данного процесса есть потомки, их работа преимущественно не прекращается вслед за работой предка.
Интерактивные процессы обычно завершаются в случае выхода пользователя из системы.
3.5.5. Синхронное и асинхронное выполнение процессов
Когда в системе появляется новый процесс, для старого процесса есть два основных варианта действий:
-
продолжить выполнение параллельно с новым процессом - такой режим работы называют асинхронным выполнением;
-
приостановить выполнение до тех пор, поки новый процесс не будет завершен, - такой режим работы называют синхронным выполнением. (В этом случае используют специальный системный вызов, который в POSIX-системах называют wait().)
Выбор того или другого режима зависит от конкретной задачи. Так, например, веб-сервер может создавать процессы-потомки для обработки запросов (если имеющегося набора потомков недостаточно для такой обработки). В этом случае обработка должна быть асинхронной, так как сразу после создания потомка предок должен быть готовый к получению следующего запроса. С другой стороны, компилятор С во время запуска процессов, которые отвечают этапам компиляции, должны ждать завершения каждого этапа, прежде чем перейти к следующему, - в таком случае используют синхронную обработку.
3.5.6. Создание и завершение потоков
Особенности создания потоков
Процедура создания потоков имеет свои особенности, связанные прежде всего из тем, что потоки создают в рамках уже существующего адресного пространства (конкретного процесса или, возможно, ядра системы). Существует несколько ситуаций, в которых может быть создан новый поток.
Если процесс создается с помощью системного вызова тогда, после распределения адресного пространства автоматически создается поток внутри этого процесса (чаще всего это - главный поток приложения).
Можно создавать потоки из кода пользователя с помощью соответствующего системного вызова.
Во многих ОС есть специальные потоки, которые создает ядро системы (код ядра тоже может выполняться в потоках).
Во время создания потока система должна выполнить такие действия.
-
Создать структуры данных, которые отображают поток в ОС.
-
Выделить память под стек потока.
-
Установить счетчик команд процессора на начало кода, который будет выполняться в потоке; этот код называют процедурой или функцией потока, указатель на такую процедуру передают в системный вызов создания потока.
Следует отметить, что во время создания потоков, в отличие от создания процессов нет необходимости выделять новую память под адресное пространство, поэтому по обыкновению потоки создаются быстрее и с меньшими затратами ресурсов.
Локальные сменные функции потока расположенные в стеку потока и доступные лишь его кода, глобальные сменные доступные всем потокам.
Особенности завершения потоков
Во время завершения потока его ресурсы высвобождаются (прежде всего, стек); эта операция обычно выполняется быстрее, чем завершение процесса. Поток может быть завершен, когда управление дойдет до конца процедуры потока; есть также специальные системные вызовы, предназначенные для досрочного прекращения выполнения потоков.
Как и процессы, потоки могут выполняться синхронно и асинхронно. Поток, создав другой поток, может приостановить свое выполнение к его завершению. Такое ожидание называют присоединением потоков (ожидает тот, кто присоединяет). После завершения присоединенного потока поток, который ожидал его завершение, может получить статус выполнения. Во время создания потока можно определить, подлежит ли он присоединению (если поток нельзя присоединить, его называют отдельным - сіеіаспеа). Если поток не является отдельным (такой поток будем называть присоединенным), после завершения его обязательно нужно присоединять, во избежание истечения памяти.
Дадим некоторые рекомендации относительно разработки многотопотоковых программ.
-
Нельзя определять порядок выполнения потоков, не позаботившись о его поддержке, так как скорость выполнения потоков зависит от многих факторов, большинство из которых программист не контролирует. Например, если запустить набор потоков внутри функции f() и не присоединить все эти потоки до завершения f(), некоторые из них могут не успеть завершить свое выполнение к моменту выхода из f(). Возможно даже, что часть потоков не завершится и к моменту следующего вызова функции, если программист выполнит его достаточно быстро.
-
Для потоков не поддерживается такая иерархия, как для процессов. Поток, который создал другой поток, имеет равные с ним права. Рассчитывать на то, что такой поток сам будет продолжать свое выполнение после завершения потоков-потомков, нет смысла.
-
Стек потока очищается после выхода из функции потока. Чтобы это предотвратить, не следует, например, передавать никуда указатели на локальные сменные такой функции. Если необходимые сменные, значения которых должны сохраняться между вызовами функции потока, но при этом они не будут доступные другим потокам, надо воспользоваться локальной памятью потока (Thread Local Storage, TLS) -определенным образом организованными данными, доступ к которым осуществляется с помощью специальных функций.