QNX/UNIX: Анатомия параллелизма
Шрифт:
• POSIX (BSD) API — это уровень API, регламентируемый постоянно расширяющейся системой стандартов группы POSIX, которым должны следовать все ОС, претендующие на принадлежность к семейству UNIX.
• System V API (POSIX) — это та часть API, которая заимствует модели, принятые в UNIX-ax, относящихся к ветви развития System V, а не к ветви BSD.
Native QNX API
Именно этот слой является базовым слоем, реализующим функциональность самой системы QNX. Два последующих слоя в значительной мере являются лишь «обертками», которые ретранслируются в вызовы native QNX API после выполнения реструктуризации или перегруппировки аргументов вызова в соответствии с синтаксисом, требуемым этим вызовом.
Совершенно естественно, что прикладное программное приложение может быть полностью прописано в этом API (как, впрочем, и в каждом другом из описываемых ниже), но это не лучший
Тем не менее нужно сразу отметить, что многие возможности и модели (например, реакция на сигналы в потоках, тонкое управление поведением мьютексов и другие моменты) не могут быть реализованы в рамках POSIX-модели и выражаются только в native API QNX.
POSIX (BSD) API
Эта часть API наиболее полно соответствует API ОС UNIX, относящихся к ветви BSD (BSD, FreeBSD, NetBSD и другие). [5] Ее наименование можно было бы сузить до «BSD API», так как описанный далее набор API System V также регламентируется POSIX, но мы будем использовать именно термин «POSIX API», следуя терминологии фундаментальной книги У. Стивенса [2]. Эквивалентом названного выше для native API
5
На сегодняшний день практически ни одна из ОС UNIX уже не может быть отнесена чисто к System V или BSD, во многом исходя именно из требования совместимости с POSIX, который требует одновременного наличия и того и другого API (хотя в каждом случае комплиментарный набор API реализуется как «обертка» к базовому). Одними из первых (к 1997–1998 гг.) ОС, поддерживающих оба набора API, стали Sun Solaris 2.6 и Digital Unix 4.0B [3].
Именно на API этого слоя и будет строиться последующее изложение и приводимые примеры кода (параллельно с вызовами этого API мы будем для справки кое-где указывать имена комплиментарных им вызовов native API), за исключением случаев использования тех возможностей QNX, которые не имеют эквивалентов в POSIX API. Как раз все, что будет выражено в этом API далее по тексту, может быть перенесено на все UNIX-подобные операционные системы, о чем мы и говорили выше.
Самый ранний стандарт POSIX известен как IEEE 1003.1–1988 и, как следует из его названия, относится к 1988 году (если точнее, то ему предшествовал рабочий вариант под названием IEEEIX 1986 года, когда термин POSIX еще не был «придуман»). Более поздняя редакция его развития, IEEE 1003.1–1996, наиболее широко известна как «стандарт POSIX», иногда называемый POSIX.1. Набор стандартов POSIX находится в постоянном развитии и расширении и к настоящему времени включает в себя набор более чем из 30 автономных стандартов.
Для целей операционных систем реального времени возникла потребность определить отдельные механизмы особыми стандартами, на семь из которых ссылаются наиболее часто: 1003.1a, 1003.1b, 1003.1c, 1003.1d, 1003.1j, 1003.21, 1003.2h. Например:
1003.1a (OS Definition) — определяет базовые интерфейсы ОС;
1003.1b (Realtime Extensions) — описывает расширения реального времени, такие как модель сигналов реального времени, диспетчеризация по приоритетам, таймеры, синхронный и асинхронный ввод-вывод, IPC-механизмы (семафоры, разделяемая память, сообщения);
1003.1c (Threads) — определяет функции поддержки потоков, такие как управление потоками, атрибуты потоков, примитивы синхронизации (мьютексы, условные переменные, барьеры и др., но не семафоры), диспетчеризация.
System V API
Этот набор API является базовым для второй ветви [6] UNIX — System V (AT&T Unix System V). Как и оба предыдущих, этот набор API самодостаточен для реализации практически всех возможностей ОС, но использует для этого совершенно другие модели, например сетевую абстракцию TLI вместо сокетов BSD. Для области рассматриваемых нами механизмов - потоков, процессов, синхронизирующих примитивов и др. — в POSIX API и System V API почти всегда существуют функциональные аналоги, отличающиеся при этом как синтаксически, так и семантически. Например, в POSIX API семафор представлен типом
6
При общей истории UNIX, начинающейся с 1971 г. [7], две ветви API — BSD и System V — в их современном виде сформировались достаточно поздно: BSD к 1983 г., a System V к 1987 г. [3, 7]. Но многие IPC-механизмы System V (например, семафоры) сформировались по времени заметно раньше своих аналогов из BSD. Как отмечается в [3]: «Информация об истории разработки и развитии функций System V IPC не слишком легко доступна <…> очереди сообщений, семафоры и разделяемая память этого типа были разработаны в конце 70х в одном из филиалов Bell Laboratories в городе Колумбус… Эта версия называлась Columbus Unix, или СВ Unix».
В технической документации присутствие System V API в QNX не упоминается ни одним словом, но он, как того и требует POSIX, действительно предоставляется и в виде библиотек, и в виде необходимых файлов определений (заголовочных файлов). Просто его заголовочные файлы, определяющие структуры данных и синтаксис вызовов, находятся в других относительно POSIX-интерфейсов местах. Так, например, описание семафоров POSIX API (тип
С позиции программиста System V API присутствует в QNX главным образом для переносимости программных проектов, ранее созданных с использованием этого API, например первоначально созданных для других ОС UNIX (Sun Solaris, HP UNIX и др.). В данной книге это семейство API рассматриваться не будет.
2. Процессы и потоки
При внимательном чтении технической документации [8] и литературы по ОС QNX [1] отчетливо бросается в глаза, что тонкие детали создания и функционирования процессов и потоков описаны крайне поверхностно и на весьма некачественном уровне. Возможно, это связано с тем, что общие POSIX-механизмы уже изучены и многократно описаны на образцах кода в общей литературе по UNIX. Однако большинство литературных источников написано в «допотоковую» эпоху, когда основной исполняемой единицей в системе являлся процесс.
Детальное рассмотрение особенностей именно QNX [7] (версии 6.X после приведения ее в соответствие с POSIX, в отличие от предыдущей 4.25) лишний раз подчеркивает, что:
• Процесс является только «мертвой» статической оболочкой, хранящей учетные данные и обеспечивающей окружение динамического исполнения… Чего? Конечно же, потока, даже если это единственный (главный) исполняемый поток приложения (процесса), как это принято в терминологии, не имеющий отношения к потоковым понятиям.
7
[4]: глава Д. Алексеева «Получение системной информации».
• Любые взаимодействия, синхронизация, диспетчеризация и другие механизмы имеют смысл только применительно к потокам, даже если это потоки, локализованные в рамках различных процессов. Вот здесь и возникает термин, ставший уже стереотипным: «IPC — средства взаимодействия процессов». Для однопотоковых приложений этот терминологический нюанс не вносит ровно никакого различия, но при переходе к многопотоковым приложениям мы должны рассуждать в терминах именно взаимодействующих потоков, локализованных внутри процессов (одного или различных).
• В системах с аппаратной трансляцией адресов памяти (MMU — Memory Management Unit) процесс создает для своих потоков дополнительные «границы существования» — защищенное адресное пространство. Большинство сложностей, описываемых в литературе в связи с использованием IPC, обусловлено необходимостью взаимодействующих потоков преодолевать адресные барьеры, устанавливаемые процессами для каждого из них. (Что касается MMU, то выданной книге предполагается исключительно x86-архитектура, хотя количество аппаратных платформ, на которых работает ОС QNX, на сегодняшний день уже перевалило за десяток.)