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 или выше). Данная программа запуска выполняет
следующие функции:
- определение
числа имеющихся процессоров;
- определение
адреса локального APIC-контроллера и APIC-контроллера
ввода/вывода;
- инициализация
каждого дополнительного процессора.
После сброса
системы код перезапуска выполняет только один процессор. Этот
процессор называется загрузочным процессором (Boot Processor, BP). Для
каждого обнаруженного дополнительного процессора загрузочный
процессор, на котором выполняется запускающая программа startup-bios, будет выполнять следующие
действия:
-
инициализировать процессор;
-
переключать его в защищенный 32-битный режим;
-
выделять для него собственную таблицу страниц;
-
переводить процессор в режим с запрещенными
прерываниями в ожидании, пока ядро не освободит его.
Загрузка многопроцессорной системы на основе
архитектуры PowerPC или MIPS
В
многопроцессорных системах на основе архитектуры PowerPC или
MIPS последовательность загрузочных действий аналогична той,
которая применяется в архитектуре x86, но отличие состоит в
том, что в них используется специальная загрузочная программа
(например, startup-mvp
или startup-bcm1250). В частности, программа
начальной загрузки в архитектуре PowerPC предназначена для
выполнения следующих функций:
-
определение числа имеющихся процессоров;
-
инициализация каждого дополнительного
процессора;
-
инициализация контроллеров прерываний (IRQ) и
межпроцессорных прерываний (IPI), системного контроллера
и т. д.
Для каждого
обнаруженного дополнительного процессора запускающая программа
будет выполнять следующие действия:
-
инициализировать процессор;
-
инициализировать блок управления памятью (блок
MMU);
-
инициализировать кеши;
-
переводить процессор в режим с запрещенными
прерываниями в ожидании, пока ядро не освободит его.
Как работает
микроядро с симметричной многопроцессорностью
После того как
дополнительные процессоры освобождены и запущены, все
процессоры считаются равноправными с точки зрения планирования
потоков.
Планирование
Алгоритм
планирования применяется в соответствии с теми же правилами,
которые действуют в однопроцессорных системах. Это значит, что
поток с наивысшим приоритетом будет выполняться на свободном
процессоре. Если возникает другой поток, готовый к выполнению
с наивысшим приоритетом, он будет передан соответствующему
процессору. Если в качестве целевого указывается более одного
процессора, тогда микроядро попытается отправить поток на тот
процессор, на котором он выполнялся в последний раз. Такой
механизм привязки (affinity) используется для того, чтобы
уменьшить миграцию потоков между процессами, которая снижает
производительность работы кешей.
В SMP-системе
планировщик может самостоятельно управлять планированием низкоприоритетных потоков для того, чтобы
оптимизировать использование кешей и минимизировать миграцию
потоков между процессами. В любом случае, те правила
планирования в реальном времени, которые применяются на
однопроцессорных системах, в полной мере применимы и в
многопроцессорных системах.
Блокировка ядра
В
однопроцессорной системе в каждый момент времени в микроядре
может выполняться только один поток. Большинство операций,
выполняемых ядром, имеют очень небольшую продолжительность
(как правило, не более нескольких микросекунд на процессоре
класса Pentium). Микроядро в ОС QNX Neutrino разработано таким
образом, чтобы обеспечить возможность его полного вытеснения и
перезапуска для тех операций, которые требуют большего
времени. Такая архитектура делает микроядро очень компактным и
быстродействующим и не требует применения большого количества
детально разработанных блокировок. Интересно отметить, что
использование множества блокировок в основном коде ядра
заметно снижает его быстродействие, ведь каждая блокировка,
как правило, требует обращения к процессорной шине, а это
может приводить к останову процессора.
В SMP-системах
ОС QNX Neutrino отвечает той же философии: в вытесняемом и
перезапускаемом ядре должен находиться только один поток.
Микроядро доступно для любого процессора, но только один из
них может получить доступ к нему в каждый момент времени.
В большинстве
систем время, расходуемое на выполнение кода микроядра,
составляет только небольшую долю от общей вычислительной
нагрузки на процессор. Поэтому возникновение конфликтов
становится, скорее, исключением, чем правилом. Это в особой
степени относится к микроядру, в котором традиционные службы
операционной системы (например, файловые системы),
представляют собой отдельные процессы и не являются частью
самого ядра.
Межпроцессорные
прерывания
Процессоры
взаимодействуют между собой посредством межпроцессорных
прерываний (Inter-Processor Interrupt, IPI). Межпроцессорные
прерывания позволяют эффективно осуществлять планирование и
управление потоками на множестве процессоров. Например,
межпроцессорное прерывание часто требуется в ситуациях, когда:
-
поток с более высоким приоритетом переходит в
состояние готовности;
-
поток, выполняемый на другом процессоре,
получает сигнал;
-
поток, выполняемый на другом процессоре,
завершается (canceled);
-
поток, выполняемый на другом процессоре,
уничтожается.
Критические
секции программного кода
Для управления
доступом к разделяемым структурам данных потоки и процессы
используют такие стандартные POSIX-примитивы, как мутексы,
условные переменные и семафоры. Все эти примитивы одинаковым
образом работают как на однопроцессорных, так и
многопроцессорных системах.
Во многих
системах реального времени часто возникает необходимость
обеспечить защиту доступа к разделяемым структурам данных
между обработчиком прерываний и потоком, владеющим этим
обработчиком. Традиционные POSIX-примитивы, используемые между
потоками, не могут применяться обработчиком прерываний.
Существует два решения этой проблемы.
-
Снять всю работу с обработчика прерываний и
перенести ее на уровень потока. Благодаря высокой
скорости планирования потоков в ОС QNX Neutrino, это
решение может быть очень эффективным.
-
В однопроцессорной системе под управлением ОС QNX
Neutrino обработчик прерываний может вытеснять потоки,
но потоки никогда не могут вытеснять обработчик
прерываний. Поток может защищать себя от вытеснения
обработчиком прерываний с помощью запрета или разрешения
прерываний на очень короткие периоды времени.
Защиту потока в
однопроцессорной системе можно реализовать с помощью
программного кода в следующей форме:
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 модели, данный подход имеет следующие
преимущества:
- Он
устраняет возможность перегрузки кэша, что может снизить
производительность SMP системы, позволяя приложениям,
разделяющим одни и те же данные, оставаться исполняемыми на
одном процессоре.
- Отладка
приложений проще, в сравнении с SMP, поскольку все потоки
приложения исполняются на одном процессоре.
- Он
помогает старым приложениям, которые используют не
оптимальные механизмы синхронизации доступа к разделяемым
данным исполняться параллельно, привязывая их к одному
процессору.
- В BMP,
приложение, привязанное к одному процессору, не сможет
использовать другой процессор, даже если он свободен
(выполняется idle). Однако, Neutrino позволяет динамически
менять назначенный процессор.
- QNX
Neutrino поддерживает концепцию жесткого назначения
процессоров через маску запуска (runmask).
- Каждый
бит, который установлен в маске запуска означает процессор,
на котором поток может быть запущен. По умолчанию, маска
запуска заполняется единицами, что означает разрешение
запускать поток на любом процессоре. Значение 0x01 позволяет
исполнять поток только на первом процессоре.
- По
умолчанию, дети процесса или потока не наследуют маску
запуска; существует отдельная маска наследования.
- Правильное
использование маски запуска позволяет разработчику системы
оптимизировать производительность системы (например,
назначая приложения, которые не отвечают требованиям
реального времени, специальному процессору). В общем случае,
в этом нет необходимости, потому что планировщик QNX
Neutrino вытеснит низкоприоритетный поток сразу, как только
высокоприоритетный поток будет готов к исполнению. Привязка
процессора может повлиять только на эффективность кэша,
поскольку миграция потоков будет запрещена.
Маска запуска
для нового потока или процесса может быть определена следующим
образом:
- установкой
поля маски запуска в структуре наследования и определением
флага SPAWN_EXPLICIT_CPU при вызове функции spawn()
Или:
- использованием
утилиты on с опциями -C и -R для запуска программы. Маске
наследования также будет присвоено переданное значение
маски запуска.
Маска запуска
существующего потока или процесса может быть установлена
следующим образом:
- использованием
вызова ядра ThreadCtl() с командой _NTO_TCTL_RUNMASK или
_NTO_TCTL_RUNMASK_GET_AND_SET_INHERIT.
Или:
- использованием
утилиты slay с опциями -C и -R. Если при вызове slay
указана опция -i, маске наследования также будет присвоено
переданное значение маски запуска.
Дополнительную
информацию можно найти в электронном документе «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) — Прим. перев.