Обычно, берясь за микроконтроллер, разработчик пишет очень конкретный код под одну задачу: устройство выполняет ровно то, что нужно, с минимальными накладными расходами. Но бывают сценарии, когда выгоднее взять более «тяжёлую» схему и запустить на микроконтроллере полноценную операционную систему — ради тех преимуществ, которые она даёт.
Источник изображения — hackaday.com
Именно этой теме был посвящён доклад на конференции Hackaday Europe. Речь шла об архитектуре ядер, операционных системах реального времени и о том, как грамотно использовать C++ в мире микроконтроллеров.
Microcontrolling
Начинается всё с полезной основы — с определения того, чем вообще является операционная система. По сути это программное обеспечение, находящееся между железом и приложениями. Если разложить на части, ОС состоит из ядра, поверх которого располагаются базовые системные службы, библиотеки и драйверы устройств, а также утилиты для обслуживания системы. Пользовательский интерфейс лежит уже над всем этим.
Архитектуры при этом различаются принципиально. Монолитные ядра, к которым относится Linux, разделяют пространство ядра и пространство пользователя: ядро имеет полный доступ к оборудованию и работает в системном режиме, а приложения — в пользовательском, без прямого доступа. Такая схема требует блока управления памятью (MMU), то есть полноценного процессора, как в ноутбуке или настольном ПК.
Юниядра (unikernel), к которым относится FreeRTOS, устроены иначе: приложения и операционная система схлопываются в один исполняемый двоичный файл с прямым доступом к железу. Ни абстракций, ни защиты памяти — зато такую архитектуру легко запустить на типичном микроконтроллере. Есть ещё микроядра, которые минимизируют объём кода, работающего в системном режиме, вынося драйверы и файловые системы в пользовательское пространство; им тоже нужен MMU, и встречаются они в нишах, где критична безопасность.
Источник изображения — hackaday.com
Проблема юниядер и идея «текучего ядра»
Для микроконтроллеров практически значимы именно юниядра. Но у них есть ограничения — по стабильности, по безопасности и в том, что нет загрузки кода во время выполнения и нет простого способа частично обновить систему.
Решить часть этих проблем призвано «текучее ядро» (fluid kernel). Замысел — масштабируемая ОС, работающая во всём диапазоне встраиваемых систем, где ресурсы ограничены, а блоков управления памятью, как правило, не бывает. При этом система должна оставаться совместимой со стандартными интерфейсами: POSIX, стандартные библиотеки C++ и так далее. Автор описывает это формулой «UNIX на кристалле».
Текучее ядро располагается на пересечении монолитного ядра и юниядра. Оно позволяет размещать приложения либо в пространстве ядра, либо в пользовательском — по необходимости. Совместимость с POSIX здесь двойная: один и тот же интерфейс работает в обоих пространствах.
Абстракция процессов достигается за счёт аппаратного блока защиты памяти (MPU), который распространён в современных 32-разрядных микроконтроллерах. Это не MMU: фокусов с виртуальной памятью он не умеет, но обеспечить базовый уровень защиты памяти на такой платформе способен. При желании текучее ядро можно собрать как юниядро — опцией времени компиляции, что убирает поддержку процессов и заметно сокращает объём кода. Практическая выгода очевидна: начать можно с юниядра, а позже перевести проект в полноценный режим, просто переключив флаг сборки.
В докладе подробно разобраны плюсы и минусы такой архитектуры, а также вытекающие вопросы безопасности. В качестве практической реализации философии рассматривается ОС реального времени Miosix, а отдельным полезным отступлением идёт разговор об эффективном использовании C++ на микроконтроллерах — теме, вокруг которой во встраиваемой разработке до сих пор ломают копья.













