Вся правда об ОСРВ. Статья #15. Разделы памяти: службы и структуры данных

в 10:57, , рубрики: api, rtos, выделение памяти, микроконтроллеры, освобождение памяти, осрв, программирование микроконтроллеров, пул разделов, раздел памяти, распределение памяти, служебные вызовы, управление памятью

Вся правда об ОСРВ. Статья #15. Разделы памяти: службы и структуры данных - 1

В этой статье мы продолжаем рассматривать разделы памяти ОСРВ.

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

Получение информации о пуле разделов

Этот служебный вызов позволяет получить частичную информацию о пуле разделов. Реализация Nucleus SE отличается от Nucleus RTOS тем, что в ней возвращается меньше информации, поскольку присваивание объектам имен и запросы на приостановку не поддерживаются, и приостановка задачи невозможна.

Вызов получения информации о пуле разделов в Nucleus RTOS

Прототип вызова:

STATUS NU_Partition_Pool_Information(NU_PARTITION_POOL *pool, CHAR *name, VOID **start_address, UNSIGNED *pool_size, UNSIGNED *partition_size, UNSIGNED *available, UNSIGNED *allocated, OPTION *suspend_type, UNSIGNED *tasks_waiting, NU_TASK **first_task);

Параметры:

pool – указатель на пул разделов, о котором запрошена информация;
name – указатель на 8-символьную область назначения для имени пула разделов; включает место для терминирующего нуля;
start_address – указатель на переменную, которая получает указатель на начало области данных пула разделов;
pool_size – указатель на переменную, которая получает размер пула разделов (в байтах);
partition_size – указатель на переменную, которая получает размер разделов в данном пуле;
available – указатель на переменную, которая получает число доступных в данный момент разделов в данном пуле;
allocated – указатель на переменную, которая получает число используемых в данный момент разделов в данном пуле;
suspend_type – указатель на переменную для получения типа приостановки задачи; допустимые типы приостановки: NU_FIFO и NU_PRIORITY;
tasks_waiting – указатель на переменную, которая получает количество приостановленных задач в данном пуле разделов;
first_task – указатель на указатель задачи, в котором размещен указатель первой приостановленной задачи.

Возвращаемое значение:

NU_SUCCESS – вызов выполнен успешно;
NU_INVALID_POOL – некорректный указатель на пул разделов.

Вызов получения информации о пуле разделов в Nucleus SE

Прототип вызова:

STATUS NUSE_Partition_Pool_Information(NUSE_PARTITION_POOL pool, ADDR *start_address, U32 *pool_size, U16 *partition_size, U8 *available, U8 *allocated, U8 *tasks_waiting, NUSE_TASK *first_task)

Параметры:

pool – индекс пула разделов, о котором запрошена информация;
start_address – указатель на переменную, которая получает указатель на начало области данных пула разделов;
pool_size – указатель на переменную, которая получает размер пула разделов (в байтах);
partition_size – указатель на переменную, которая получает размер разделов в данном пуле;
available – указатель на переменную, которая получает число доступных в данный момент разделов в данном пуле;
allocated – указатель на переменную, которая получает число используемых в данный момент разделов в данном пуле;
tasks_waiting – указатель на переменную, которая получает количество приостановленных задач в данном пуле разделов (если приостановка задач отключена, возвращается 0);
first_task – указатель на переменную типа NUSE_TASK, которая получает индекс первой приостановленной задачи (если приостановка задач отключена, возвращается 0).

Возвращаемое значение:

NUSE_SUCCESS – вызов выполнен успешно;
NUSE_INVALID_POOL – некорректный индекс пула разделов;
NUSE_INVALID_POINTER – один или несколько переданных указателей некорректны.

Реализация получения информации и пуле разделов в Nucleus SE

Реализация такого вызова API проста в исполнении:

Вся правда об ОСРВ. Статья #15. Разделы памяти: службы и структуры данных - 2

Функция возвращает статус пула разделов. Затем, если активирована блокировка вызовов API, возвращаются число ожидающих задач и индекс первой из них (в противном случае эти параметры устанавливаются как 0).

Получение количества пулов разделов

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

Вызов получения количества пулов разделов в Nucleus RTOS

Вызов поддерживает основной функционал API Nucleus RTOS.

Прототип вызова:

UNSIGNED NU_Established_Partition_Pools(VOID);

Параметры:

Отсутствуют.

Возвращаемое значение:

Количество созданных в приложении пулов разделов.

Вызов получения количества пулов разделов в Nucleus SE

Этот служебный вызов поддерживает основной функционал API Nucleus RTOS.

Прототип вызова:

U8 NUSE_Partition_Pool_Count(void);

Параметры:

Отсутствуют

Возвращаемое значение:

Количество созданных в приложении пулов разделов.

Реализация

Реализация данного вызова API крайне проста: возвращается значение #define символа NUSE_PARTITION_POOL_NUMBER.

Структуры данных

Как и все другие объекты Nucleus SE, пулы разделов используют массивы структуры и в ПЗУ, и в ОЗУ, число которых зависит от числа пулов, заданного в настройках.

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

Структура данных ядра, размещаемых в ОЗУ

К таким структурам данных относятся:

NUSE_Partition_Pool_Partition_Used[] – массив типа U8, имеющий одну запись для каждого сконфигурированного пула разделов, содержащий счетчик используемых в данный момент пулов;
NUSE_Partition_Pool_Blocking_Count[] – массив типа U8, содержащий счетчик заблокированных задач в каждом пуле разделов. Данный массив существует, если возможна блокировка вызова API.

Такие структуры данных инициализируются нулями с помощью NUSE_Init_ Partition_Pool() при запуске Nucleus SE. Это логично, поскольку делает каждый раздел в каждом пуле неиспользуемым (свободным). В следующей статье будет представлено полное описание процедур запуска в Nucleus SE.

Ниже приведены описания структур данных в файле nuse_init.c.

Вся правда об ОСРВ. Статья #15. Разделы памяти: службы и структуры данных - 3

Пользовательские данные в ОЗУ

Пользователю необходимо выделить область в ОЗУ для хранения данных для каждого пула раздела. Объем области в ОЗУ должен соответствовать объему сконфигурированных разделов (см. «Данные в ПЗУ» ниже) с дополнительным байтом для каждого раздела в пуле. В каждом разделе области данных предшествует один байт состояния.

Данные в ПЗУ

К ним относятся:

NUSE_Partition_Pool_Data_Address[] – массив типа ADDR, с одной записью для каждого сконфигурированного пула разделов, содержащий адрес начала области хранения данных;
NUSE_Partition_Pool_Partition_Number[]– массив типа U8 с одной записью для каждого сконфигурированного пула разделов, содержащий информацию о количестве разделов в пуле;
NUSE_Partition_Pool_Partition_Size[] – массив типа U16 с одной записью для каждого сконфигурированного пула разделов, содержащий размер разделов для пулов.

Такие структуры данных объявляются и инициализируются (статически) в nuse_config.c:

Вся правда об ОСРВ. Статья #15. Разделы памяти: службы и структуры данных - 4

Объем памяти (Data footprint) для пула разделов

Как и для всех объектов ядра в Nucleus SE, объем памяти, необходимой для пулов разделов, предсказуем.

Объем ПЗУ (в байтах) для всех пулов разделов приложения может быть вычислен следующим образом:

NUSE_PARTITION_POOL_NUMBER * (sizeof(ADDR) + 2)

Объем данных ядра в ОЗУ для всех пулов разделов приложения при активированной блокировке вызовов API занимает всего 2 байта на один пул разделов, при не активированной блокировке – 1 байт.

Объем памяти для хранения пользовательских данных в ОЗУ варьируется для каждого пула разделов, хотя, как уже говорилось, для пула с индексом n его можно вычислить как:

NUSE_Partition_Pool_Partition_Number[n] *
(NUSE_Partition_Pool_Partition_Size[n] + 1)

Нереализованные вызовы API

Три вызова API для пулов разделов, реализованные в Nucleus RTOS, не поддерживаются в Nucleus SE.

Создание пула разделов (Create Partition Pool)

Этот вызов API создает пул разделов. В Nucleus SE в нем нет необходимости, поскольку задачи создаются статично.

Прототип вызова:

STATUS NU_Create_Partition_Pool(NU_PARTITION_POOL *pool, CHAR *name, VOID *start_address, UNSIGNED pool_size, UNSIGNED partition_size, OPTION suspend_type);

Параметры:

pool – указатель на пользовательский блок управления пулами разделов; используется в качестве дескриптера (“handle”) пула разделов в других вызовах API;
name – указатель на имя пула разделов, 7-символьную строку с терминирующий нулем;
start_address – задает начальный адрес для области памяти пула разделов;
pool_size – общий объем области памяти в байтах;
partition_size – объем памяти в байтах для каждого раздела в пуле. Сверх этого выделяется дополнительно небольшой объем памяти, связанный с каждым разделом, что реализуется благодаря двум используемым указателям данных.
suspend_type – определяет, как задачи приостанавливаются в пуле разделов; допустимые варианты параметра: NU_FIFO и NU_PRIORITY.

Возвращаемое значение:

NU_SUCCESS – указывает на успешное завершение вызова;
NU_INVALID_POOL – указывает на нулевое значение блока управления пулом разделов (NULL);
NU_INVALID_MEMORY – указывает на нулевое значение области памяти, определяемой start_ address (NULL);
NU_INVALID_SIZE – указывает, что размер раздела либо равен 0, либо больше памяти, выделенной для раздела;
NU_INVALID_SUSPEND – некорректное значение suspend_type.

Удаление пула разделов

Этот вызов API удаляет ранее созданный пул разделов. В Nucleus SE в нем нет необходимости, поскольку объекты создаются статически и не могут быть удалены.

Прототип вызова:

STATUS NU_Delete_Partition_Pool(NU_PARTITION_POOL *pool);

Параметры:

pool – указатель на блок управления пулом разделов;

Возвращаемое значение:

NU_SUCCESS – указывает на успешное завершение вызова;
NU_INVALID_POOL – указывает на некорректное значение указателя пула разделов;

Указатели пула разделов

Этот вызов API строит последовательный список указателей на все пулы разделов в системе. В Nucleus SE в этом нет необходимости, поскольку объекты идентифицируются индексом, а не указателем.

Прототип вызова:

UNSIGNED NU_Partition_Pool_Pointers(NU_PARTITION_POOL **pointer_list, UNSIGNED maximum_pointers);

Параметры:

pointer_list – указатель на массив указателей NU_PARTITION_POOL; массив заполняется указателями на сконфигурированные пулы в системе;
maximum_pointers – максимальное количество указателей, которые можно поместить в массив.

Возвращаемое значение:

Количество указателей NU_PARTITION_POOL, помещенных в массив.

Совместимость с Nucleus RTOS

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

Идентификаторы объектов

В Nucleus RTOS все объекты описаны структурами данных (блоками управления), имеющими определенный тип. Указатель на этот блок управления используется как идентификатор для пула разделов. Я решил, что в Nucleus SE требуется другой подход для более эффективного использования памяти. Все объекты ядра описываются несколькими таблицами в ОЗУ и/или ПЗУ. Размеры этих таблиц определяются количеством конфигурируемых типов всех объектов. Идентификатор для конкретного объекта – индекс в этих таблицах. Поэтому я определил NUSE_PARTITION_POOL эквивалентным U8, после чего переменная (не указатель) данного типа служит в качестве идентификатора задачи. С этой небольшой несовместимостью легко разобраться, если код перенесен с или на Nucleus RTOS. Идентификаторы объектов обычно хранятся и передаются без изменений.

Nucleus RTOS также поддерживает присваивание имен пулам разделов. Эти имена используются только при отладке. Я исключил их из Nucleus SE для экономии памяти.

Количество разделов и их объем

В Nucleus RTOS пул разделов конфигурируется с учетом общего объема пула и объема разделов (которые несут с собой еще 2 указателя). Эти параметры определены как UNSIGNED (примерно, 32 бита). В Nucleus SE пул разделов конфигурируется с учетом объема раздела (для которого добавлен дополнительный байт) и общего количества разделов. Эти параметры определены как U16 и U8 соответственно.

Нереализованные вызовы API

Nucleus RTOS поддерживает 7 вызовов для работы с пулами разделов, 3 из которых не реализованы в Nucleus SE. Более подробно об этих вызовах и о причинах их исключения изложено выше.

Следующая статья будет посвящена сигналам.

Об авторе: Колин Уоллс уже более тридцати лет работает в сфере электронной промышленности, значительную часть времени уделяя встроенному ПО. Сейчас он — инженер в области встроенного ПО в Mentor Embedded (подразделение Mentor Graphics). Колин Уоллс часто выступает на конференциях и семинарах, автор многочисленных технических статей и двух книг по встроенному ПО. Живет в Великобритании. Профессиональный блог Колина, e-mail: colin_walls@mentor.com.

Автор: EasyLy

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js