За счет умелого использования программных модулей и установки связи между ОСРВ и приложением можно добиться качественной работы в режиме реального времени, даже если ресурсы устройства ограничены.
В большинстве встраиваемых систем бытового, медицинского и промышленного назначения для эффективного взаимодействия с пользователем требуется поддержка режима реального времени. Связь по мобильному телефону, обработка ультразвукового изображения или видеоконтроль на производственной линии — во всех этих и многих других приложениях обработка входных данных и вывод конечной информации должны осуществляться очень быстро. Обычно в таких системах используется маломощный процессор с небольшим объемом памяти, достаточным для выполнения всех операций. Учитывая ограниченность ресурсов, для управления прикладными задачами и потоками, а также для обработки прерываний и обеспечения взаимосвязи между несколькими потоками и их синхронизации рекомендуется использовать операционные системы реального времени (ОСРВ).
Существует огромное разнообразие ОСРВ — от больших, как Wind River VxWorks, до самых компактных, как Express Logic ThreadX. Надежные ОСРВ обеспечивают функционал, приближенный к возможностям настольного компьютера. Компактные ОСРВ не позволяют быстро выполнять сложные процедуры, поэтому их возможности ограничены. Компактные ОСРВ применяются в качестве библиотеки служб, которые вызываются напрямую соответствующей командой приложения. Инфраструктура ОСРВ и службы планирования и связи помогают упростить реализацию этих команд (см. рис. 1).
![]() |
Рис. 1. Вызов службы в компактной ОСРВ
|
В большинстве компактных ОСРВ код приложения непосредственно связан со службами ОСРВ, которые он использует. В итоге образуется единый исполнительный файл-образ с расширением .elf. Для вызова функции используются команды вызова, прописанные в программном интерфейсе операционной системы. Все служебные функции привязаны к приложению через библиотеки ОСРВ. При проектировании образ загружается в целевую память и запускается с нее. В готовом устройстве он записывается в ПЗУ и выполняется при включении устройства. Такой «монолитный» подход эффективен как по временным затратам, так и по занимаемой схемой площади, но ему не хватает гибкости. Для внесения изменений в приложение или ОСРВ требуется повторная компоновка программы и загрузка или запись всего образа в память. Это обычная процедура на стадии проектирования, однако для готового устройства она может создать некоторые ограничения.
Операционные системы для настольных ПК, такие как Windows и Linux, и более функциональные ОСРВ, такие как VxWorks и QNX, имеют сдвоенную архитектуру, разделенную на ОС и приложение. В них имеется резидентное ядро, содержащее все доступные для приложений службы ОС или сервисы, необходимые для других функций ядра и связанные в отдельный исполнительный файл.
Система загружается с ядра и работает непрерывно. Это своего рода основание для приложений, которые загружаются и выполняются динамично, т.е. во время работы. Как правило, подкачка страниц в память накопителей и обратно в настольных системах и разделение пользователей во встраиваемых системах обеспечивается файлом виртуальной памяти. Такой подход используется в мобильных устройствах, таких как iPhone Apple или iPad, где новые приложения можно скачать через беспроводную сеть. ОС работает непрерывно и обрабатывает пользовательский интерфейс, через который осуществляется выбор приложения. Выбранные приложения запускаются вместе с ОС на процессоре. Аналогично в системах на основе ОСРВ приложения отделены от ядра ОС и содержатся в виртуальной памяти.
Еще одной общей особенностью больших ОСРВ и настольных ОС является возможность динамической загрузки приложения во время работы системы. В архитектурах с выделением ядра процессор работает независимо от приложений. Для доступа к аппаратным ресурсам встраиваемой системы используются службы ОС. Большие ОСРВ используются в телекоммуникационной сфере, оборонной и аэрокосмической промышленности. Они обеспечивают высокую степень модульности системы и позволяют производить обновление без остановки работы.
Эти приложения не являются монолитными и не связываются с ОСРВ, поэтому доступ к службам ОСРВ происходит через внутренние прерывания. Внутреннее прерывание — это программное прерывание, которое генерируется при наступлении различных событий, в зависимости от архитектуры (см. рис. 2). Обычно они используются для обнаружения ошибок до того, как они распространятся, или для остановки системы в случае, если невозможно выполнить требуемую операцию. Примерами может служить деление на ноль или загрузка невыровненных данных. Внутреннее прерывание можно вызвать принудительно с помощью инструкции вызова программного прерывания swi.
![]() |
Рис. 2. Конфигурация с несколькими выходными делителями
|
Когда операционной системе требуется обработать запрос обслуживания, один из этих механизмов может быть использован для намеренной генерации внутреннего прерывания. Например, когда приложение пытается использовать службу, которая находится в ядре и не может быть вызвана непосредственно из приложения. В этом случае приложение выставляет прерывание. Непосредственно перед началом генерации внутреннего прерывания процессор записывает в специальный регистр значение, по которому обработчик внутренних прерываний определяет, какое событие вызвало прерывание. Если произошел вызов службы, обработчик обращается к другим регистрам , загружаемым запрашивающим модулем и определяет, какие службы следует вызвать. Затем он собирает параметры для этой службы из регистров, предварительно загруженных приложением, после чего производится вызов службы как связанной функции с заданными параметрами.
Таким образом, полная процедура сводится к обнаружению и обработке прерывания, вызову функции, после чего операции повторяются в обратном порядке. Для настольной ОС или большой ОСРВ эта нагрузка незначительна по сравнению с объемом кода, который она выполняет постоянно для поддержания работоспособности системы. Кроме того, вызов службы — это не срочная задача и не требует отклика в режиме реального времени. Тем не менее, для компактных ОСРВ, работающих в жестком реальном времени, любая лишняя инструкция может затормозить работу, поэтому применение внутренних прерываний крайне нежелательно, особенно учитывая высокую эффективность остального кода с прямым доступом.
Для небольших ОСРВ необходимо эффективно реализовать динамическую загрузку приложений с помощью вызовов служб ОСРВ. Требуется интерфейс вызова службы, который поддерживает загрузку во время работы. Для реализации такой архитектуры в компактных ОСРВ используется структура «модуль приложения». Программные модули представляют собой наборы потоков приложения, не связанных с ядром, а реализованных в виде отдельного исполняемого файла, который загружается в целевую память. Модули обращаются к службам ядра менеджера модулей — агента в образе ядра, который загружает и инициализирует модули, а также обрабатывает запросы служб ОСРВ.
Потоки внутри модулей осуществляют вызовы служб точно так, как они делали бы это, если были бы непосредственно связаны с приложением. Внутри модуля вызовы обрабатываются по протоколу взаимодействия с менеджером модулей. Таким образом, механизм внутренних прерываний не используется, и дополнительная нагрузка при вызове служб невелика.
Менеджер модулей может быть только один, а ограничений на количество модулей, которые могут быть загружены одновременно, и на количество потоков в одном модуле не установлено. Таким образом, ядро находится в отдельной структуре исполнения, работает непрерывно и обслуживает запросы модулей.
Как показано на рисунке 3, для достижения максимальной эффективности потоки приложения могут быть связаны с ядром и храниться в целевой памяти как часть его исполняемого образа. Этот вариант позволяет избежать необходимости перезагружать модули, содержащие эти потоки, и предоставляет наиболее эффективный интерфейс, однако при таком подходе увеличивается размер файла образа ядра и остается меньше памяти для использования модулями. Кроме того, отсутствует возможность динамической замены потоков. Загружаемые приложения-модули позволяют ОСРВ динамически запускать дополнительные потоки приложения, не связанные с ядром. Функциональность системы увеличивается без дополнительных схемотехнических приемов.
![]() |
Рис. 3. Связывание приложения с ядром
|
Этот метод позволяет также менять настройки и обновлять систему. Принцип загрузки модулей по требованию идеально подходит для случаев, когда размер кода приложения превышает объем доступной памяти (см. рис. 4), в случае, если требуется частичное обновление прошивки или когда новые модули могут быть добавлены после начала эксплуатации продукта.
Еще одно преимущество загрузки отдельных программных модулей заключается в том, что каждый модуль может разрабатываться отдельным специалистом. Это помогает более эффективно построить процесс проектирования.
![]() |
Рис. 4. Выборочная загрузка модулей
|
Недостатком такого подхода является повышенный риск неправильного функционирования при загрузке модуля в систему, когда он начинает взаимодействовать с ядром и другими модулями. По сравнению с программными ошибками, которые вызывают сбой в модуле, более опасны ошибки взаимодействия, в результате которых повреждаются другие модули или ядро. Несмотря на успешное прохождение программного тестирования, в реальном устройстве могут появиться ошибки в стеке или может произойти повреждение памяти из-за переполнения стека или неправильно рассчитанного предела массива. Эти ошибки могут быть катастрофическими, и их трудно выявить. Особенно, если только часть группы программистов участвовала в разработке проблемного модуля. Задача становится еще сложнее, если отказ произошел из-за кода, находящегося в другом модуле. Подобных ошибок лучше избегать.
Для защиты системы от перекрестных повреждений необходимо закрывать потокам прямой доступ к внешней памяти, расположенной за пределами собственного модуля. Обращение к внешним ячейкам осуществляется через службы ОСРВ, как показано на рисунке 5. Такая защита позволяет оградить модули и ядро от случайного повреждения из-за ошибочных записей или переходов. С помощью блока управления памятью или регистров микропроцессора можно ограничить доступ модуля к памяти.
![]() |
Рис. 5. Обращение к внешней памяти
|
Загружаемые программные модули — это прорыв для компактных приложений. Они позволяют существенно расширить возможности приложения и повысить удобство обслуживания. Меры по защите памяти на уровне потоков позволяют избежать ошибок доступа, устраняя самую распространенную причину трудных для диагностики сбоев программы.