Краткое описание
-
Embox — операционная система реального времени, ориентированная на применение во встроенных системах.
-
Embox — кросс-платформенная ОС. Архитектурно-зависимые части вынесены в отдельные модули, что упрощает процесс портирования на новые платформы. Embox поддерживает следующие процессорные архитектуры: x86, ARM, MIPS, SPARC, PPC, Microblaze, E2k.
-
Embox является многозадачной ОС и поддерживает различные уровни приоритета задач, вытесняющую многозадачность, защиту от инверсии приоритета и различные способы синхронизации.
-
Embox предоставляет слой POSIX-совместимости, который позволяет использовать большое количество готового ПО разработанного под Linux. Примерами являются библиотека Qt, клиент и сервер SSH — Dropbear, проект по созданию SIP-телефонов на основе популярной библиотеки PJSIP.
-
Embox позволяет сохранить разработку привычной и удобной на всех платформах, в том числе на платформах с ограниченными ресурсами, такими как микроконтроллеры, так как используются стандартные средства разработки для Linux, а библиотека языка С входит в состав проекта.
-
Embox имеет низкие требования к ресурсам, поскольку построен на принципах модульности и конфигурируемости. При конфигурации системы можно выбрать, какие модули будут включены в образ. Таким образом, достигается создание образа, нацеленного на конкретную задачу, будь то система для тестирования аппаратного обеспечения, или полноценная система с множеством приложений и богатой функциональностью.
-
Embox позволяет создавать более безопасные системы. Основным вариантом использования является статическая конфигурация системы, при которой гарантируется, что невозможно выполнить приложения не включенные в конечный образ.
-
Embox хорошо подходит для устройств интернета вещей (IoT). Поскольку имеет развитый сетевой стек и богатый набор прикладных приложений и библиотек, но вместе с тем имеет низкие требования к аппаратным ресурсам.
-
Embox хорошо подходит для робототехники, поскольку позволяет совмещать в одной системе, задачи требующие сложные функциональные возможности и задачи “реального времени”.
Быстрый старт
Ознакомление с Embox лучше начать с запуска на эмуляторе qemu поддерживающего различные процессорные архитектуры.
Получение кода Embox
Клонируем мастер:
$ git clone https://github.com/embox/embox
Либо скачиваем архивом из https://github.com/embox/embox/releases
Работа в Windows и MacOS
Все описания сделаны для Linux. Для сборки и работы под Windows и MacOS лучше использовать Docker в котором уже настроено все необходимое окружение для начальной работы. Для этого:
- Установите docker для вашей ОС.
- Убедитесь, что docker установлен правильно с помощью команд:
$ docker-machine ls
$ docker-machine start default
- Перейдите в консоли в корневую папку с исходниками Embox.
- Запустите докер выполнив скрипт
$ ./scripts/docker/docker_start.sh
- Для упрощения работы выполните скрипт командой
$. ./scripts/docker/docker_rc.sh
Данный скрипт позволит писать команду dr
вместо docker run
Далее перед каждой командой необходимо вставлять dr
. Например,
$ dr make confload-x86/qemu
$ dr make
$ dr ./scripts/qemu/auto_qemu
Для конфигурации сборки и запуска базового темплейта.
После установки и проверки работоспособности, можно пропустить раздел настройка окружения и перейти сразу к разделу “Сборка и запуск на QEMU”
Настройка окружения
Необходимые пакеты: make, gcc (кросс-компилятор под выбранную архитектуру, см. “Установка кросс-компилятора”). Дополнительные пакеты (рекомендуется установить сразу): build-essential gcc-multilib curl libmpc-dev python Пример установки для Debian:
$ sudo apt-get install make gcc \
build-essential gcc-multilib \
curl libmpc-dev python
Пример установка для Arch:
$ sudo pacman -S make gcc-multilib cpio qemu
Установка кросс-компилятора
x86:
$ sudo apt-get install gcc
Обычно уже установленный пакет. Вам потребуется другой компилятор, если Вы настраиваете окружение самостоятельно для Windows или MacOS.
ARM:
$ sudo apt install arm-none-eabi-gcc
Или для Debian
$ sudo apt install gcc-arm-none-eabi
Либо скачать архив с тулчейном с сайта https://launchpad.net/gcc-arm-embedded. Распаковать архив и сделать export тулчейна:
$ export PATH=$PATH:<путь к тулчейну>/gcc-arm-none-eabi-<version>/bin
SPARC, Microblaze, MIPS, PowerPC, MSP430:
Для этих архитектур можно воспользоваться нашим проектом по сборке crosstool https://github.com/embox/crosstool.
Можно скачать последнюю версию уже собранного архива в требуемым кросс-компилятором отсюда https://github.com/embox/crosstool/releases.
Или собрать его выкачав скрипты из репозитория с помощью команды
$ ./crosstool.sh ARCH
После этого должен появиться архив с тулчейном — ARCH-elf-toolchain.tar.bz2. Далее, его нужно распаковать и добавить в переменную окружения PATH как показано выше для ARM.
Установка эмулятора QEMU
Поддерживаемые архитектуры: x86, ARM, MIPS, Sparc, PPC, Microblaze.
Необходимые пакеты: qemu (под выбранную архитектуру)
$ sudo apt-get install qemu-system-<ARCH>
где
$ sudo apt-get install qemu-system
Сборка и запуск на QEMU
Загружаем конфигурацию по умолчанию для выбранной архитектуры:
$ make confload-<ARCH>/qemu
, где
$ make confload-x86/qemu
Собираем Embox:
$ make
или запускаем параллельную сборку:
$ make (-jN)
Пример:
$ make -j4
Запускаем:
$ ./scripts/qemu/auto_qemu
Пример вывода в консоль:
Embox kernel start
unit: initializing embox.kernel.task.task_resource: done
unit: initializing embox.mem.vmem_alloc: done
...
Если все unit тесты прошли успешно и система загружена, появиться консоль в которой можно выполнять команды. Начать можно с команды 'help' которая выведет список доступных команд для вашей конфигурации. Для выхода из эмулятора qemu нажмите последовательно ctrl+’A’ затем ‘x’.
Особенности системы сборки Mybuild
Embox - модульная и конфигурируемая система. Для этих целей был разработан декларативный язык описания Mybuild. Он позволяет описывать как отдельные единицы системы (модули) так и всю систему в целом.
Модуль является базовым понятием для системы сборки. Он содержит: список файлов относящихся к данному модулю, параметры которые можно задать модулю в момент конфигурации и список зависимостей.
Конфигурация является детализированным описанием желаемой системы. Включает в себя: список модулей необходимых для сборки, параметры модулей и описания правил сборки (компилятор, флаги компилятора, карта памяти устройства, и так далее). На основе конфигурации и описания модулей строится граф с параметрами системы и по нему генерируются различные файлы для сборки: линкер скрипты, makefile-ы, заголовочные файлы. В конфигурации не обязательно указывать все необходимые модули, они подтягиваются по зависимостям из описания модулей.
Текущая конфигурация располагается в папке conf/. Может быть выбрана с помощью команды
$ make confload-<CONF_NAME>
Например, для задания демонстрационной конфигурации для запуска на qemu-system-arm необходимо выполнить
$ make confload-arm/qemu
Для просмотра готовых конфигураций можно выполнить
$ make confload
После задания текущей конфигурации можно изменять файлы под свои требования. Например, чтобы добавить какое нибудь приложение которого нет в текущей конфигурации достаточно добавить в файл conf/mods.conf строку
include <PACKAGE_NAME><MODULE_NAME>
Пример, для добавления в конфигурацию команды 'help' нужно добавить строчку
include embox.cmd.help
Создание и запуск “hello world”
Приложение в Embox представляет собой модуль в описании которого содержаться атрибуты указывающие, что это приложение можно запускать из командной строки. Исходный код представляет собой обычное приложение которое можно скомпилировать с том числе и в Linux окружении.
Создание и запуск примера
Разберем простейшее приложение “hello world”.
- Создадим папку hello_world в src/cmds:
$ mkdir src/cmds/hello_world
- Создадим файл с исходным кодом приложения src/cmds/hello_world/hello_world.c со следующим содержанием:
#include <stdio.h>
int main(int argc, char **argv) {
printf("Hello, world!\n");
}
- Создадим файл описания модуля src/cmds/hello_world/Mybuild следующего содержания:
package embox.cmd
@AutoCmd
@Cmd(name = "hello_world", help=”First Embox application”)
module hello_world {
source "hello_world.c"
}
- Добавим в файл конфигурации системы conf/mods.conf строчку с подключением нового модуля:
include embox.cmd.hello_world
- Компилируем:
$ make
- Запускаем:
$ ./scripts/qemu/auto_qemu
В появившейся консоли убедимся, что если набрать команду 'help' то в списке будет новая команда. Выполним команду набрав ее в консоли hello_world Должно появиться наше сообщение выведенное с помощью функции printf() :
root@embox:/#hello_world
Hello, world!
root@embox:/#
Файл описания модуля
Разберем немного подробнее файл описания модуля.
package embox.cmd
@AutoCmd
@Cmd(name = "hello_world", help=”First Embox application”)
module hello_world {
source "hello_world.c"
}
В первой строке идет указание имени пакета package embox.cmd. В Embox все модули распределены по пакетам, для удобства именования. Полное имя модуля будет состоять из имени пакета и имени модуля. Имя модуля в нашем случае находиться в строке module hello_world.
Строка source "hello_world.c" указывает файлы с исходным кодом необходимые для корректной сборки модуля.
В строке @Cmd(name = "hello_world", help=”First Embox application”) задается атрибут для модуля. Во первых модуль будет представлять из себя приложение, во вторых задает имя с помощью которого это приложение можно вызвать. И наконец, задается строка, которая будет отображаться для этого приложения при вызове команды 'help'. Строка @AutoCmd указывает, что в приложении есть стандартная функция входа в приложение main(), которая будет заменена на другой символ в процессе сборки.
Система сборки Mybuild
Mybuild - это система автоматизации сборки и конфигурирования для модульных приложений.
Mybuild реализован поверх GNU Make, тем самым поддерживая все возможности инкрементальной и параллельной сборки последнего. Кроме того, это позволяет встроить Mybuild в уже существующую инфраструктуру сборки, использующую Make. В то же время, несмотря на использование Make, Mybuild использует собственный синтаксис, лишенный недостатков языка Makefile'ов.
Процесс разработки
Типичный сценарий разработки проекта, использующего Mybuild, включает две группы людей:
Разработчики | Пользователи |
---|---|
которые поддерживают файлы для сборки разрабатываемых ими компонентов. | которым требуется просто собрать проект (возможно, в различных конфигурациях). |
В действительности, есть еще разработчики самого Mybuild, но их мы пока что не рассматриваем. Таким образом, существует два типа файлов для сборки: My-файлы и Config-файлы
My-файлы
My-файлы используются для описания всех модулей приложения, доступных для сборки, их отношения между собой, а также параметры конфигурации. Эти файлы пишутся разработчиками самого приложения и обычно располагаются в дереве файлов с исходным кодом.
module HelloWorld {
source "hello.c"
}
My-файлы именуются Mybuild
либо *.my
.
Config-файлы
Config-файлы содержат указания для сборки определенных модулей и конкретные значения параметров конфигурации. Предполагается, что данные файлы составляются пользователями, тем не менее, разработчики могут предоставить шаблоны для типичных конфигураций.
configuration Main {
include HelloWorld
}
Конфигурационные файлы имеют расширение .config
.
Оба типа файлов являются обычными текстовыми файлами (не XML!), которые можно редактировать в любом текстовом редакторе.
После того как необходимые my- и config-файлы готовы, проект можно собрать командой make
.
На основе предоставленных сборочных файлов Mybuild решит, что именно и в каком порядке необходимо собрать.
В заключение, процесс сборки можно представить следующим образом:
Mybuild процесс сборки
Процесс сборки с помощью системы Mybuild можно представить следующим образом:
Подготовка скриптов
Mybuild написана на расширении языка Make, поэтому, сначала требуется преобразовать код написанный на расширении языка Make в классические Make-файлы. Для ускорения этой процедуры используется кэширование: преобразуются только измененные с предыдущего запуска файлы.
На этом этапе происходит проверка актуальности кэша исходных файлов Mybuild и библиотеки расширения. Если файл с исходным кодом не изменялся, используется его закэшированная версия. Иначе файл загружается, преобразуется и кэшируется с помощью скрипта.
Создание графа описания модулей
Обработка Mybuild файлов (файлов описание модулей) разбивается на фазы:
- Нахождение Myfile в дереве с исходным кодом.
- Для каждого файла:
- Чтение файла.
- Синтаксический разбор, создание объекта (mk/mybuild/myfile-model.mk) на каждую разобранную синтаксическую конструкцию.
- Сохранение полученной после разбора модели файла для использования последующими стадиями (путь по умолчанию: mk/.cache/mybuild/files/).
- Создание набора моделей, представляющего полное описание графа модулей, разрешение ссылок между моделями в наборе.
- Проведение различных проверок, относящихся к графу модулей (соответствие типов опций и инициализирующих их значений, циклическое наследование, и т.д)
- Кэширование полученного набора моделей.
Создание модели системы
Для описания модели системы так же используется специализированный язык описания. Поэтому, для обработки файлов конфигурации проводятся фазы, аналогичные фазам 1-5 обработки файлов описания модулей. Затем, происходит создание Build-модели (mk/mybuild/build-model.mk), в нее входит модули, их файлы, опции модулей, опции компиляции. Для этого используются модели конфигурации и Mybuild.
Генерация необходимых ресурсов
На этом этапе проводится сборка полученной на предыдущем этапе Build-модели.
- Генерируется исходник на языке Си, содержащий run-time представление Build-модели.
- Для модулей генерируются заголовочные файлы с опциями и экспортированными из модуля заголовочными файлами.
- Для файлов с исходным кодом генерируются командные файлы, содержащие параметры командной строки компилятора.
- Генерируется Make-правила для сборки целевого образа и промежуточных объектных файлов.
Запуск скриптов на исполнение
На этой стадии сборки происходит передача управления от Mybuild к сгенерированному им скриптам сборки Make. Результатом выполнения данного этапа является полностью собранный образ целевой системы.
Mybuild - внутренняя структура
Система сборки целиком написана на языке GNU Make и состоит из набора скриптов. Следует различать три основных типа Makefile’ов, используемых для реализации:
- Классические Makefile’ы, в которых задаются правила для выполнения тех или иных целей, их зависимости и т.д. В основном к ним относятся скрипты верхнего уровня, определяющие последовательность запуска остальных скриптов.
- Библиотеки функций, в которых определяются функции и классы. Большая часть логики Mybuild реализована именно в таких скриптах. Основная часть кода (за исключением сравнительно небольшого блока раскрутки), написана с использованием расширенного синтаксиса языка Make.
- Скрипты, результатом выполнения которых является текстовый файл.
Классические Makefile’ы
Для выполнения любой внешней цели (которую вызывает пользователь) сперва читается корневой Makefile. Его можно рассматривать как обертку для запуска самой системы сборки. В этом скрипте происходит проверка версии Make и необходимых флагов, задаются значения по умолчанию для директорий сборки и т.д. Далее управление передается в mk/main.mk. mk/main.mk
Этот скрипт - фасад системы. В нем определяются все доступные для выполнения цели, а также документация для этих целей, которую можно увидеть, выполнив “make help” или “make help-'<'goal'>'”. Можно различать два вида целей: Цели, которые можно выполнить “на месте” (загрузка конфигурации, clean, вывод справки по целям и т.п.). Цели, для выполнения которых требуется чтение всех my-файлов и построения модели сборки (собственно, сборка проекта). В этом случае управление передается специальному загрузочному скрипту mk/load.mk mk/load.mk
Скрипт используется для раскрутки остальных скриптов системы сборки, загрузки my-файлов и создания модели сборки. Разделен на два файла:
- load-mk.inc.mk, который занимается раскруткой скриптов (библиотеки функций),
- load-mybuild.inc.mk, который вызывает скрипты для разбора my-файлов, их линковки, сериализации и построения модели сборки.
Модульная структура Embox
Важными особенностями Embox является модульность и конфигурируемость. Под модульностью подразумевается разбиение проекта на небольшие логические части - модули, а под конфигурируемостью - возможность детального задания характеристик конечной системы, на основе списка требуемых модулей и параметров используемых модулей. Для этого используется система сборки Mybuild, со специальным языком программирования (DSL) позволяющим описывать как модули так и систему в целом. При этом программная логика модулей располагается отдельно от описания и разрабатывается на обычном языке программирования (GPL).
Файлы описания модулей
Пакеты модулей
Структурно модули организованы в иерархические группы (пространства имен), составленные по функциональной принадлежности. Данный механизм позволяет избежать коллизий в именах модулей, сохраняя их короткими. Как правило, имя пакета совпадает в путем в файловой системе, что облегчает поиск файлов модуля в дереве исходного кода.
Пример задания имени пакета:
package embox.arch
Интерфейсы и абстрактные модули
Интерфейсы для модулей являются прямым аналогом интерфейсов и абстрактных классов в объектно-ориентированном программировании. Язык описания модулей поддерживает наследование, таким образом, позволяя ввести понятие и интерфейсов (модулей без реализации) и абстрактных модулей (с частичной реализацией). Модули, которые реализуют один интерфейс или наследуют общий родительский модуль, взаимозаменяемы до тех пор, пока это не изменяет функциональность системы. Данный подход позволяет пользователю выбирать среди модулей, реализующих один интерфейс, но имеющих разные алгоритмы, чтобы обеспечить необходимые свойства системы. Для указания того что модуль является наследуемым нужно использовать ключевое слово abstract.
Пример задания абстрактного модуля:
package embox.arch
//...
abstract module interrupt { }
//...
Для указания наследования используется ключевое слово extends.
Пример наследования от абстрактного модуля:
module interrupt_stub extends embox.arch.interrupt {
//...
}
Атрибуты модулей
Описание каждого модуля состоит из нескольких возможных атрибутов: файлы исходного кода, опции и зависимости.
Файлы исходного кода
Каждый модуль может указывать список файлов, которые необходимо скомпилировать и включить в итоговый образ в случае включения данного модуля в сборку. Список файлов указывается в атрибуте source модуля. Помимо “обычных” файлов на языке Си или ассемблера, можно также добавлять заголовочные файлы и дополнительные линкер-скрипты. Тип файлов различается по расширению:
- .c/.S - исходные коды на языке Си или ассемблера. При сборке компилируются и включаются в итоговый образ системы. Во время компиляции есть возможность получить значения опций модуля, к которому относятся эти файлы исходного кода.
- .h - заголовочные файлы, содержащие объявления и определения, необходимые для реализации модулем какого-либо интерфейса. При сборке для каждого включенного модуля генерируется специальный заголовочный файл, включающий все перечисленные .h-файлы данного модуля, а также модулей, расширяющих данный. Это позволяет использовать различные реализации того или иного интерфейса без изменения исходного кода модулей, которые его используют. Такой способ абстракции необходим, поскольку различные реализации могут определять ту или иную структуру по разному, в то время как структура может использоваться другими модулями без знания деталей реализации. То же самое относится и к макросам, inline-функциям и константам.
- .lds.S - линкер-скрипты, позволяющие влиять на процесс компоновки модулей в итоговый образ. Типичное использование таких скриптов - это добавление новых секций.
Пример задания заголовочного файла с реализацией абстрактного модуля:
module interrupt_stub extends embox.arch.interrupt {
source "interrupt_stub.h"
}
Пример задания файла с линкер скриптом и файла с исходным кодом:
module static_heap extends heap_place {
// ...
source "heap.lds.S"
source "static_heap.c"
// ...
}
Опции
Опции позволяют определить на этапе конфигурирования числовые, логические или строковые параметры, которые могут влиять способ сборки, инициализацию или работу модуля. Опции также могут иметь или не иметь значение по умолчанию. В последнем случае значение должно быть указано во время конфигурирования, в первом - это не обязательно, тогда будет использоваться значение по умолчанию. Опции разделяются на три типа в зависимости от типа задаваемого значения. Тип опции задается после ключевого слова option перед именем опции:
- string - строковые опции
- number - целочисленные данные
- boolean - булевое значение - true или false
Чтобы получить значение опции при компиляции исходного кода используются специальные макросы:
- OPTION_STRING_GET - для получения значения строковых опций
- OPTION_NUMBER_GET - для числовых опций
- OPTION_BOOLEAN_GET - для булевых опций
Аргументом макроса выступает имя опции, определенное в my-файле.
Зависимости
Зависимости являются способом указать системе сборки, что корректное функционирование данного модуля невозможно без некоторых других модулей. Список зависимостей может включать интерфейсы, при этом это означает, что в сборку должен быть включен ровно один модуль, реализующий требуемый интерфейс. Межмодульные зависимости указываются через атрибут depends. В значении атрибута можно перечислять как модули, так и интерфейсы. Система сборки гарантирует, что при включении данного модуля, будут добавлены и все его зависимости. В случае зависимости от интерфейса используется одна из его реализаций. Знание о межмодульных зависимостях используется как для получения списка модулей для сборки, так и в момент загрузки системы (см. далее). В некоторых случаях требуется просто включить нужный модуль вместе с данным, без изменения порядка загрузки, например, для использования таких глобальных модулей (аспектов), как поддержка многопроцессорности, логгирование или отладочные утверждения (assert). Поскольку у таких модулей нет как такового состояния (загружен или не загружен), для указания их в качестве зависимости атрибут depends дополняется аннотацией @NoRuntime. В этом случае зависимость будет использоваться во время сборки, но не будет определять порядок загрузки модулей относительно друг друга.
Аннотации
Аннотации применяются для модификации семантики некоторых элементов описания. Это позволяет дополнять язык описаний без изменения грамматики, что делает язык более гибким.
Пример задания с помощью аннотации реализации абстрактного модуля по умолчанию:
@DefaultImpl(embox.arch.generic.interrupt_stub)
abstract module interrupt { }
Описание конфигурации
Описание модулей необходимо для создания целевого образа. При конфигурировании система сборки позволяет объединить отдельные модули системы (модули ядра, драйвера, тесты, приложения), задать параметры для отдельных модулей и указать дополнительные параметры для создания образа под различные аппаратные платформы.
Структура конфигурации
Конфигурация образа происходит редактированием файлов конфигурации в каталоге conf/. Его содержимое следующее:
- lds.conf - Файл lds.conf содержит определение карты памяти, использующейся на конкретной аппаратной платформе.
- mods.config - Файл mods.config содержит названия и опции модулей, которые будут включены в образ ОС. Также, для каждого из перечисленного в этом файле модуля возможно указать новые значения опций.
- rootfs/ - Директория rootfs содержит файлы, которые будут включены в состав файловой системы, которая будет доступна на ранних этапах загрузки.
Процесс конфигурирования
Использование модуля в образе ОС подразумевает его включение в конфигурацию ОС.
Базовая конфигурация
Подготовка конфигурации является длительным процессом, поэтому, чтобы сократить время, затрачиваемое на конфигурирование, используются базовые конфигурации. Их назначение заключается в том, чтобы предоставить пользователю основу с некоторыми базовыми функциями, которую тот сможет модифицировать под свои нужды.
В состав ОС включены несколько конфигураций, предназначенных для использования как базовых. Для каждой платформы подготовлено несколько конфигураций, обладающих разными свойствами и возможностями.
Чтобы получить подготовленную конфигурацию, для, например, самой основной поддержки платформы x86, воспользуйтесь следующей командой:
make confload-x86/qemu
Эта команда загружает в каталог conf подготовленную базовую конфигурацию под названием qemu для платформы x86. Список всех базовых конфигураций можно увидеть и выбрать среди них, набрав:
make confload
Включение модуля в конфигурацию
Список модулей для включения в конфигурацию находится в файле conf/mods.config, который имеет следующую структуру:
package genconfig
configuration conf {
[список_модулей]
}
[список_модулей] определяет положение набора строк, каждая из которых определяет включение модуля. Добавьте новую строку к списку модулей следющего содержания:
include pkg.new_package.empty
В итоге, файл conf/mods.config должен иметь следующее содержание:
package genconfig
configuration conf {
[список_модулей]
include pkg.new_package.empty
}
После этого, модуль empty из пакета pkg.new_package будет включен сборку. Чтобы проверить непротиворечивость полученной сборки и, в случае успеха, произвести создание образа ОС, наберите
make
В случае положительного результата будет выведено сообщение “Build complete”. Убедиться в том, что ОС содержит новый модуль можно запустив ОС на исполнение и выполнить команду lsmod c параметрами -n и empty, которая выведет список модулей, у которых в имени модуля присутствует подстрока “empty”.
lsmod -n empty
Результатом lsmod будет являться печать
* pkg.new_package.empty
Это означает, что модуль pkg.new_package.empty имеется в системе, а символ “*” обозначает, что в данный момент модуль загружен и работает.
Сборка
Описание конфигурации проекта
Описание конфигурация представляет собой детальное задание требований к функциональности системы. Подробнее смотри раздел "Модульная структура Embox". Текущая конфигурация содержится в файлах расположенных папке 'conf/'.
Выбор конфигурации
Для задания желаемых свойств целевой системы необходимо создать описание в папке 'conf/'. Если существует описание конфигурации с близкими свойствами проще воспользоваться ими. И на основе уже существующей конфигурации создать свою.
Посмотреть существующие конфигурации можно с помощью команды:
$ make confload
затем можно сделать выбранную конфигурацию рабочей с помощью команды:
$ make confload-<template>
Пример:
$ make confload
List of available templates:
...
platform/quake3/qemu
...
Use 'make confload-<template>' to load one.
$ make confload-platform/quake3/qemu
в нем выбирается имеющаяся в списке конфигурация platform/quake3/qemu
Сборка из имеющейся конфигурации
Для сборки образа из имеющейся конфигурации необходимо сделать рабочей выбранную конфигурацию, как описано в предыдущем примере. А затем выполнить команду
$ make
для сборки образа. В случае успешного завершения появится надпись "Build complete", а также файл образа в формате ELF 'build/base/bin/embox'.
Например:
text data bss dec hex filename
1259425 248540 170593504 172101469 a420f5d build/base/bin/embox
Build complete
Изменение характеристик системы
После выбора конфигурации ее требуется изменить. Текущая (рабочая) конфигурация находиться в папке conf/. Изменения файлов в этой папке изменяют характеристики конечной системы до желаемых. Важно: если сделать make confload-, удалить папку или сделать очистку проекта, которая затронет текущую конфигурацию (например, make distclean), текущие изменения потеряются.
Изменение функциональности системы
Изменение списка модулей
Функциональность системы описывается в файле mods.conf. Для изменения нужно изменить данный файл. Например, для включения дополнительной команды в список нужно добавить строчку
include <PACKAGE_NAME>.<MODULE_NAME>
Например
include embox.cmds.help
то же самое для обычных модулей.
Изменение параметров модулей
Для изменения параметров модуля нужно указать в скобках имя опции и желаемое значение. Например, для установки размера стека потока нужно изменить (или установить) параметр thread_stack_size в модуле embox.kernel.thread.core:
include embox.kernel.thread.core(thread_stack_size=0x4000)
Порядок загрузки
В конфигурации можно также повлиять на порядок загрузки модулей системы, для этого используется аттрибут @Runlevel(level) например @Runlevel(2), но обычно этого не требуется, поскольку при загрузке модулей учитываются их зависимости, и если требуется подгрузить модуль для работы данного модуля, то он подгрузится по зависимостям.
Изменение реализации интерфейса
Для изменения реализации интерфейса или абстрактного модуля, что это такое см. раздел "Модульная структура Embox", нужно просто подключить другой модуль реализующий данный интерфейс.
Например, абстрактный модуль heap_api
@DefaultImpl(heap_bm)
abstract module heap_api {
...
}
Для подключения модуля heap_simple который также реализует данный интерфейс
module heap_simple extends heap_api {module heap_simple extends heap_api {
...
}
необходимо удалить (если есть) модуль heap_bm то есть следующую строчку
include embox.mem.heap_bm
и все остальные модули которые наследуют данный абстрактный модуль и добавить
include embox.mem.heap_simple
Изменение флагов компиляции (отладка, оптимизации)
Некоторыми флагами компиляции можно управлять. Флаги компиляции задаются в файле build.conf текущей конфигурации. Например, флаг, который часто требует изменения, флаг оптимизации:
CFLAGS += -O0
данную строчку нужно заменить на
CFLAGS += -O2
для сборки с оптимизацией O2.
Также важным флагом является флаг -g линкера, который добавляет секцию с отладочной информацией в образ системы:
LDFLAGS += -N -g
его также можно удалить для уменьшения размера, но тогда отладка будет не доступна.
Очистка проекта
Сборка Embox происходит в несколько этапов. Можно выделить следующие большие этапы:
- Задание текущей конфигурации
- Создание графа модулей и генерация на их основе артефактов для сборки
- Сборка
Поэтому очистка тоже может "очищать" (приводить в состояние) проект до разных стадий сборки. Для этого используются три цели make:
- make distclean
- make cacheclean
- make clean
Цель clean просто удаляет папку build c собранными объектными и бинарными файлами. Для большинства случаем этого достаточно.
Цель cacheclean полностью выполняет цель clean (то есть удаляет папку build), но кроме этого удаляет папку mk/.cache
, в которой содержатся артефакты от разбора mybuild файлов (файлов описания модулей).
Цель distclean полностью приводит проект в первоначальное состояние. То есть удаляет рабочую конфигурацию, чистит все сгенерированные и скомпилированные файлы.
Полезные команды
Справка по командам сборки
Больше информации о возможностях командной строки при сборке можно получить с помощью make цели help:
$ make help
есть различные подразделы цели, например с помощью:
$ make help-mod
выводится справка по управлению модулями в текущей конфигурации
Usage: make mod-<INFO>
Print <INFO> info about modules:
list: list all modules included in build
brief-<module_name>: show brief informataion about module: dependencies, options,
source files
include-reason-<module_name>: show dependence subtree desribing why <module_name>
was included in build
Получение дизассемблера текущего образа
Файл с дизассебмлером можно получить с помощью команды:
$ make disasm
Получение графа модулей
Граф модулей в формате png:
$ make dot
появиться файл build/doc/embox.png.
Вам потребуется установить пакет graphviz
$ sudo apt install graphviz
Получение документации из комментариев в doxygen формате
Для генерации документации по API из комментариев в doxygen формате можно воспользоваться командой
$ make docsgen
появиться папка build/docs/html в которой в формате html находится сгенеренная документация
Вам потребуется пакет doxygen
$ sudo apt install doxygen
Управление модулями
Для получения списка всех модулей которые включены в текущую конфигурацию нужно выполнить команду:
$ make mod-list
Можно получить более подробную информацию по каждому модулю, например, список модулей включает embox.net.route
...
embox.net.route
...
получить по нему информацию можно с помощью команды
$ make mod-brief-embox.net.route
в результате появится информация о модуле
--- embox.net.route ---
Inclusion reason: as dependence
Depends:
embox.net.core
embox.mem.pool_ndebug
embox.util.DListDebug
Dependents:
embox.cmd.net.ping
embox.cmd.net.route
embox.net.af_inet
embox.net.ipv4
embox.net.tcp_sock
OptInsts:
route_table_size : 8
Sources:
src/net/l3/route.c
в которой можно узнать файлы которые включены в модуль, опции уже с установленными значениями, причину по которой включен модуль (напрямую из конфига или подтянулся по зависимостям) и зависимости самого модуля.
Порой нужно понять почему подключается тот или иной модуль. Для этого можно воспользоваться командой:
make include-reason-<module_name>
Например, для того же модуля embox.net.route:
$ make mod-include-reason-embox.net.route
embox.net.route: as dependence:
embox.cmd.net.ping: explicit
embox.cmd.net.route: explicit
embox.net.af_inet: explicit
embox.net.ipv4: explicit
embox.net.tcp_sock: explicit
#
Запуск
Загрузка на QEMU
Собранный образ можно запустить QEMU. Самый простой способ - выполнить скрипт ./scripts/qemu/auto_qemu:
$ sudo ./scripts/qemu/auto_qemu
Этот скрипт создает tuntab, для чего ему нужны права суперпользователя. Кроме того, в скрипте используется утилита ethtool. Установка для основанных на Debian систем:
$ sudo apt-get install ethtool
Если запуск прошел успешно, то на экране QEMU будут выводиться сообщения. После загрузки системы появится приглашение "embox>", теперь можно запускать команды на выполнение. Например, "help" выведет список доступных команд.
Для проверки соединения можно пропинговать интерфейс 10.0.2.16. Если соединение установлено, к терминалу можно подключиться по telnet.
Для выхода из Qemu нужно набрать последовательность ctrl + 'a', затем 'X'.
Отладка
Для работы в режиме отладки можно использовать тот же скрипт, передав ему в качестве параметров флаги -s -S -no-kvm, то есть:
$ sudo ./scripts/qemu/auto_qemu -s -S -no-kvm
После этого QEMU будет ожидать подключения gdb-клиентом.
Для отладки с помощью консольного отладчика В другом терминале нужно запустить gdb:
$ gdb ./build/base/bin/embox
...
(gdb) target extended-remote :1234
(gdb) continue
Cистема начнет загрузку.
В любой момент в терминале gdb можно нажать ctrl + 'c', посмотреть стек текущего потока (backtrace), установить точки останова (break <имя функции>, break <имя файла>:<номер строки>).
Примеры
Пример “Hello World”
Для добавления обычного примера команды выводящей сообщение “hello world” необходимо создать файл “hello.c” с исходным кодом на языке Си.
#include <stdio.h>
int main(int argc, char **argv) {
printf("Hello World!\n");
return 0;
}
И затем добавить описание модуля.
Добавление модуля
Все модули и интерфейсы системы описываются в my-файлах: файлах с расширением .my или имеющих имя Mybuild (без расширения). Структурно каждый my-файл содержит объявление пакета, которому принадлежат все определяемые в файле сущности, (опционально) список импортируемых имен из других пакетов, а также определения самих модулей и интерфейсов. К примеру, для добавления новой команды для встроенного в ядро командного интерпретатора создадим новый файл Hello.my со следующим содержимым:
package embox.cmd.tutorial
@AutoCmd
@Cmd(name="hello", help="Prints ‘Hello World’ string")
module hello {
source "hello.c"
}
В этом примере описывается простой модуль имеющий всего один атрибут (source) - файл с исходным кодом “hello.c”, которых будет скомпилирован и связан с ядром в случае добавления модуля в сборку. Аннотация @Cmd регистрирует модуль в системе как команду встроенного интерпретатора, позволяя, таким образом, запускать ее по имени “hello”. Необязательный параметр help содержит строку, которая будет выведана при запуске команды “help hello” (если модуль help также включен в сборку). Аннотация @AutoCmd позволяет использовать привычную функцию main() в качестве точки входа в программу.
Атрибуты модуля
Модуль hello достаточно примитивен и не определяет никаких внешних зависимостей или опций. Единственный его атрибут - source - определяет набор исходных файлов для компиляции. Опции Теперь модифицируем пример таким образом, чтобы строка приветствия задавалась в виде опции. Для этого используется атрибут option. Добавим модулю hello (файл Hello.my) строковую опцию greeting:
// ...
module hello {
// ...
option string greeting = "World"
}
И модифицируем файл исходного кода hello.c таким образом, чтобы команда после слова “Hello” выводила строку, содержащуюся в значении опции greeting:
#define GREETING OPTION_STRING_GET(greeting)
// ...
printf("Hello %s!\n", GREETING);
// ...
Включение в сборку
Для того, чтобы новый модуль оказался в результирующем образе ядра, его необходимо добавить в конфигурацию сборки, которая описывается в файле conf/mods.config:
package genconfig
configuration conf {
// ...
include embox.cmd.tutorial.hello
}
Для того, чтобы переопределить значение опции, в конфигурации следует задать новые значения для опций в скобках после указания модуля:
// ...
include embox.cmd.tutorial.hello(greeting="Everyone")
// ...
Как и многие атрибуты модуля, конфигурация также поддерживает добавление аннотаций. Наиболее часто используемой является аннотрация @Runlevel, позволяющая разбить загрузку системы на стадии и определить, на каком этапе загружать тот или иной модуль.
Как правило, на ранних стадиях (runlevel=0) загружаются драйверы устройств, необходимых для корректного функционирования системы (например, контроллер прерываний), основные компоненты системы, а также исполняются низкоуровневые тесты и процедуры самодиагностики. Следует отметить, что runlevel определяет лишь максимальную стадию, на которой должен быть запущен данный модуль. Таким образом, если какой-либо модуль, загружаемый на нулевой стадии, зависит от модуля, загружаемого позже, последний все-равно будет загружен раньше, т.е. на нулевой стадии.
Пример “Blinking led”
Библиотека libgpio
В Embox интерфейс GPIO предоставляется библиотекой libgpio. Для использование библиотеки в команде необходимо подключить заголовочный файл <drivers/gpio/gpio.h>
.
Библиотека имеет следующий интерфейс:
-
int gpio_setup_mode(unsigned short port, gpio_mask_t pins, int mode)
- функция задания режима работы вывода. Режим может быть одним из следующих:Некоторые базовые режимы (см.
<drivers/gpio/gpio.h>
):GPIO_MODE_OUTPUT
- режим выводаGPIO_MODE_INPUT
- режим вводаGPIO_MODE_INT_MODE_RISING
- режим прерыванияGPIO_MODE_ALTERNATE
- режим Alternate. При этом номер функции может быть выставлен с помощьюGPIO_ALTERNATE(num)
. То есть, в итоге режим будет видаGPIO_MODE_ALTERNATE | GPIO_ALTERNATE(num)
.
-
void gpio_set(unsigned short port, gpio_mask_t pins, char level)
- установить значение на выходе. Режим вывода должен бытьGPIO_MODE_OUTPUT
. -
void gpio_toggle(unsigned short port, gpio_mask_t pins)
- инвертировать значение на выходе. Режим вывода должен быть GPIO_MODE_OUTPUT. -
gpio_mask_t gpio_get(unsigned short port, gpio_mask_t pins)
- получить значение на входе. Результат представляется в виде маски. Режим вывода должен быть GPIO_MODE_INPUT. -
int gpio_irq_attach(unsigned short port, gpio_mask_t pins, irq_handler_t pin_handler, void *data)
- задание обработчика прерывания. Режим вывода должен бытьGPIO_MODE_INT_MODE_RISING
.
Простое мигание диодом
Данный пример демонстрирует исходный код простейшего приложения которое в цикле мигает светодиодом для платы STM32F4discovery. В данном разделе разбирается только исходный код на языке Си. Как добавить описание модуля и включить приложение в текущую конфигурацию описано в разделе "Пример “Hello World”"
Светодиод подключен к выводу 12 порта D в примере используются именно эти значения для простоты и понятности. Номера портов могут быть переданы как через аргументы командной строки, так и через опции модуля ( см. раздел "Опции модуля"), либо заданы для конкретной платы самостоятельно.
#include <unistd.h>
#include <drivers/gpio/gpio.h>
#define LED4_PIN (1 << 12)
int main(int argc, char *argv[]) {
int cnt;
gpio_setup_mode(GPIO_PORT_D, LED4_PIN, GPIO_MODE_OUTPUT);
gpio_set(GPIO_PORT_D, LED4_PIN, 0);
for(cnt = 0; cnt < 100; cnt++) {
sleep(1);
gpio_toggle(GPIO_PORT_D, LED4_PIN);
}
return 0;
}
Перед использованием вывод нужно проинициализировать, задать режим выхода, с помощью функции gpio_setup_mode()
. Затем устанстанавливается начальное значение 0.
Далее идет цикл до 100 переключений, в котором происходит задержка на 1 секунду и инвертируется значение на выходе.
Переключение светодиода как реакция на нажатие кнопки
Данный пример демонстрирует исходный код приложения которое переключает состояние сведодиода по нажатию кнопки для платы STM32F4discovery.
Как и в случае с предыдущим примером состояние выводов определено схематикой, задано константами прямо в коде и может быть изменено описанными способами.
#include <unistd.h>
#include <kernel/irq.h>
#include <drivers/gpio/gpio.h>
#define LED4_PIN (1 << 12)
#define USER_BUTTON_PIN (1 << 0)
irq_return_t user_button_hnd(unsigned int irq_nr, void *data) {
gpio_toggle(GPIO_PORT_D, LED4_PIN);
return IRQ_HANDLED;
}
int main(int argc, char *argv[]) {
gpio_setup_mode(GPIO_PORT_D, LED4_PIN, GPIO_MODE_OUTPUT);
gpio_setup_mode(GPIO_PORT_A, USER_BUTTON_PIN,
GPIO_MODE_INT_MODE_RISING);
gpio_set(GPIO_PORT_D, LED4_PIN, 0);
if (0 > gpio_irq_attach(GPIO_PORT_A, USER_BUTTON_PIN,
user_button_hnd, NULL)) {
return -1;
}
sleep(30);
return 0;
}
Отличие от примера с простым миганием светодиодом заключается в использовании еще одного вывода, который переключен в режим прерывания с помощью функции gpio_setup_mode()
. А с помощью функции gpio_irq_attach()
на событие по изменению состояния на выводе подключенном к кнопке, повешен обработчик. В обработчике происходит инвертирование значения на выходе светодиода.
Пример добавления драйвера
Пример стороннего приложения
Данный пример демонстрирует как подключить к сборке сторонний проект с открытым кодом.
Для примера разберем добавление редактора 'nano'
Как и в случае с другими приложениями, необходимо добавить описание модуля
package third_party.cmd
@App
@AutoCmd
@Build(stage=2,script="$(EXTERNAL_MAKE)")
@Cmd(name = "nano",
help = "Text editor",
man = '''
NAME
nano - Nano's ANOther editor
SYNOPSIS
nano [OPTIONS] [[+LINE,COLUMN] FILE]...
AUTHORS
Ilia Vaprol - Adaptation for Embox
''')
module nano {
source "^BUILD/extbld/^MOD_PATH/install/nano.o"
@NoRuntime depends embox.compat.posix.regex
depends embox.compat.posix.LibCurses
}
Отличием описание данного модуля от описания обычного приложения являются два аспекта:
- Наличие аннотации @Build позволяющего задать некоторые детали процесса сборки модуля
- В качестве файла исходного кода используется объектный файл
Аннотация @Build
Аннотация @Build позволяет задавать некоторые детали процесса сборки модуля к которому применена данная аннотация.
Стадия сборки
Сборка Embox происходит в несколько стадий. Некоторые модули требуют предварительно подготовленного окружения. Например, если модулю требуются стандартная библиотека и собранные библиотеки из внешних репозиторий. Опция stage позволяет задать стадию сборки. Нулевая стадия включает в себя сборку стандартной библиотеки и ядра ОС. Начиная с первой стадии используется компилятор в котором в качестве библиотек используются собранные библиотеки с нулевой стадии. Вторая стадия используется для приложений которым требуется какое то собранное окружение.
Задание внешнего скрипта сборки
С помощью аннотации @Build можно задавать свои правила для сборки. В том числе можно указать, часто используемый Makefile который позволяет задавать три цели (configure, build, install) (по аналогии с configure, make, make install). Для использования в качестве опции к аннотации нужно использовать
script="$(EXTERNAL_MAKE)"
При использовании данного скрипта происходит выполнение makefile находящегося в данной директории.
Для того чтобы использовать разработанные скрипты упрощающие включение стороннего приложение можно использовать скрипт EXTBLD_LIB. Для этого необходимо включить эту библиотеку в makefile
...
include $(EXTBLD_LIB)
...
При подключении происходит скачивание архива, проверка контрольной суммы md5, накладывание патчей и затем выполнение целей
$(CONFIGURE) :
...
$(BUILD) :
...
$(INSTALL) :
...
для использования данного скрипта необходимо задать несколько переменных:
- PKG_SOURCES - название пакета
- PKG_VER - версия пакета
- PKG_SOURCES - url для скачивания
- PKG_MD5 - контрольная сумма md5
- PKG_PATCHES - список файлов с патчами для наложения на оригинальные исходники
PKG_NAME := nano
PKG_VER := 2.2.6
PKG_SOURCES := http://www.nano-editor.org/dist/v2.2/$(PKG_NAME)-$(PKG_VER).tar.gz
PKG_MD5 := 03233ae480689a008eb98feb1b599807
PKG_PATCHES := pkg_patch.txt
include $(EXTBLD_LIB)
$(CONFIGURE) :
...
цель $(CONFIGURE), должна перейти с директорию с распакованными и пропатчеными исходниками и затем вызвать configure с требуемыми параметрами:
$(CONFIGURE) :
export EMBOX_GCC_LINK=full; \
cd $(PKG_SOURCE_DIR) && ( \
./configure --host=$(AUTOCONF_TARGET_TRIPLET) \
--target=$(AUTOCONF_TARGET_TRIPLET) \
--enable-tiny \
--disable-shared \
--disable-static \
--disable-largefile \
--disable-rpath \
--disable-nls \
--disable-extra \
--disable-browser \
--disable-help \
--disable-justify \
--disable-mouse \
--disable-operatingdir \
--disable-speller \
--disable-tabcomp \
--disable-wrapping \
--disable-wrapping-as-root \
--disable-color \
--disable-multibuffer \
--disable-nanorc \
--disable-glibtest \
CC=$(EMBOX_GCC) \
)
touch $@
цель $(BUILD) должна перейти в папку с исходниками и вызвать make если требуется передать дополнительные флаги.
$(BUILD) :
cd $(PKG_SOURCE_DIR) && ( \
$(MAKE) MAKEFLAGS='$(EMBOX_IMPORTED_MAKEFLAGS)'; \
)
touch $@
цель $(INSTALL), должна скопировать результаты сборки в папку для дальнейшей обработки в составе Embox (линковка в конечном образе или использование заголовочных файлов)
$(INSTALL) :
cp $(PKG_SOURCE_DIR)/src/nano $(PKG_INSTALL_DIR)/nano.o
touch $@
Включение объектных файлов
В конечный образ Embox включаются только те файлы которые указаны в описании модуля, а не все файлы которые получились в процессе сборки стороннего проекта. Таким образом объектный файл представляющий из себя приложение nano и скопированный в цели $(INSTALL) описанного выше Makefile не будет использоваться, если это не описано в модуле с помощью ключевого слова source:
...
module nano {
source "^BUILD/extbld/^MOD_PATH/install/nano.o"
...
}
В окончательный образ Embox включается бинарный код команды nano и если требуется, линкуется с конечным образом
Драйвера
раздел в разработке
Описание структуры драйверов
раздел в разработке
Типы драйверов
раздел в разработке
Диагностический интерфейс
раздел в разработке
Контроллеры прерываний
раздел в разработке
Таймеры
раздел в разработке
Последовательные порты (ttys)
раздел в разработке
PCI
раздел в разработке
Ethernet карты
раздел в разработке
Графические карты (framebuffer)
раздел в разработке
devfs
раздел в разработке
Блочные устройста
раздел в разработке
Символьные устройства
раздел в разработке
ARM архитектура
Embox поддерживает большинство ARM подархитектур: ARM7, ARM9, ARM-Cortex-M, ARM-Cortex-A. для работы со всеми платформами необходимо установить компилятор arm-none-eabi-gcc, как описано в разделе "установка кросс-компилятора".
Embox поддерживает несколько популярных аппаратных платформ на базе процессоров с ARM архитектурой:
- STM32 MCU Eval Tools
- Raspberry Pi
- BeagleBoard
Поддержка эмулятора QEMU
Embox поддерживает запуск на QEMU со следующими архитектурами:
- qemu-system-i386
- qemu-system-arm
- qemu-system-mips
- qemu-system-ppc
- qemu-system-microblaze
- qemu-system-sparc
Для каждой архитектуры необходимо установить соответствующий кросс-компилятор
Для каждой из поддерживаемых архитектур необходимо сконфигурировать Embox
$ make confload-<ARCH>/qemu
где
Для запуска можно выполнить скрипт:
$ ./scripts/qemu/auto_qemu
данный скрипт разберет содержимое конфигурационных файлов в папке conf/ и запустит qemu c нужными параметрами.
Поддерживаемые платформы
Embox поддерживает несколько процессорных архитектур: ARM, x86, SPARC, DLX (Microblaze), MIPS, PPC (PowerPC), E2k (Elbrus). Для каждой архитектуры может быть поддержано несколько аппаратных платформ, как реальных плат, так и виртуальных. Для каждой архитектуры необходимо установить кросс-компилятор, как описано в разделе "Установка кросс-компилятора" и отладчик gdb, который обычно входит в состав кросс-компилятора при установке. Процесс запуска и отладки для различных платформ может отличаться.
x86 архитектура
Embox поддерживает 32 разрядную архитектуры x86 и драйвера для различного рода периферии. Для работы на Linux в качестве кросс-компилятора используется gcc.
Embox поддерживает:
- qemu-system-i386
- Загрузка через GRUB
- BifferBoard