Основные типы нитей

Процесс представляет собой составную сущность, которую можно разде­лить на два основных компонента: набор нитей и набор ресурсов. Нить — это динамический объект, в процессе представленный отдельной точкой управ­ления и выполняющий последовательность команд, отсчитываемую от этой точки. Ресурсы, включающие адресное пространство, открытые файлы, пол­номочия, квоты и т. д., используются всеми нитями процесса совместно. Каждая нить, кроме того, обладает собственными объектами, такими как ука­затель команд, стек или контекст регистров.

Существуют различные типы: нити ядра, легковесные процессы и прикладные нити.

Многонитевость в Solaris

Нити ядра не требуют связи с каким-либо прикладным процессом. Они создаются и уничтожаются ядром и внутри ядра по мере необходимости и от­вечают за выполнение определенных функций. Такие нити используют совместно доступные области кода и глобальные данные ядра, но обладают собственным стеком в ядре. Они могут независимо назначаться на выполне­ние и используют стандартные механизмы синхронизации ядра, такие как sleep() или wakeup().

Нити ядра применяются для выполнения таких операций, как асинхрон­ный ввод-вывод. Вместо поддержки каких-либо специальных механизмов ядро просто создает новую нить для обработки запросов для каждой такой операции. Запрос обрабатывается нитью синхронно, но для ядра представля­ется асинхронным событием. Нити ядра могут быть также использованы для обработки прерываний.

Нить ядра в системе Solaris — это основной легковесный объект, который мо­жет независимо планироваться и отправляться на выполнение одному из процессоров системы. Такой объект не нуждается в ассоциации с каким-либо процессом, он может быть создан, запущен и уничтожен ядром при помощи специальных функций. В результате ядру системы не нужно переотображать виртуальное адресное пространство при переключении нитей ядра. Структура данных нити ядра содержит следую­щую информацию:

  1. сохраненная копия регистров ядра;

  2. приоритет и информация, связанная с расписанием;

  3. указатели на местонахождение нити в очереди планировщика или, если выполнение нити блокировано, в очереди ожидания ресурсов;

  4. указатель на стек;

  5. указатели на связанные с нитью структуры Iwp и рrос

Легковесный процесс (или LWP, lightweight process) — это прикладная нить, поддерживаемая ядром. LWP является абстракцией высокого уровня, осно­ванной на нитях ядра. Каждый процесс может иметь один или более LWP, любой из которых поддерживается отдельной нитью ядра. Легко­весные процессы планируются на выполнение независимо от процесса, но совместно разделяют адресное пространство и другие ресурсы процесса. Они обладают возможностью производить системные вызовы и блокироваться в ожидании окончания ввода-вывода или освобождения ресурсов. На много­процессорных системах процесс в состоянии использовать преимущества на­стоящей параллельной работы, так как каждый LWP может выполняться на отдельном процессоре. Однако даже на однопроцессорных системах при­менение LWP дает существенные преимущества, поскольку при ожидании ресурсов или окончания ввода-вывода блокируется не весь процесс в целом, а только отдельные LWP.

Если доступ к каким-либо данным производится одновременно несколькими LWP, необходимо обеспечить некоторую синхронизацию досту­па. Каждый вызов требует двух переключений режима: сначала из режима задачи в режим ядра и обратное переключение после завершения ра­боты функции. При каждом переключении режима LWP пересекает границу защиты (protection boundary).

Легковесные процессы обеспечивают многонитевое выполнение внутри одно­го процесса. Планирование выполнения LWP происходит независимо, и такие процессы могут выполняться параллельно на многопроцессорных системах. Каждый LWP связан со своей собственной нитью ядра, такая связь не преры­вается на протяжении всего жизненного цикла процесса.

Традиционные структуры ргос и user недостаточны для представления многонитевых процессов.

Вдобавок к структурам, описывающим процесс в ядре, появляется новая структура — Iwp, которая хранит информацию о каждой LWP-составляющей контекста процесса. Структура Iwp содержит следующую информацию:

  1. сохраненные значения регистров прикладного уровня (когда LWP не выполняется);

  2. аргументы системного вызова, результаты работы и код ошибки;

- информацию об обработке сигналов;

- данные об использовании ресурсов и данные профиля процесса;

- указатель на нить ядра;

  • указатель на структуру ргос.

Все LWP используют общий набор обработчиков сигналов. Однако каж­дый LWP может обладать собственной маской сигналов, решая самостоятель­но, какие из полученных сигналов нужно игнорировать или блокировать. Легковесные процессы не имеют глобального пространства имен и вслед­ствие этого невидимы для других процессов.

Прикладные нити реализованы в системе при помощи нитевой библиотеки. Они могут создаваться, уничтожаться и обрабатываться без участия ядра. Библиотека также предоставляет средства синхронизации и планирования нитей. Это позволяет процессу использовать большое количество нитей, не потребляя при этом ресурсы ядра и не загружая систему лишними вызовами.

По умолчанию библиотека создает для каждого процесса набор LWP и муль­типлексирует в него все прикладные нити. Таким образом, процесс может обладать прикладными нитями двух типов: свободными (связанными с единственным LWP) нитями и несвязанными, разделяющими общий набор легковесных процессов.

Каждая прикладная нить должна поддерживать информацию следующего со­держания:

  1. Идентификатор нити (thread ID). Позволяет нитям взаимодействовать друг с другом в рамках процесса при помощи сигналов и прочих средств.

  2. Сохраненное состояние регистров (saved register state). Содержит указатель команд и указатель стека.

  3. Стек в приложении (user stack). Каждая нить обладает своим собственным стеком, размещаемым при помощи библиотеки. Ядро системы не знает о существовании подобных стеков.

  4. Маска сигналов (signal mask). Каждая нить может обладать собствен­ной маской сигналов. Когда приходит сигнал, библиотека доведет его до соответствующей нити, исходя из информации, содержащейся в мас­ках всех нитей.

  5. Приоритет (priority). Прикладная нить имеет приоритет внутри про­цесса, который используется планировщиком нитей.

  6. Локальная область хранения нити (thread kernel storage).

Во многих современных системах ядро поддерживает многонитевые про­цессы через LWP. В таком случае библиотеки прикладных нитей могут быть реализованы различными способами:

  1. Каждой нити назначается свой легковесный процесс.

  2. Прикладные нити мультиплексируются в меньший набор легковесных процессов.

  3. Связанные и несвязанные нити смешиваются в одном процессе.

Нитевая библиотека содержит алгоритм планирования, по которому выби­рается очередная нить для выполнения. Он обрабатывает приоритеты и со­стояния каждой нити, не зависящие от состояния или приоритета LWP, внутри которого находятся эти нити.

Нити используют средства синхронизации, предоставляемые библиотекой, которые похожи на аналогичные средства ядра (условные переменные, семафо­ры и т. д.). Система Solaris позволяет нитям разных процессов синхронизиро­ваться друг с другом при помощи переменных синхронизации, помещаемых в совместно используемый участок памяти.

Доставка и обработка сигналов

В системах UNIX доставка и обработка сигналов производится на уровне процесса. В многонитевых системах необходимо определять, какой из LWP процесса будет заниматься обработкой сигналов. При использовании при­кладных нитей имеется аналогичная проблема: после того, как ядро передаст сигнал в LWP, нитевая библиотека должна определить, в какую нить его на­править. Существует несколько вариантов решения данной проблемы:

♦ пересылка сигналов каждой нити;

- объявление одной из нитей процесса «главной», после чего все сигна­лы передаются только этой нити;

- отправка сигналов в любую произвольно выбранную нить;

  1. использование эвристических методов для определения, какой из ни­тей необходимо отправить данный сигнал;

  2. создание новой нити для обработки каждого сигнала.

Программный интерфейс

Интерфейс, обеспечиваемый пакетами функций для работы с нитями, должен обладать несколькими важными средствами. Он должен поддерживать большой набор различных операций над нитями, таких как:

  1. создание и уничтожение нитей;

  2. перевод нитей в режим ожидания и их восстановление в работоспособ­ное состояние;

  3. назначение приоритетов для отдельных нитей;

  4. планирование выполнения нитей и переключение контекста;

  5. синхронизацию действий при помощи таких средств, как семафоры или взаимные исключения;

  6. обмен сообщениями между нитями.

Пакеты функций для работы с нитями должны по возможности миними­зировать участие ядра, так как переключение между режимом задачи и ре­жимом ядра может быть весьма затратной процедурой.

Соседние файлы в папке материалы к собеседованию