лекции, учебные пособия / материалы к собеседованию / nit
.docОсновные типы нитей
Процесс представляет собой составную сущность, которую можно разделить на два основных компонента: набор нитей и набор ресурсов. Нить — это динамический объект, в процессе представленный отдельной точкой управления и выполняющий последовательность команд, отсчитываемую от этой точки. Ресурсы, включающие адресное пространство, открытые файлы, полномочия, квоты и т. д., используются всеми нитями процесса совместно. Каждая нить, кроме того, обладает собственными объектами, такими как указатель команд, стек или контекст регистров.
Существуют различные типы: нити ядра, легковесные процессы и прикладные нити.
Многонитевость в Solaris
Нити ядра не требуют связи с каким-либо прикладным процессом. Они создаются и уничтожаются ядром и внутри ядра по мере необходимости и отвечают за выполнение определенных функций. Такие нити используют совместно доступные области кода и глобальные данные ядра, но обладают собственным стеком в ядре. Они могут независимо назначаться на выполнение и используют стандартные механизмы синхронизации ядра, такие как sleep() или wakeup().
Нити ядра применяются для выполнения таких операций, как асинхронный ввод-вывод. Вместо поддержки каких-либо специальных механизмов ядро просто создает новую нить для обработки запросов для каждой такой операции. Запрос обрабатывается нитью синхронно, но для ядра представляется асинхронным событием. Нити ядра могут быть также использованы для обработки прерываний.
Нить ядра в системе Solaris — это основной легковесный объект, который может независимо планироваться и отправляться на выполнение одному из процессоров системы. Такой объект не нуждается в ассоциации с каким-либо процессом, он может быть создан, запущен и уничтожен ядром при помощи специальных функций. В результате ядру системы не нужно переотображать виртуальное адресное пространство при переключении нитей ядра. Структура данных нити ядра содержит следующую информацию:
-
сохраненная копия регистров ядра;
-
приоритет и информация, связанная с расписанием;
-
указатели на местонахождение нити в очереди планировщика или, если выполнение нити блокировано, в очереди ожидания ресурсов;
-
указатель на стек;
-
указатели на связанные с нитью структуры Iwp и рrос
Легковесный процесс (или LWP, lightweight process) — это прикладная нить, поддерживаемая ядром. LWP является абстракцией высокого уровня, основанной на нитях ядра. Каждый процесс может иметь один или более LWP, любой из которых поддерживается отдельной нитью ядра. Легковесные процессы планируются на выполнение независимо от процесса, но совместно разделяют адресное пространство и другие ресурсы процесса. Они обладают возможностью производить системные вызовы и блокироваться в ожидании окончания ввода-вывода или освобождения ресурсов. На многопроцессорных системах процесс в состоянии использовать преимущества настоящей параллельной работы, так как каждый LWP может выполняться на отдельном процессоре. Однако даже на однопроцессорных системах применение LWP дает существенные преимущества, поскольку при ожидании ресурсов или окончания ввода-вывода блокируется не весь процесс в целом, а только отдельные LWP.
Если доступ к каким-либо данным производится одновременно несколькими LWP, необходимо обеспечить некоторую синхронизацию доступа. Каждый вызов требует двух переключений режима: сначала из режима задачи в режим ядра и обратное переключение после завершения работы функции. При каждом переключении режима LWP пересекает границу защиты (protection boundary).
Легковесные процессы обеспечивают многонитевое выполнение внутри одного процесса. Планирование выполнения LWP происходит независимо, и такие процессы могут выполняться параллельно на многопроцессорных системах. Каждый LWP связан со своей собственной нитью ядра, такая связь не прерывается на протяжении всего жизненного цикла процесса.
Традиционные структуры ргос и user недостаточны для представления многонитевых процессов.
Вдобавок к структурам, описывающим процесс в ядре, появляется новая структура — Iwp, которая хранит информацию о каждой LWP-составляющей контекста процесса. Структура Iwp содержит следующую информацию:
-
сохраненные значения регистров прикладного уровня (когда LWP не выполняется);
-
аргументы системного вызова, результаты работы и код ошибки;
- информацию об обработке сигналов;
- данные об использовании ресурсов и данные профиля процесса;
- указатель на нить ядра;
-
указатель на структуру ргос.
Все LWP используют общий набор обработчиков сигналов. Однако каждый LWP может обладать собственной маской сигналов, решая самостоятельно, какие из полученных сигналов нужно игнорировать или блокировать. Легковесные процессы не имеют глобального пространства имен и вследствие этого невидимы для других процессов.
Прикладные нити реализованы в системе при помощи нитевой библиотеки. Они могут создаваться, уничтожаться и обрабатываться без участия ядра. Библиотека также предоставляет средства синхронизации и планирования нитей. Это позволяет процессу использовать большое количество нитей, не потребляя при этом ресурсы ядра и не загружая систему лишними вызовами.
По умолчанию библиотека создает для каждого процесса набор LWP и мультиплексирует в него все прикладные нити. Таким образом, процесс может обладать прикладными нитями двух типов: свободными (связанными с единственным LWP) нитями и несвязанными, разделяющими общий набор легковесных процессов.
Каждая прикладная нить должна поддерживать информацию следующего содержания:
-
Идентификатор нити (thread ID). Позволяет нитям взаимодействовать друг с другом в рамках процесса при помощи сигналов и прочих средств.
-
Сохраненное состояние регистров (saved register state). Содержит указатель команд и указатель стека.
-
Стек в приложении (user stack). Каждая нить обладает своим собственным стеком, размещаемым при помощи библиотеки. Ядро системы не знает о существовании подобных стеков.
-
Маска сигналов (signal mask). Каждая нить может обладать собственной маской сигналов. Когда приходит сигнал, библиотека доведет его до соответствующей нити, исходя из информации, содержащейся в масках всех нитей.
-
Приоритет (priority). Прикладная нить имеет приоритет внутри процесса, который используется планировщиком нитей.
-
Локальная область хранения нити (thread kernel storage).
Во многих современных системах ядро поддерживает многонитевые процессы через LWP. В таком случае библиотеки прикладных нитей могут быть реализованы различными способами:
-
Каждой нити назначается свой легковесный процесс.
-
Прикладные нити мультиплексируются в меньший набор легковесных процессов.
-
Связанные и несвязанные нити смешиваются в одном процессе.
Нитевая библиотека содержит алгоритм планирования, по которому выбирается очередная нить для выполнения. Он обрабатывает приоритеты и состояния каждой нити, не зависящие от состояния или приоритета LWP, внутри которого находятся эти нити.
Нити используют средства синхронизации, предоставляемые библиотекой, которые похожи на аналогичные средства ядра (условные переменные, семафоры и т. д.). Система Solaris позволяет нитям разных процессов синхронизироваться друг с другом при помощи переменных синхронизации, помещаемых в совместно используемый участок памяти.
Доставка и обработка сигналов
В системах UNIX доставка и обработка сигналов производится на уровне процесса. В многонитевых системах необходимо определять, какой из LWP процесса будет заниматься обработкой сигналов. При использовании прикладных нитей имеется аналогичная проблема: после того, как ядро передаст сигнал в LWP, нитевая библиотека должна определить, в какую нить его направить. Существует несколько вариантов решения данной проблемы:
♦ пересылка сигналов каждой нити;
- объявление одной из нитей процесса «главной», после чего все сигналы передаются только этой нити;
- отправка сигналов в любую произвольно выбранную нить;
-
использование эвристических методов для определения, какой из нитей необходимо отправить данный сигнал;
-
создание новой нити для обработки каждого сигнала.
Программный интерфейс
Интерфейс, обеспечиваемый пакетами функций для работы с нитями, должен обладать несколькими важными средствами. Он должен поддерживать большой набор различных операций над нитями, таких как:
-
создание и уничтожение нитей;
-
перевод нитей в режим ожидания и их восстановление в работоспособное состояние;
-
назначение приоритетов для отдельных нитей;
-
планирование выполнения нитей и переключение контекста;
-
синхронизацию действий при помощи таких средств, как семафоры или взаимные исключения;
-
обмен сообщениями между нитями.
Пакеты функций для работы с нитями должны по возможности минимизировать участие ядра, так как переключение между режимом задачи и режимом ядра может быть весьма затратной процедурой.