5. Поддержка многоядерных процессоров


Введение

Два или более процессоров в компьютере могут значительно увеличить производительность.

Многопроцессорные системы могут быть:

Дискретные или традиционные

Система имеет раздельные физические процессоры, соединенные по шине на уровне плат.

Многоядерные

Микросхема содержит один физический процессор с множеством процессоров, соединенных шиной на уровне микросхемы.

Многоядерные процессоры обеспечивают большую вычислительную мощность за счет параллелизма, большую плотность системы и работают на меньших частотах, чем одноядерные процессоры. Многоядерные процессоры также меньше выделяют тепла, меньше расходуют энергию и занимают меньшую площадь (таким образом, уменьшая стоимость системы).

Многопроцессорность включает несколько режимов работы: Каждая ОС или экземпляр одной ОС работает на отдельном процессоре. Один экземпляр ОС управляет всеми процессорами одновременно и приложения могут работать на любом из них. Один экземпляр ОС управляет всеми процессорами, но каждое приложение привязано к определенному процессору.

Примечание. Чтобы определить, сколько процессоров имеется в системе, следует смотреть значение num_cpu в системной странице. Дополнительную информацию см. в «Structure of the system page» в главе «Customizing Image Startup Programs» электронного документа «QNX Neutrino Realtime Operation System. Building Embedded Systems».

Асиметричная многопроцессорность (AMP)

Асимметричная многопроцессорность предоставляет среду для выполнения задач подобно обычным однопроцессорным системам. Она предлагает относительно простой путь для портирования устаревшего кода и предоставляет прямой механизм для управления использования процессоров. В большинстве случаев это позволяет работать со стандартными средствами отладки.

AMP может быть:

гомогенной — все процессоры выполняют один тип и версию ОС

гетерогенный — все процессоры выполняют либо различные ОС, либо разные версии одной ОС

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

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

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

В AMP системах процессы всегда исполняются на одном и том же процессоре, даже если другой процессор свободен (выполняется idle). Как результат, один из процессоров может быть не нагружен или перегружен. Чтобы решить эту проблему, система может позволить приложениям динамически мигрировать с одного процессора на другой, однако, такое поведение может вызвать усложнение ведения контрольный точек с информацией о состоянии или усложнить сервисное прерывание, которое приводит к прекращению исполнения приложения на одном процессоре и его запуске на другом. Такие миграции сложны, или даже невозможны, когда процессоры выполняют различные ОС.
Симметричная многопроцессорность
Распределение ресурсов при проектировании многоядерной системы может оказаться сложной задачей, особенно, если различные программные компоненты не знают, как другие компоненты используют эти ресурсы.

Симметричная многопроцессорность (SMP) решает эту проблему, позволяя исполнять одну копию Neutrino на всех процессорах системы. ОС взаимодействует со всеми элементами системы, что позволяет распределять ресурсы между несколькими процессорами почти или вообще без вмешательства со стороны разработчика. Кроме того, Neutrino предоставляет встроенный механизм стандартизованных примитивов, таких как: pthread_mutex_lock(), pthread_mutex_unlock(), pthread_spin_lock(), and pthread_spin_unlock(), которые позволяют нескольким приложениям просто и безопасно разделять ресурсы.

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

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

Neutrino позволяет потокам приложения исполняться параллельно на любом процессоре, делая доступной приложениям в любое время всю имеющуюся вычислительную мощность многоядерной системы. Вытеснение и возможности приоритетов потоков позволяют разработчику быть уверенным, что процессорное время будет передано тому приложению, которое нуждается в нем больше всего.
Микроядро Neutrino в приближении
Симметричная многопроцессорная обработка (Symmetrical Multi-Processing, SMP) обычно ассоциируется с новейшими операционными системами (например, UNIX и NT), установленными на высокопроизводительные серверы. Такие крупные, монолитные системы, как правило, имеют весьма сложную архитектуру и являются результатом множества человеко-часов, затраченных на разработку. Поскольку ядро с такой большой архитектурой несет в себе практически все службы операционной системы, для обеспечения симметричной многопроцессорности в нем требуется произвести огромные изменения, которые обычно влекут за собой необходимость внести в код большое количество модификаций и использовать специальные блокировки активного ожидания (spinlocks).

ОС QNX Neutrino, напротив, содержит в себе компактное микроядро, которое окружено процессами, действующими в качестве администраторов ресурсов и обеспечивающими такие службы, как файловые системы, символьный ввод/вывод, сетевое взаимодействие. За счет модификации микроядра можно обеспечить SMP-функции для всех служб ОС без изменения программного кода. Если процессы, реализующие эти службы, являются многопоточными, все эти потоки можно распределить между несколькими процессорами. Более того, даже однопоточный серверный процесс может быть более эффективно реализован благодаря симметричной многопроцессорности, так как этот поток можно запланировать на исполнение имеющимися процессорами вместе с другими серверными и клиентскими процессами.

Действительно, в соответствии с описанным ранее подходом, для реализации возможностей симметричной многопроцессорности в ядре/администраторе процессов ОС QNX Neutrino используется всего лишь несколько килобайт программного кода. Версии администратора процессов с поддержкой симметричной многопроцессорности существуют для следующих основных семейств процессоров: Версия для платформы x86 может также использоваться и для любой другой системы, которая соответствует спецификации Intel MultiProcessor Specification (MP Spec) и содержит до восьми процессоров Pentium (или более производительных, чем Pentium). Кроме того, ОС QNX Neutrino поддерживает новую технологию многопоточной обработки фирмы Intel (Hyper-Threading Technology), которая применяется в процессорах P4 и Xeon.

Администратор procnto-smp, конечно, работает и на однопроцессорных системах, не имеющих возможностей параллельных вычислений. Таким образом, если двухпроцессорная материнская плата Pentium во многих отношениях аналогична однопроцессорной, она становится весьма выгодным решением, поскольку такую материнскую плату можно легко масштабировать посредством простого добавления еще одного процессора. Благодаря тому, что реализация симметричной многопроцессорности достигается в ОС QNX Neutrino всего лишь несколькими дополнительными килобайтами, она может легко применяться во встраиваемых системах даже с ограниченными вычислительными ресурсами, а не только на высокопроизводительных серверах.

Версии SMP-ядра для семейств микропроцессоров PowerPC и MIPS обеспечивают полную поддержку многопроцессорности на соответствующем оборудовании (в том числе синхронизацию кеша, межпроцессорные прерывания и т. д.). Версия SMP-ядра для PowerPC поддерживает любые системы с процессорами серии 7хх или 74хх (например, на таких отладочных платформах, как Motorola MVP или Marvell EV-64260-2XMPC7450 SMP Development System). Версия SMP-ядра для MIPS поддерживает такие системы, как процессор с двойным ядром Broadcom BCM1250.
Загрузка многопроцессорной системы на основе архитектуры x86
Микроядро ОС QNX Neutrino содержит очень небольшой объем программного кода, связанного с аппаратной стороной системы. Программный код, который задает возможности системы, есть в программе, активизируемой при запуске системы и предназначенной для системной инициализации, определения доступных ресурсов памяти и т. д. Собранная информация помещается в таблицу памяти, используемую (только для чтения) микроядром и всеми процессами.

Программа startup-bios предназначена для работы на системах, совместимых со спецификацией Intel MP Spec (версии 1.4 или выше). Данная программа запуска выполняет следующие функции:
После сброса системы код перезапуска выполняет только один процессор. Этот процессор называется загрузочным процессором (Boot Processor, BP). Для каждого обнаруженного дополнительного процессора загрузочный процессор, на котором выполняется запускающая программа startup-bios, будет выполнять следующие действия:
Загрузка многопроцессорной системы на основе архитектуры PowerPC или MIPS
В многопроцессорных системах на основе архитектуры PowerPC или MIPS последовательность загрузочных действий аналогична той, которая применяется в архитектуре x86, но отличие состоит в том, что в них используется специальная загрузочная программа (например, startup-mvp или startup-bcm1250). В частности, программа начальной загрузки в архитектуре PowerPC предназначена для выполнения следующих функций: Для каждого обнаруженного дополнительного процессора запускающая программа будет выполнять следующие действия:
Как работает микроядро с симметричной многопроцессорностью
После того как дополнительные процессоры освобождены и запущены, все процессоры считаются равноправными с точки зрения планирования потоков.
Планирование
Алгоритм планирования применяется в соответствии с теми же правилами, которые действуют в однопроцессорных системах. Это значит, что поток с наивысшим приоритетом будет выполняться на свободном процессоре. Если возникает другой поток, готовый к выполнению с наивысшим приоритетом, он будет передан соответствующему процессору. Если в качестве целевого указывается более одного процессора, тогда микроядро попытается отправить поток на тот процессор, на котором он выполнялся в последний раз. Такой механизм привязки (affinity) используется для того, чтобы уменьшить миграцию потоков между процессами, которая снижает производительность работы кешей.

В SMP-системе планировщик может самостоятельно управлять планированием низкоприоритетных потоков для того, чтобы оптимизировать использование кешей и минимизировать миграцию потоков между процессами. В любом случае, те правила планирования в реальном времени, которые применяются на однопроцессорных системах, в полной мере применимы и в многопроцессорных системах.
Блокировка ядра
В однопроцессорной системе в каждый момент времени в микроядре может выполняться только один поток. Большинство операций, выполняемых ядром, имеют очень небольшую продолжительность (как правило, не более нескольких микросекунд на процессоре класса Pentium). Микроядро в ОС QNX Neutrino разработано таким образом, чтобы обеспечить возможность его полного вытеснения и перезапуска для тех операций, которые требуют большего времени. Такая архитектура делает микроядро очень компактным и быстродействующим и не требует применения большого количества детально разработанных блокировок. Интересно отметить, что использование множества блокировок в основном коде ядра заметно снижает его быстродействие, ведь каждая блокировка, как правило, требует обращения к процессорной шине, а это может приводить к останову процессора.

В SMP-системах ОС QNX Neutrino отвечает той же философии: в вытесняемом и перезапускаемом ядре должен находиться только один поток. Микроядро доступно для любого процессора, но только один из них может получить доступ к нему в каждый момент времени.

В большинстве систем время, расходуемое на выполнение кода микроядра, составляет только небольшую долю от общей вычислительной нагрузки на процессор. Поэтому возникновение конфликтов становится, скорее, исключением, чем правилом. Это в особой степени относится к микроядру, в котором традиционные службы операционной системы (например, файловые системы), представляют собой отдельные процессы и не являются частью самого ядра.
Межпроцессорные прерывания
Процессоры взаимодействуют между собой посредством межпроцессорных прерываний (Inter-Processor Interrupt, IPI). Межпроцессорные прерывания позволяют эффективно осуществлять планирование и управление потоками на множестве процессоров. Например, межпроцессорное прерывание часто требуется в ситуациях, когда:
Критические секции программного кода
Для управления доступом к разделяемым структурам данных потоки и процессы используют такие стандартные POSIX-примитивы, как мутексы, условные переменные и семафоры. Все эти примитивы одинаковым образом работают как на однопроцессорных, так и многопроцессорных системах.

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

InterruptDisable()

// критическая секция кода

InterruptEnable()

или:

InterruptMask(intr)

// критическая секция кода

InterruptUnmask(intr)


Однако такой код не будет работать в SMP-системе, так как поток может выполняться на одном процессоре, а обработчик прерываний в это время может выполняться на другом процессоре.

Одним из решений может стать явная привязка потока к определенному процессору (см. “Многопроцессорность с привязкой (BMP),” далее в этой главе).

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

InterruptLock(intrspin_t* spinlock)


Данный вызов является попыткой получить блокировку активного ожидания (spinlock) — переменную, которая совместно используется обработчиком прерываний и потоком. Программный код будет выполняться в очень коротком цикле до тех пор, пока блокировка не будет получена. После выполнения запрета прерываний код получит блокировку (если она была получена потоком). Блокировка должна быть снята как можно быстрее (как правило, в течение нескольких строк кода Си, не содержащего циклов).

InterruptUnlock(intrspin_t* spinlock)


Данный вызов снимает блокировку и вновь разрешает прерывания.


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


Дополнительную информацию можно найти в руководстве Multicore Processing User’s Guide
Многопроцессорность с привязкой (BMP)
Многопроцессорность с привязкой обеспечивает контроль планирования асимметричной многопроцессорной модели, при сохранении аппаратных абстракций и модели управления симметричной многопроцессорности. BMP подобен SMP, за исключением того, что позволяет определить на каком процессоре определенный поток может быть исполнен. SMP и BMP можно использовать в одной системе, позволяя одним потокам мигрировать с одного процессора на другой, в то время как другие потоки будут привязаны к одному или нескольким процессорам.

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

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

В сравнении с полной свободой SMP модели, данный подход имеет следующие преимущества:

Маска запуска для нового потока или процесса может быть определена следующим образом:

Или:


Маска запуска существующего потока или процесса может быть установлена следующим образом:

Или:


Дополнительную информацию можно найти в электронном документе «QNX Neutrino Multicore Processing. User’s Guide».
Приемлемая стратегия миграции
Как промежуточный вариант между AMP и SMP, BMP предлагает приемлемую стратегию миграции, если требуется двигаться по пути к полной модели SMP, но есть опасения, что существующий код может работать некорректно в конкурентной модели исполнения.

Разработчик может портировать старый код в многоядерную систему и прикрепить полученное приложение к определенному процессору, чтобы убедиться в корректности его работы. Осмысленно назначая приложения (возможно и однопоточные) определенным процессорам, разработчик системы может определить потенциальные проблемы, на уровне приложений и потоков, связанные с параллельным выполнением. Устранение этих проблем позволит приложению получить все преимущества параллельного исполнения, таким образом обеспечить максимальный прирост производительности, предоставляемый многопроцессорной системой.
Выбор между AMP, SMP и BMP
Выбор между AMP, SMP и BMP зависит от задач, которые требуется решить:

AMP хорошо подходит для старых приложений, но ограничено в масштабируемости двумя процессорами.

SMP предлагает прозрачное управление ресурсами, но приложения, которые не были разработаны для параллельного исполнения, могут работать некорректно.

BMP предлагает множество из тех преимуществ, которыми обладает SMP, и гарантирует, что приложения, разработанные для однопроцессорных систем, будут работать корректно, что существенно упрощает перенос старого программного обеспечения.

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

Особенности

SMP

BMP

AMP

Прозрачное разделение ресурсов

да

да

-

Масштабируемость (более 2 CPU)

да

да

С ограничениями

Работа старых приложений

В большинстве случаев

да

да

Одновременная работа с несколькими ОС (например,

Neutrino и Linux)

-

-

да

Назначение функций процессорам

-

да

да

Обмен сообщениями между ядрами

Быстро

(примитивы OS)

Быстро

(примитивы OS)

Медленно

(приложение)

Синхронизация потоков между CPU

да

да

-

Балансировка загрузки

да

да

-

Отладка и оптимизация системы

да

да

-


2  APIC — Advanced Programmable Interrupt Controller (Улучшенный программируемый контроллер прерываний) — Контроллер прерываний, позволяющий использовать 24 аппаратных прерывания вместо 16. Ограничение в 16 аппаратных прерываний, не менявшееся с 1982 года, сдерживало установку в персональный компьютер дополнительных устройств. В конце 2001 года появились первые материнские платы с APIC. (www.svcd.ru) — Прим. перев.