Обзор одной российской rtos, часть 8. работа с прерываниями

Особенности ядра

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

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

Важной частью любой ОСРВ является планировщик задач, чья функция — определить, какая из задач должна выполняться в системе в каждый конкретный момент времени. К основным методам планирования обычно относят: циклический алгоритм (в стиле round robin), разделение времени с равнодоступностью (time sharing with fairness), кооперативную многозадачность

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

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

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

Выделение памяти

Следующим проблемам выделения памяти в ОСРВ уделяется больше внимания, нежели в операционных системах общего назначения.

Во-первых, скорости выделения памяти. Стандартная схема выделения памяти предусматривает сканирование списка неопределённой длины для нахождения свободной области памяти заданного размера, а это неприемлемо, так как в ОСРВ выделение памяти должно происходить за фиксированное время.

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

Простой алгоритм с фиксированной длиной участков памяти очень хорошо работает в несложных встроенных системах.

Также этот алгоритм отлично функционирует и в настольных системах, особенно тогда, когда во время обработки участка памяти одним ядром следующий участок памяти обрабатывается другим ядром. Такие оптимизированные для настольных систем ОСРВ, как Unison Operating System или DSPnano RTOS, предоставляют указанную возможность.

Применение систем реального времени

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

Примеры

Примеры систем, работающих в режиме реального времени:

  • АСУ ТП химического реактора;
  • бортовая система управления космического аппарата;
  • АСНИ в области ядерной физики;
  • система обработки аудио- и видеопотоков при трансляции в прямом эфире;
  • интерактивная компьютерная игра.

Отличительные черты ОСРВ

Таблица сравнения ОСРВ и обычных операционных систем:

ОС реального времени ОС общего назначения
Основная задача Успеть среагировать на события, происходящие на оборудовании Оптимально распределить ресурсы компьютера между пользователями и задачами
На что ориентирована Обработка внешних событий Обработка действий пользователя
Как позиционируется Инструмент для создания конкретного аппаратно-программного комплекса реального времени Воспринимается пользователем как набор приложений, готовых к использованию
Кому предназначена Квалифицированный разработчик Пользователь средней квалификации

Средства контроля за использованием ресурсов памяти в Mbed

Динамическая память равно как и стек — это ресурсы требующие постоянного внимания. Чтобы видеть насколько использована динамическая память и какова интенсивность запросов к ней, сколько осталось стека у каждой задачи и какова была пиковая загрузка стеков в Mbed есть специальные счетчики. По умолчанию они отключены директивой условной компиляции, для их включения надо объявить дефайн MBED_ALL_STATS_ENABLED. Когда дефайн объявлен, нужно написать свою процедуру для вывода информации пользователю. Мы написали специальную процедуру для вывода статистики в эмулятор терминала VT100, о чем будет рассказано позже.

Помимо средств предоставляемых OS среда разработки IAR в последних версиях добавляет новую возможность — стековые канарейки. Прочитать о них можно здесь. Общие вопросы защиты стека от переполнения рассмотрены здесь и здесь.

Назначение размеров стеков задач

Урезание стеков задач до минимума — самый привлекательный вариант экономии RAM.
Чтобы задачи требовали меньше стека применяются разные техники. Например, на стек сильно влияют библиотечные функции для вывода и форматирования строк printf, sprintf, scanf и т.д. Они имеют особенность выделять в стеке большие временные области для хранения данных. Если мы откажемся в задаче использовать эти функции, то сможем сократить стек задачи на каких-нибудь добрых пару сотен байт.

Mbed OS при старте сразу создает три задачи с именами: «main_thread», «timer_thread», «idle_thread». Размер стека по умолчанию для них определялся макросами в заголовочном файле mbed_rtx_conf.h. Мы перенесли объявления этих стеков в файл конфигурации mbed_config.h и сократили размер стеков. Теперь определения выглядят так:

  • Стек задачи «main_thread» определяется макросом MBED_CONF_APP_MAIN_STACK_SIZE = 1024 байт
  • Стек задачи «timer_thread» определяется макросом MBED_CONF_APP_TIMER_THREAD_STACK_SIZE = 512 байт
  • Стек задачи «idle_thread» определяется макросом MBED_CONF_APP_IDLE_THREAD_STACK_SIZE = 512 байт

Взаимодействие между задачами и разделение ресурсов

Многозадачным системам необходимо распределять доступ к ресурсам. Одновременный доступ двух и более процессов к какой-либо области памяти или другим ресурсам представляет определённую угрозу. А именно Deadloc и Priority inversionСуществует три способа решения этой проблемы:

  • Временное блокирование прерываний
  • Двоичные семафоры
  • Посылка сигналов.

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

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

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

Как организуются прерывания в Mbed

Как ни странно, но найти класс или функцию или что-то подходящее для организации обслуживания прерываний в API Mbed не удастся. Можно обнаружить только класс InterruptIn, который предначзначен только для внешних портов.

Ответы на такие вопросы надо искать в , а именно в хидерах CMSIS Cortex-M4 Core Peripheral Access Layer. Там определены макросы:

  • NVIC_SetVector — устанавливает для заданного вектора процедуру обслуживания прерываний (interrupt service routine, ISR)
  • NVIC_SetPriority — устанавливает приоритет для заданного вектора прерываний.
  • NVIC_EnableIRQ — разрешает вызов прерываний по заданному вектору

Вот как инициализируется организация прерыванияй от таймера SysTick:

Нельзя путать приоритеты задач Mbed и приоритеты прерываний.

Если приоритет не назначается, то по умолчанию он устанавливается максимальным.
Из назначенных таким образом обработчиков прерываний можно вызывать любые сервисы RTOS, которые не вызывают ожиданий. Т.е. передавать флаги, семафоры, сообщения, майлбоксы и проч. Сервисы вызываются однако не из самого ISR, а путем вызова программного прерываний установкой бита PENDSVSET в регистре Interrupt Control and State Register (ICSR) блока System control block (SCB) ядра Cortex-M. Т.е. после завершения текущего обработчика прерываний если нет других приоритетных прерываний произойдет вызов системного обработчика по вектору PendSV где и будет проведено обслуживание.

Основные характеристики

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

Nucleus RTOS обеспечивает всестороннюю поддержку многоядерных решений с 32/64 битными решениями для uAMP, sAMP и SMP архитектур. Масштабируемость Nucleus облегчает ее использование в качестве исполнительной среды выполнения, по существу, для каждого ядра многоядерного SoC, включая ARM Cortex-A, ARM Cortex-R, ARM Cortex-M и DSP. Как компонент многоядерных решений Mentor Embedded, Nucleus RTOS дополняет Mentor Embedded Hypervisor, Mentor Embedded Multicore Framework и Mentor Embedded Linux, что позволяет разработчикам использовать мощные современные многоядерные системы на кристаллах (SoCs). Многоядерное решение Mentor предоставляет самый широкий набор интегрированных инструментов, технологий разделения и межпроцессорных коммуникационных решений (IPC) для управления операционными системами и ресурсами устройств на сложных многоядерных SoC-архитектурах.

Масштабируемость

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

Модель процесса

Модель процесса Nucleus обеспечивает разбиение пространства для изоляции программных подсистем для повышения надежности системы. Эта облегченная структура обеспечивает изоляцию с использованием MMU на ядрах на базе Cortex-A или MPU на процессорах на базе Cortex-M. Без накладных расходов на виртуализацию памяти модель процесса Nucleus использует карту линейной памяти с защищенными областями памяти, которая гарантирует, что подсистемы программного обеспечения не могут обращаться к областям памяти без особых прав. Разработчики могут динамически перезагружать, перезапускать и обновлять модули приложений и ядра, не затрагивая другие модули или снимая систему.

Энергосберегающая разработка

Разработчики могут воспользоваться преимуществами последних энергосберегающих функций в современных процессорах с Nucleus Power Management Framework. Фреймворк Nucleus был построен с нуля для обеспечения поддержки DVFS, режимов сна, режимов глубокого сна, подавления галочки, переходов мощности и т. д. Разработчики могут использовать инфраструктуру управления энергопотреблением для написания приложений, удовлетворяющих требованиям низкого энергопотребления, путем вызова высокоуровневых API для управления состоянием питания отдельных устройств или всей системы.

Связность и промежуточное ПО

Nucleus поддерживает широкий спектр решений для подключения, в том числе оптимизированные USB 2.0 / 3.0, SDIO 2.0 / 3.0, Wi-Fi, Bluetooth / BLE, 802.15.4, PCIe и другие. Решения промежуточного ПО Nucleus включают в себя двойной сетевой стек IPv4 / IPv6, OPC UA, Ethernet / IP, мастер EtherCAT, службу сбора данных (DDS) и многое другое. Для удовлетворения требований безопасности устройства криптографическая поддержка Nucleus включает в себя OpenSSL, TLS и CyaSSL, а также поддержку аппаратного шифрования. Надежность Nucleus доказана прохождением строгих стандартов, связанные с сертификацией уровня 1 и 2 Wurldtech Achilles
(сертификация отказоустойчивости сети).

Графика

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

Безопасность

Решения Nucleus для обеспечения безопасности включают в себя целый ряд технологий безопасности, обеспечивающих поддержку ARM TrustZone, высоконадежную загрузку, корень доверия, безопасное хранение и защиту данных при передаче.

Интегрированная среда разработки CodeBench IDE

Nucleus ReadyStart включает в себя полную среду разработки на основе Mentor Embedded Sourcery CodeBench с инструментальными средствами GCC / C++ на основе GNU и Sourcery Analyzer для охвата всех аспектов встроенной разработки, начиная от воссоздания устройств и разделения ресурсов для многоядерных устройств до оптимизации приложений.

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

Работа планировщика

Основная статья: Диспетчер операционной системы

Большинство ОСРВ выполняет планирование задач, руководствуясь следующей схемой. Каждой задаче в приложении ставится в соответствие некоторый приоритет. Чем больше приоритет, тем выше должна быть реактивность задачи. Высокая реактивность достигается путём реализации подхода приоритетного вытесняющего планирования (preemptive priority scheduling), суть которого заключается в том, что планировщику разрешается останавливать выполнение любой задачи в произвольный момент времени, если установлено, что другая задача должна быть запущена незамедлительно.

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

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

Каждый раз, когда планировщик задач получает сигнал о наступлении некоторого внешнего события (триггер), причина которого может быть как аппаратная, так и программная, он действует по следующему алгоритму:

  1. Определяет, должна ли текущая выполняемая задача продолжать работать.
  2. Устанавливает, какая задача должна запускаться следующей.
  3. Сохраняет контекст остановленной задачи (чтобы она потом возобновила работу с места остановки).
  4. Устанавливает контекст для следующей задачи.
  5. Запускает эту задачу.

Эти пять шагов алгоритма также называются переключением задач.

Выполнение задачи

В обычных ОСРВ задача может находиться в трёх возможных состояниях:

  • задача выполняется;
  • задача готова к выполнению;
  • задача заблокирована.

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

Основная функция администратора ОСРВ заключается в составлении такого планировщика задач.

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

Алгоритмы планирования

В настоящее время для решения задачи эффективного планирования в ОСРВ наиболее интенсивно развиваются два подхода:

  • статические алгоритмы планирования (RMS, Rate Monotonic Scheduling) — используют приоритетное вытесняющее планирование, приоритет присваивается каждой задаче до того, как она начала выполняться, преимущество отдаётся задачам с самыми короткими периодами выполнения;
  • динамические алгоритмы планирования (EDF, Earliest Deadline First Scheduling) — приоритет задачам присваивается динамически, причём предпочтение отдаётся задачам с наиболее ранним предельным временем начала (завершения) выполнения.

При больших загрузках системы EDF более эффективен, нежели RMS.

Особенности перехода на C++ в применении к RTOS

API верхнего уровня в Mbed написано на C++, поэтому этот язык приходится использовать и в прикладном коде. Но тут есть нюансы о которых необходимо знать.
Использование C++ для RTOS в малых встраиваемых системах еще сравнительная редкость. Проблема здесь в том, что успешные проекты RTOS стремятся быть мультиплатформенными, а С++ предъявляет повышенные требования к менеджменту ресурсов платформы по сравнению с С. Причина в стремлении скрыть от пользователя детали низкоуровневого управления ресурсами. Речь прежде всего о ресурсах памяти. Конструкторы, деструкторы, потоки, исключения c автоматической деструкцией, шаблоны объектов структур данных и др. используют неявные операции с динамической памятью. Но ресурс оперативной памяти RAM в малых системах очень ограничен. Оперативная память — самый дефицитный ресурс в таких системах и особенно в RTOS. В RTOS каждой задаче выделяется стек, его точный размер разработчик заранее спрогнозировать не может и поэтому выбирает с запасом. Таким образом наличие RTOS с десятком задач сразу вызывает необходимость в RAM размером от 10 до 30 кБ. Еще много памяти нужно для различных парсеров и протоколов (HTTP, HTML…) и файловых систем. Если применяется дисплей, то еще более увеличиваются требования к свободной RAM.

Библиотеки сред разработки типа IAR оснащаются неплохими менеджерами динамической памяти, но они рассчитаны на однопоточную среду исполнения. Чтобы они начали работать в RTOS нужно написать дополнительный код. Этот процесс называется retargeting.
В RTOS написанных на C ретаргетинг как правило не производится. Поскольку там нет неявных операций с динамической памятью на уровне языка, то все операции выполняются явно вызовом собственных потокобезопасных вариантов функций malloc и free. Программист имеет полный контроль над операциями с динамической памятью и легко может применять все возможные меры по ее экономии.

В случае C++ если мы хотим использовать все возможности этого языка нам придется делать retargeting. Но retargeting в каждой среде разработки процесс сугубо индивидуальный. Это и усложняет жизнь разработчикам RTOS.

На рисунке ниже пример структуры вызовов с ретаргетингом. Функции __write, __lseek, __read могут пользователем и не реализовываться, но тогда их функциональность остается на усмотрение IDE. И уж точно printf и scanf не будут многопоточными.

Mbed одна из немногих если не единственная RTOS, которая предоставляет исходники с уже проделанным ретаргетингом под триаду известных средств разработки: GCC, IAR, Keil
Несмотря на все сказанное выше можно встретить статьи о портировании RTOS на C++ без выполнения ретаргетинга, например решая проблему простой заменой некоторых распространненных стандартных функций на свои. Это работать может, но программисту тогда нужно помнить о разных неявных и недокументированных ограничениях при использовании конструкций C++ в IAR(только статические конструкторы, проверять все шаблоны на использование new, отказаться от исключений и т.д.). Это уже будет сложно назвать C++. Mbed как система дружелюбная к пользователю снимает многие такие ограничения приближаясь по простоте к Arduino.

Помимо всего в свежих версиях IAR есть трудности перехода на C11 и C++14 о чем написано здесь — https://www.iar.com/support/resources/articles/exploring-c11-and-c14/

Добавить комментарий

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

Adblock
detector