В этой статье продолжается обзор почтовых ящиков, начатый в предыдущей статье серии «Вся правда об ОСРВ».
Nucleus RTOS имеет четыре вызова API, которые предоставляют вспомогательные функции, связанные с почтовыми ящиками: сброс почтового ящика, получение информации о почтовом ящике, получение количества почтовых ящиков в приложении и получение указателей на все почтовые ящики в приложении. Первые три из этих функций реализованы в Nucleus SE.
Этот служебный вызов API сбрасывает почтовый ящик в его начальное, неиспользуемое состояние. Сообщение, хранящееся в почтовом ящике, будет утеряно. Любые приостановленные на почтовом ящике задачи возобновятся с кодом возврата NUSE_MAILBOX_WAS_RESET.
Вариант кода функции NUSE_Mailbox_Reset (после проверки параметров) выбирается при помощи условной компиляции, в зависимости от того, активирована поддержка блокировки (приостановки) задач или нет. Мы рассмотрим оба эти варианта.
Если блокировка не активирована, код этой функции API довольно прост. Почтовый ящик помечается как неиспользуемый путем присвоения параметру NUSE_Mailbox_Status[] значения FALSE.
while (NUSE_Mailbox_Blocking_Count[mailbox] != 0)
{
U8 index; /* check whether any tasks are blocked */
/* on this mailbox */
for (index=0; index<NUSE_TASK_NUMBER; index++)
{
if ((LONIB(NUSE_Task_Status[index]) ==
NUSE_MAILBOX_SUSPEND)
&& (HINIB(NUSE_Task_Status[index]) == mailbox))
{
NUSE_Task_Blocking_Return[index] =
NUSE_MAILBOX_WAS_RESET;
NUSE_Task_Status[index] = NUSE_READY;
break;
}
}
NUSE_Mailbox_Blocking_Count[mailbox]--;
}
#if NUSE_SCHEDULER_TYPE == NUSE_PRIORITY_SCHEDULER
NUSE_Reschedule(NUSE_NO_TASK);
#endif
Каждой приостановленной задаче на почтовом ящике присваивается статус «готова» с кодом возврата NUSE_MAILBOX_WAS_RESET. После того, как этот процесс завершен, если используется планировщик Priority, выполняется служебный вызов NUSE_Reschedule(), так как одна или несколько задач с более высоким приоритетом могли стать готовыми и ждут разрешения на выполнение.
Этот служебный вызов предоставляет набор информации о почтовом ящике. Реализация этого вызова в Nucleus SE отличается от Nucleus RTOS тем, что она возвращает меньше информации, так как именование объектов и порядок приостановки не поддерживается, а приостановка задач может быть отключена.
mailbox – указатель на блок управления почтовым ящиком;
name – указатель на 8-символьную область для имени почтового ящика. В эту область включен и терминирующий нулевой байт;
suspend_type – указатель на переменную, в которой хранится тип приостановки задачи. Может принимать значения NU_FIFO и NU_PRIORITY;
message_present – указатель на переменную, которая примет значение NU_TRUE или NU_FALSE, в зависимости от того, заполнен почтовый ящик, или нет;
tasks_waiting – указатель на переменную, которая примет количество приостановленных на этом почтовом ящике задач;
first_task – указатель на указатель задачи, который примет указатель на первую приостановленную задачу.
mailbox – индекс почтового ящика, о котором запрашивается информация;
message_present – указатель на переменную, которая примет значение TRUE или FALSE, в зависимости от того, заполнен почтовый ящик, или нет;
tasks_waiting – указатель на переменную, которая примет количество приостановленных на этом почтовом ящике задач (ничего не возвращается, если приостановка задач отключена);
first_task – указатель на переменную типа NUSE_TASK, которая примет индекс первой приостановленной задачи (ничего не возвращается, если приостановка задач отключена).
*message_present = NUSE_Mailbox_Status[mailbox];
#if NUSE_BLOCKING_ENABLE
*tasks_waiting = NUSE_Mailbox_Blocking_Count[mailbox];
if (NUSE_Mailbox_Blocking_Count[mailbox] != 0)
{
U8 index;
for (index=0; index<NUSE_TASK_NUMBER; index++)
{
if ((LONIB(NUSE_Task_Status[index]) ==
NUSE_MAILBOX_SUSPEND)
&& (HINIB(NUSE_Task_Status[index]) == mailbox))
{
*first_task = index;
break;
}
}
}
else
{
*first_task = 0;
}
#else
*tasks_waiting = 0;
*first_task = 0;
#endif
return NUSE_SUCCESS;
Функция возвращает статус почтового ящика. Затем, если служебные вызовы для блокировки задач активированы, возвращается количество приостановленных задач и индекс первой из них (в противном случае, этим параметрам присваивается значение 0).
Этот служебный вызов возвращает количество почтовых ящиков в приложении. В то время как в Nucleus RTOS их количество со временем может меняться, а возвращаемое значение будет показывать текущее количество почтовых ящиков, в Nucleus SE количество почтовых ящиков устанавливается на этапе сборки и не может изменяться.
Почтовые ящики используют два или три массива структур данных (все они находятся в ОЗУ), являющихся, как и другие объекты Nucleus SE, набором таблиц, размер которых зависит от количества сконфигурированных почтовых ящиков и их параметров.
Настоятельно рекомендую, чтобы код приложения не использовал прямой доступ к этим структурам данных, а обращался к ним через предоставляемые функции API. Это позволяет избежать несовместимости с будущими версиями Nucleus SE и нежелательных побочных эффектов, а также упростит портирование приложения на Nucleus RTOS. Ниже приведен подробный обзор структур данных для лучшего понимания принципа работы кода служебных вызовов и для отладки.
RAM ADDR NUSE_Mailbox_Data[NUSE_MAILBOX_NUMBER];
RAM U8 NUSE_Mailbox_Status[NUSE_MAILBOX_NUMBER];
#if NUSE_BLOCKING_ENABLE
RAM U8 NUSE_Mailbox_Blocking_Count[NUSE_MAILBOX_NUMBER];
#endif
Объем данных в ОЗУ для всех почтовых ящиков в приложении (в байтах) при активированных вызовах API для блокировки задач можно вычислить следующим образом:
NUSE_MAILBOX_NUMBER * (sizeof(ADDR) +2)
Этот служебный вызов API создает почтовый ящик. В Nucleus SE в нем нет необходимости, так как почтовый ящики создаются статически.
mailbox – указатель на блок управления почтовым ящиком, предоставляемый пользователем; используется для управления почтовыми ящиками в других вызовах API;
name – указатель на 7-символьное имя почтового ящика с нулевым терминирующим байтом;
suspend_type – указывает принцип приостановки задачи на почтовом ящике. Может принимать значения NU_FIFO и NU_PRIORITY, которые означают принцип FIFO (First-In-First-Out) или принцип приоритета приостановки задач, соответственно.
Этот служебный вызов API удаляет ранее созданный почтовый ящик. В Nucleus SE в этом нет необходимости, так как почтовые ящики создаются статически и не могут быть удалены.
Этот вызов API составляет последовательный список из указателей на все почтовые ящики в системе. В Nucleus SE в нем нет необходимости, так как почтовые ящики идентифицируются при помощи простого индекса, а не указателя.
Этот служебный вызов передает сообщение всем задачам, ожидающим сообщений от определенного почтового ящика. В Nucleus SE этот служебный вызов не реализован, так как он добавил бы излишней сложности.
Как и в случае со всеми другими объектами Nucleus SE, моей целью было обеспечить максимальную совместимость кода приложений с Nucleus RTOS. Почтовые ящики не являются исключением и, с точки зрения пользователя, они реализованы также, как и в Nucleus RTOS. Есть и определенная несовместимость, которую я посчитал допустимой с учетом того, что в результате код станет более понятным и более эффективным с точки зрения объема требуемой памяти. В остальном вызовы API Nucleus RTOS могут быть практически напрямую перенесены на Nucleus SE.
В Nucleus RTOS все объекты описываются структурами данных (блоками управления), имеющими определенный тип. Указатель на этот блок управления служит идентификатором почтового ящика. Я решил, что в Nucleus SE для эффективного использования памяти необходим другой подход: все объекты ядра описываются набором таблиц в ОЗУ и/или ПЗУ. Размер этих таблиц определяется количеством сконфигурированных объектов каждого типа. Идентификатор конкретного объекта – индекс в этой таблице. Таким образом, я определил NUSE_MAILBOX в качестве эквивалента U8, переменная (а не указатель) этого типа служит идентификатором почтового ящика. С этой небольшой несовместимостью легко справиться, если код портируется с Nucleus SE на Nucleus RTOS и наоборот. Обычно над идентификаторами объектов не выполняются никакие операции, кроме перемещения и хранения.
Nucleus RTOS также поддерживает присвоение имен почтовым ящикам. Эти имена используются только при отладке. Я исключил их из Nucleus SE, чтобы сэкономить память.
В Nucleus RTOS сообщение почтового ящика состоит из четырех 32-битных слов. В Nucleus SE я решил уменьшить это значение до одной переменной типа ADDR. Это изменение приводит к значительной экономии памяти и уменьшению времени выполнения задач. Оно также говорит о том, что привычным применением почтового ящика является пересылка информации от одной задаче к другой. Такая несовместимость не вызовет больших проблем при портировании приложений на Nucleus RTOS. Nucleus SE можно модифицировать, если необходим другой формат сообщения.
Nucleus RTOS поддерживает девять служебных вызовов для работы с почтовыми ящиками. Из них четыре не реализованы в Nucleus SE. Детали этих вызовов, а также причины, почему они были исключены из Nucleus SE, описаны выше.
В следующей статье будут рассматриваться очереди.
Об авторе: Колин Уоллс уже более тридцати лет работает в сфере электронной промышленности, значительную часть времени уделяя встроенному ПО. Сейчас он – инженер в области встроенного ПО в Mentor Embedded ( подразделение Mentor Graphics). Колин Уоллс часто выступает на конференциях и семинарах, автор многочисленных технических статей и двух книг по встроенному ПО. Живет в Великобритании. Профессиональный блог Колина: https://blogs.mentor.com/colinwalls/, e-mail: colin_walls@mentor.com