
- •Глава 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.6 Управление потоками в Linux
3.6.1. Базовая поддержка многотопотоковости
До недавнего времени единым средством поддержки многотопотоковости в ядре Linux был системный вызов clone(), который дает возможность создать новый процесс на базе имеющегося. Этот вызов во многом похожий на вызов fork(), но имеет несколько особенностей.
-
Для нового процесса нужно задать специальный набор флажков, которые определяют, как будут распределяться ресурсы между предком и потомком. К ресурсам, которые можно разделять, принадлежат адресное пространство, информация об открытых файлах, обработчике сигналов. Укажем, что в традиционной реализации clone() отсутствующее общее использование идентификатора процесса (pid), идентификатора предка и некоторых других важных атрибутов.
-
Для нового процесса нужно задать новый стек (поскольку вследствие вызова clone() два процессы могут совместно использовать общую память, для их недопустимое использование общего стека).
Поддержка clone() означает реализацию многотопотоковости по схеме 1:1. При этом первичными в системе будут процессы, а не потоки (в Linux последовательности инструкций процессора, с которыми работает ядро, называют процессами, а не потоками ядра).
Традиционная реализация многотопотоковости в Linux определяет, что потоки пользователя отображаются на процессы в ядре. Поэтому в этом разделе нецелесообразно рассматривать отдельно структуры данных потока в Linux - он отображается такой же самой структурой task_struct, как и процесс.
Библиотека поддержки потоков LinuxThreads
Непосредственно использовать системный вызов clone() для создания багатопо-токових применений сложно. Кроме того, такое использование ведет к потере способности к перенесению приложений (вызов clone() реализованный только в Linux и не является частью POSIX). Для того чтобы прикладной программист мог использовать в своих программах потоки POSIX, необходимая реализация этого стандарта в библиотеке поддержки потоков.
Основной библиотекой поддержки потоков в Linux длительное время была библиотека LinuxThreads. В ней поддерживаются те возможности стандарта POSIX.ld, которые реализуются на основе вызова clone(). К сожалению, через ограниченность этого вызова библиотеке присущий объективные недостатки, которые мы рассмотрим подробно.
Недостатки традиционной поддержки многопотоковости в Linux
Основы поддержки многотопотоковости в ядре Linux не испытали принципиальных изменений от появления системного вызова clone(). За это время Linux из экспериментальной системы превратился на систему промышленного уровня, в которой выполняют корпоративные применения в круглосуточном режиме с большой нагрузкой.
Такое использование системы выдвинуло к реализации многотопотоковости требования высокой надежности и масштабируемости. Стало очевидно, что реализация, основанная на системном вызове clone(), этим требованиям не отвечает. Назовем некоторые причины этого несоответствия.
-
Поскольку потоки, созданные с помощью clone(), фактически были процессами, которые совместно используют ресурсы, создание каждого потока увеличивало количество процессов в системе.
-
Каждый поток в системе, будучи по сути процессом, имел собственный идентификатор процесса (ріd), что не отвечало стандарта POSIX. Кроме того, если поток создавал другой поток, между ними возникала связь «предок-потомок», тогда как все потоки должны быть равноправными.
Каждое многотопотоковое применение пользователя обязательно создавало дополнительный поток (поток-менеджер), который отвечал за создание и уничтожение потоков и перенаправлял им сигналы. Наличие такого потока снижало надежность и масштабируемость системы (например, в случае аварийного завершения потока-менеджера приложения оставалось в неопределенном виде).
Все эти факторы делали многотопотоковость в Linux быстрее интересным полем для экспериментов, чем реальным средством повышения производительности и масштабируемости приложений. Решить все проблемы путем устранения недостатков clone() и LinuxThreads оказалось невозможным, нужно была полная переработка средств реализации многотопотоковости в ядре и создание новой библиотеки поддержки потоков.