Компактные операционные системы реального времени


PDF версия

В статье рассмотрена архитектура нетребовательной к ресурсам операционной системы реального времени (ОСРВ) для встраиваемых систем на базе загружаемых прикладных модулей, которая позволяет увеличить ее эффективность и гибкость. Описаны методы защиты системы от появления случайных отказов. Статья представляет собой перевод [1].

Во многих приложениях для эффективного взаимодействия с пользователем часто требуется отклик в режиме реального времени. Подобные системы работают на базе процессоров с низким энергопотреблением и часто выполняют все задачи по обработке данных, используя сравнительно небольшой объем памяти. Такое сочетание системных требований приводит, как правило, к решению использовать ОСРВ.
Существуют ОСРВ разных размеров и разновидностей: крупные, наподобие VxWorks от Wind River и суперкомпактные, типа ThreadX от Express Logiс. Устойчивые крупные ОСРВ обеспечивают множество функций, заимствованных из настольных систем и обычно недоступных для компактных ОСРВ, поскольку эти функции обрабатывают большой объем кода, требующий значительной памяти, и вызывают замедление отклика системы в режиме реального времени. В отличие от таких ОС компактные ОСРВ обычно работают как библиотеки сервисов, вызываемых приложением с помощью прямых обращений к функциям. В основе этих сервисов ОСРВ находится инфраструктура средств распределения времени и коммуникаций, которые поддерживают эти функции (см. рис. 1).
В большинстве нетребовательных к ресурсам компактных ОСРВ применена архитектура, в которой прикладной код напрямую связан с используемыми сервисами ОСРВ, формируя единый исполняемый образ (см. рис. 2). Приложение явным образом указывает на сервис, который ему нужен, используя вызовы функции с помощью интерфейса прикладного программирования (API), установленного в ОСРВ. Эти сервисные функции связаны с приложением из библиотеки ОСРВ. В результате формируется единый исполняемый образ, обычно в формате .elf. Для дальнейшей обработки этот образ загружается в целевой участок памяти и исполняется, или в условиях производства записывается в ПЗУ и исполняется при включении питания устройства.

 

Рис. 1. Компактная ОСРВ обычно работает как библиотека сервисов, вызываемых приложением с помощью прямых обращений к функциям

Рис. 2. В большинстве компактных ОСРВ применена архитектура, в которой прикладной код напрямую связан с используемыми сервисами ОСРВ, формируя единый исполняемый образ

Такой монолитный подход эффективен как по времени, так и по занимаемому пространству, но недостаточно гибок. Любые изменения в приложении или ОСРВ требуют перекомпоновки и новой загрузки/записи всего образа. Во время разработки это обычная процедура, однако после производства, в полевых условиях, это может создавать определенные неудобства.
В отличие от этого ОС для настольных систем, такие как Windows и Linux, а также более крупные ОСРВ, например, VxWorks и QNX, имеют двухсекционную архитектуру «ОС/Приложение». В такой архитектуре имеется резидентное ядро, содержащее все сервисы ОС, доступные для приложений или те, которые нужны другим сервисам внутри ядра, связанным с исполнимым модулем.
Этот исполнимый модуль ядра осуществляет начальную загрузку системы и работает непрерывно, обеспечивая базу для приложений, которые динамически загружаются и выполняются. Обычно виртуальная память обеспечивает вызов страниц по требованию в накопитель и извлекает их из накопителя настольной системы или осуществляет многопользовательское разделение во встраиваемых системах. Такой подход используется в мобильных устройствах, например, в iPhone или iPad от Apple, где новое приложение может быть загружено по беспроводной сети. В таких устройствах ОС обрабатывает пользовательский интерфейс, что позволяет сделать выбор любого загруженного приложения.
Выбранное приложение затем запускается и работает вместе с ОС на центральном процессоре. Подобным образом крупные системы на базе ОСРВ отделяют приложения от ядра ОСРВ, обычно в определенном пространстве в пределах виртуальной памяти.
Полезной особенностью крупных ОСРВ, которые используются совместно с настольными системами, является способность динамически загружать приложения в работающую систему. В таких разделяемых архитектурах ядро работает как модуль, а приложения исполняются независимо, но пользуются сервисами ОС для доступа и использования аппаратных ресурсов. Даже во встраиваемых системах встречается возможность такой загрузки, а также динамического добавления или изменения приложений, например, когда крупная ОСРВ работает в телекоммуникационной инфраструктуре и оборонных/аэрокосмических приложениях. Эта особенность обеспечивает высокую степень модульности и возможность обновления систем в полевых условиях.
Эти приложения не являются монолитными и не связаны с ОСРВ, поэтому доступ к сервисам ОСРВ осуществляется с помощью механизма системного прерывания при возникновении непредусмотренной ситуации (треппинга). Это программное прерывание, возникающее в результате действия одного из нескольких механизмов в зависимости от архитектуры системы (см. рис. 3). Оно используется обычно для захвата ошибок до их распространения по системе или для остановки системы, если невозможно выполнить запрашиваемую операцию. Деление на ноль или загрузка фрагментированных данных представляют собой примеры недопустимых операций, которые могут активировать обработчик прерываний, как в случае внешнего прерывания. В других случаях преднамеренное системное прерывание может быть инициировано с помощью команды программного прерывания (SWI).

 

Рис. 3. Системное прерывание (треппинг)

Когда для ОС необходимо обработать запрос приложения на предоставление сервиса, может быть использован один из механизмов намеренного вызова системного прерывания. Например, когда приложение хочет использовать сервис, который находится в ядре и его нельзя вызвать непосредственно из приложения, то приложение может вызвать системное прерывание.
Обработчик системного прерывания затем проверяет регистр (в который процессор обычно непосредственно перед генерированием системного прерывания загружает поддающееся идентификации значение) для определения того, какое событие вызвало это системное прерывание. Как только обработчик замечает вызов сервиса, он проверяет другие регистры (загруженные запрашивающим модулем) и определяет, какой сервис был востребован. Затем он собирает параметры для этого сервиса из регистров, в которые приложения предварительно загрузили данные. Наконец, обработчик системного прерывания вызывает сервис (который является локальным для своего исполнимого модуля) как связанную функцию с определенными параметрами.
Весь этот процесс требует обслуживания прерывания, несложной обработки, вызова функции и затем выполнения тех же самых действий в обратном порядке. Для настольных или крупных ОСРВ эта нагрузка незначительна (по сравнению с обработкой кода, который непрерывно выполняется, например, для поддержания в порядке служебных функций системы).
Непроизводительные издержки также несущественны из-за отсутствия жестко заданных сроков выполнения заданий. Крупные ОС напоминают медлительный тягач с прицепом. Это не вызывает проблем, т.к. большую часть времени тягач курсирует по автостраде, в отличие от легкового автомобиля, который часто должен перемещаться быстро и быть способным резко маневрировать.
В крупной системе, такой как Linux, Windows или большая ОСРВ, непроизводительные издержки для вызова функции незначительны, поскольку здесь не требуется быстрой реакции. Однако в компактной ОСРВ, используемой в приложениях в режиме жесткого реального времени, необходимо учитывать реакцию системы, в которой интерфейс системного прерывания представляет собой нежелательный элемент, непроизводительно расходующий системные ресурсы, в отличие от эффективного метода прямого доступа.
Как сделать компактную ОСРВ более динамичной ?
В компактной ОСРВ для повышения ее эффективности используется метод получения динамически загружаемых приложений с помощью вызовов сервисов ОСРВ, включаемых в определенный, обособленно подключаемый участок кода. Для этого нужен интерфейс, который эффективно доставляет сервисы приложениям, в то же время обеспечивая преимущества динамической загрузки. Чтобы реализовать такую архитектуру в компактной ОСРВ, нужна структура прикладного модуля, показанная на рисунке 4. Прикладные модули представляют собой группу из одного или нескольких потоков приложений, не связанных с ядром, а построенных в виде отдельных исполнимых модулей, которые при необходимости загружаются в целевой объем памяти.

 

Рис. 4. Структура прикладного модуля, необходимая для динамической загрузки приложений в компактной ОСРВ

Модули используют сервисы ядра через интерфейс с менеджером модулей — программного агента внутри образа ядра, который загружает и инициирует модуль, а также реализует все запросы сервисов ОСРВ со стороны модуля.
Потоки внутри модулей выполняют вызовы сервиса в точности, как они выполняли бы вызовы, если бы функция сервиса была напрямую связана с приложением. В модуле, однако, эти вызовы обрабатываются элементом интерфейса, который связан с менеджером модулей. В этом случае механизм системного прерывания исключается, что обеспечивает низкие непроизводительные издержки системы.
Несмотря на то, что имеется только одна копия менеджера модулей, нет ограничений по числу модулей, которые можно загрузить одновременно, и нет ограничений по числу потоков в каждом модуле. Таким образом ядро постоянно находится в определенном исполнимом модуле, непрерывно обслуживая его запросы.
Как показано на рисунке 5, для максимальной эффективности потоки приложения могут поочередно быть связаны с ядром и располагаться вместе с ним в заданной области памяти, как часть его исполняемого образа. Хотя такая опция избавляет от необходимости перегрузки модулей, содержащих эти потоки и обеспечивающих наиболее эффективный интерфейс, она увеличивает размер резидентного образа ядра, оставляя меньше доступного объема памяти для использования модулями, и пропадает возможность динамического изменения потоков приложения.
Загружаемые прикладные модули позволяют ОСРВ динамически загружать и запускать дополнительные потоки приложения помимо тех, которые связаны с ядром. Функциональность приложения улучшилась без увеличения объема требуемой памяти, обеспечивая при этом эффективный интерфейс вызова сервиса.

 

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

Этот метод обеспечивает также переконфигурирование по требованию и обновление развернутых систем. Технология загружаемого прикладного модуля идеально подходит для случаев, когда размер прикладного кода превышает объем доступной памяти (см. рис. 6), когда нужно добавлять новые модули после развертывания системы или когда требуется частичное обновление микропрограммного обеспечения.

 

Рис. 6. Метод загружаемого прикладного модуля идеально подходит к случаям, когда размер прикладного кода превышает объем доступной памяти, когда нужно добавлять новые модули после развертывания системы или когда требуется частичное обновление микропрограммного обеспечения

Другим преимуществом загрузки отдельных прикладных модулей является то, что каждый модуль может быть разработан своей группой программистов или одним программистом. Группа может тогда сконцентрироваться на одном аспекте функциональных свойств продукта, без необходимости изучения деталей.
Недостатком такого подхода является риск нарушения функционирования, когда модуль помещают в систему, и он должен взаимодействовать с ядром и другими модулями. Разработчики должны быть уверены, что внешние модули корректно работают с другими компонентами системы. Наряду с ошибками в программе, которые могут генерировать сбои или вызывать повреждение модуля, существуют дефекты, способные вызвать заражение другого модуля или самого ядра ОСРВ.
Несмотря на тщательное тестирование программ, существует риск случайного повреждения стека или памяти из-за неправильно рассчитанного указателя или границ массива, а также переполнения стека. Эти дефекты могут быть катастрофическими и трудно обнаруживаемыми, особенно если не все разработчики группы осведомлены о неисправном модуле. Проблема еще больше усложняется, когда источник такого сбоя обнаруживается в коде, который находится в совершенно другом модуле. Поиск источника такого катастрофического отказа чрезвычайно сложен, поэтому подобных ошибок нужно избегать.
Чтобы защитить систему от случайного отказа, необходимо исключить доступ потоков модулей к области памяти вне области собственной памяти модуля и использовать сервисы ОСРВ, которые выполняют пересылку сообщений (см. рис. 7).

 

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

 

Данный вид защиты противодействует случайным отказам модулей и ядра из-за ошибочного ввода, передачи управления или ошибочного чтения. Используя системные блоки защиты и управления памятью (MMU или MPU), граничные регистры памяти могут быть установлены так, чтобы код каждого модуля не выходил за пределы своей собственной памяти.
Загружаемые прикладные модули существенно улучшают функциональные свойства приложений, нетребовательных к ресурсам, в которых нужна надежность и быстрота реакции компактной ОСРВ, а также обеспечивают более широкий набор возможностей, удобство в эксплуатации и модульный принцип организации системы. Кроме того, с помощью реализации системы защиты памяти любой желаемый уровень системы — от отдельного потока до их неограниченного числа — можно оградить от непреднамеренного доступа, исключая широко распространенную причину программного сбоя, которая трудно поддается диагностике.

Литература
1. John A. Carbone. Bring big system features to small RTOS devices with downloadable app modules//www.eetimes.com

Оставьте отзыв

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