В этой статье продолжается обзор семафоров.
Вспомогательные службы семафоров
Nucleus RTOS имеет четыре вызова API, предоставляющие функционал, связанный с семафорами: сброс семафора, получение информации о семафоре, получение количества семафоров в приложении и получение указателей на все семафоры в приложении. Первые три из них реализованы в Nucleus SE.
Предыдущие статьи серии:
Статья #19. Семафоры: введение и базовые службы
Статья #18. Группы флагов событий: вспомогательные службы и структуры данных
Статья #17. Группы флагов событий: введение и базовые службы
Статья #16. Сигналы
Статья #15. Разделы памяти: службы и структуры данных
Статья #14. Разделы памяти: введение и базовые службы
Статья #13. Структуры данных задач и неподдерживаемые вызовы API
Статья #12. Службы для работы с задачами
Статья #11. Задачи: конфигурация и введение в API
Статья #10. Планировщик: дополнительные возможности и сохранение контекста
Статья #9. Планировщик: реализация
Статья #8. Nucleus SE: внутреннее устройство и развертывание
Статья #7. Nucleus SE: введение
Статья #6. Другие сервисы ОСРВ
Статья #5. Взаимодействие между задачами и синхронизация
Статья #4. Задачи, переключение контекста и прерывания
Статья #3. Задачи и планирование
Статья #2. ОСРВ: Структура и режим реального времени
Статья #1. ОСРВ: введение.
Сброс семафора
Этот вызов API сбрасывает семафор в его начальное, неиспользуемое состояние. Данная функция API необычна по сравнению с функциями других объектов ядра, так как несмотря на то, что она выполняет сброс, она не просто устанавливает счетчик в начальное значение, а в вызове передается новое начальное значение счетчика. Любая задача, которая была приостановлена на семафоре, возобновляется и возвращает код NUSE_SEMAPHORE_WAS_RESET в Nucleus SE, а в Nucleus RTOS – NU_SEMAPHORE_RESET.
Вызов для сброса семафора в Nucleus RTOS
Прототип служебного вызова:
STATUS NU_Reset_Semaphore(NU_SEMAPHORE *semaphore, UNSIGNED initial_count);
Параметры:
semaphore – указатель на предоставленный пользователем блок управления семафором;
initial_count – значение, в которое будет установлен семафор.
Возвращаемое значение:
NU_SUCCESS – вызов был успешно завершен;
NU_INVALID_SEMAPHORE – некорректный указатель на семафор.
Вызов для сброса семафора в Nucleus SE
Этот вызов API поддерживает основной функционал Nucleus RTOS API.
Прототип служебного вызова:
STATUS NUSE_Semaphore_Reset(NUSE_SEMAPHORE semaphore, U8 initial_count);
Параметры:
semaphore – индекс (ID) сбрасываемого семафора;
initial_count – значение, в которое будет установлен семафор.
Возвращаемое значение:
NUSE_SUCCESS – вызов был успешно завершен;
NUSE_INVALID_SEMAPHORE – некорректный индекс семафора.
Реализация сброса семафора в Nucleus SE
Основная задача функции API NUSE_Semaphore_Reset() – установить соответствующий элемент NUSE_Semaphore_Counter[] в указанное значение (после проверки параметров).
Если блокировка задач активирована, для разблокировки задач необходим следующий код:
while (NUSE_Semaphore_Blocking_Count[semaphore] != 0)
{
U8 index; /* check whether any tasks are blocked */
/* on this semaphore */
for (index=0; index<NUSE_TASK_NUMBER; index++)
{
if ((LONIB(NUSE_Task_Status[index]) ==
NUSE_SEMAPHORE_SUSPEND)
&& (HINIB(NUSE_Task_Status[index]) == semaphore))
{
NUSE_Task_Blocking_Return[index] =
NUSE_SEMAPHORE_WAS_RESET;
NUSE_Task_Status[index] = NUSE_READY;
break;
}
}
NUSE_Semaphore_Blocking_Count[semaphore]--;
}
#if NUSE_SCHEDULER_TYPE == NUSE_PRIORITY_SCHEDULER
NUSE_Reschedule(NUSE_NO_TASK);
#endif
Каждая приостановленная на семафоре задача помечается как «готовая», а код приостановки задачи возвращает NUSE_SEMAPHORE_WAS_RESET. После того, как этот процесс завершен, если используется планировщик Priority, вызов инициализирует NUSE_Reschedule(), так как одна или несколько задач с более высоким приоритетом могли перейти в готовое состояние и ожидают возобновления.
Информация о семафоре
Этот служебный вызов возвращает информацию о семафоре. Реализация этого вызова в Nucleus SE отличается от Nucleus RTOS тем, что возвращается меньше информации, т. к. именование объектов и порядок приостановки не поддерживается, а сама приостановка задач может быть отключена.
Вызов для получения информации о семафоре в Nucleus RTOS
Прототип служебного вызова:
STATUS NU_Semaphore_Information(NU_SEMAPHORE *semaphore, CHAR *name, UNSIGNED *current_count, OPTION *suspend_type, UNSIGNED *tasks_waiting, NU_TASK **first_task);
Параметры:
semaphore – указатель на блок управления семафора, о котором требуется предоставить информацию;
name – указатель на 8-символьное имя семафора, с включенным в эту область нулевым терминирующим байтом;
current_count – указатель на переменную, которая примет текущее значение счетчика семафора;
suspend_type – указатель на переменную, которая примет тип приостановки задачи, может принимать значения NU_FIFO и NU_PRIORITY;
task_waiting – указатель на переменную, которая примет количество приостановленных задач в семафоре;
first_task – указатель на переменную типа NU_TASK, которая примет указатель на блок управления первой приостановленной задачи.
Возвращаемое значение:
NU_SUCCESS – вызов был успешно завершен;
NU_INVALID_SEMAPHORE – некорректный указатель на семафор.
Вызов для получения информации о семафоре в Nucleus SE
Этот вызов API поддерживает основной функционал Nucleus RTOS API.
Прототип служебного вызова:
STATUS NUSE_Semaphore_Information(NUSE_SEMAPHORE semaphore, U8 *current_count, U8 *tasks_waiting, NUSE_TASK *first_task);
Параметры:
semaphore – индекс семафора, о котором требуется предоставить информацию;
current_count – указатель на переменную, которая примет текущее значение счетчика семафора;
tasks_waiting – указатель на переменную, которая примет количество приостановленных на этом семафоре задач (ничего не возвращается, если поддержка приостановки задач отключена);
first_task – указатель на переменную типа NUSE_TASK, которая примет индекс первой приостановленной задачи (ничего не возвращается, если поддержка приостановки задач отключена).
Возвращаемое значение:
NUSE_SUCCESS – вызов был успешно завершен;
NUSE_INVALID_SEMAPHORE – некорректный индекс семафора;
NUSE_INVALID_POINTER – один или несколько параметров указателя некорректны.
Реализация получения информации о семафоре в Nucleus SE
Реализация этого вызова API довольно проста:
NUSE_CS_Enter();
*current_count = NUSE_Semaphore_Counter[semaphore];
#if NUSE_BLOCKING_ENABLE
*tasks_waiting = NUSE_Semaphore_Blocking_Count[semaphore];
if (NUSE_Semaphore_Blocking_Count[semaphore] != 0)
{
U8 index;
for (index=0; index<NUSE_TASK_NUMBER; index++)
{
if ((LONIB(NUSE_Task_Status[index]) ==
NUSE_SEMAPHORE_SUSPEND) &&
(HINIB(NUSE_Task_Status[index]) == semaphore))
{
*first_task = index;
break;
}
}
}
else
{
*first_task = 0;
}
#else
*tasks_waiting = 0;
*first_task = 0;
#endif
NUSE_CS_Exit();
return NUSE_SUCCESS;
Функция возвращает статус семафора. Затем, если функциональность блокировки API-вызовов активирована, возвращается количество ожидающих задач и индекс первой из них (в противном случае этим параметрам присваивается значение 0).
Получение количества семафоров
Этот служебный вызов возвращает количество семафоров в приложении. В Nucleus RTOS это значение меняется со временем и возвращаемое значение соответствует текущему количеству семафоров, а в Nucleus SE возвращаемое значение устанавливается на этапе сборки и больше не меняется.
Вызов для счетчика семафоров в Nucleus RTOS
Прототип служебного вызова:
UNSIGNED NU_Established_Semaphores(VOID);
Параметры:
Отсутствуют.
Возвращаемое значение:
Количество созданных семафоров в приложении.
Вызов для счетчика семафоров в Nucleus SE
Этот вызов API поддерживает основной функционал Nucleus RTOS API.
Прототип служебного вызова:
U8 NUSE_Semaphore_Count(void);
Параметры:
Отсутствуют.
Возвращаемое значение:
Количество сконфигурированных семафоров в приложении.
Реализация счетчиков семафоров в Nucleus SE
Реализация этого вызова API довольно проста: возвращается значение символа #define NUSE_SEMAPHORE_NUMBER.
Структуры данных
Семафоры используют два или три массива структур данных (в ОЗУ и ПЗУ), которые, как и все другие объекты Nucleus SE, являются набором таблиц, размер которых зависит от количества семафоров в приложении и выбранных параметров.
Настоятельно рекомендую, чтобы код приложения не использовал прямой доступ к этим структурам данных, а обращался к ним через предоставляемые функции API. Это позволит избежать несовместимости с будущими версиями Nucleus SE и нежелательных побочных эффектов, а также упростит портирование приложения на Nucleus RTOS. Для лучшего понимания принципа работы кода служебных вызовов и для отладки ниже дан подробный обзор структур данных.
Данные в ОЗУ
Эти данные имеют следующую структуру:
NUSE_Semaphore_Counter[] – массив типа U8, имеющий одну запись для каждого сконфигурированного семафора, в нем хранится значение счетчика.
NUSE_Semaphore_Blocking_Count[] – массив типа U8, содержит счетчики заблокированных на каждом семафоре задач. Этот массив существует, только если активирована функциональность блокировки API вызовов.
NUSE_Semaphore_Counter[] инициализируется в начальное значение (смотри «Данные в ПЗУ» ниже), а NUSE_Semaphore_Blocking_Count[] зануляется при помощи NUSE_Init_Semaphore() при запуске Nucleus SE. Одна из следующих статей предоставит полное описание процедур запуска Nucleus SE.
Ниже приведены определения этих структур данных в файле nuse_init.c.
RAM U8 NUSE_Semaphore_Counter[NUSE_SEMAPHORE_NUMBER];
#if NUSE_BLOCKING_ENABLE
RAM U8 NUSE_Semaphore_Blocking_Count[NUSE_SEMAPHORE_NUMBER];
#endif
Данные в ПЗУ
Структура данных:
NUSE_Semaphore_Initial_Value[] – массив типа U8, имеющий одну запись для каждого семафора, это начальные значения семафоров.
Эта структура данных объявляется и инициализируется (статически) в nuse_config.c:
ROM U8 NUSE_Semaphore_Initial_Value[NUSE_SEMAPHORE_NUMBER] =
{
/* semaphore initial count values */
};
Объем памяти для семафоров
Как и у всех объектов ядра Nucleus SE, объем данных, необходимый для семафоров, предсказуем.
Объем памяти в ПЗУ (в байтах) для всех семафоров в приложении равен NUSE_SEMAPHORE_NUMBER.
Объем памяти в ОЗУ (в байтах) для всех семафоров в приложении при активированных вызовах API блокировки может быть вычислен следующим образом:
NUSE_SEMAPHORE_NUMBER * 2
В противном случае он равен NUSE_SEMAPHORE_NUMBER.
Нереализованные вызовы API
Три вызова API для семафоров, которые присутствуют в Nucleus RTOS, не реализованы в Nucleus SE.
Создание семафоров
Этот вызов API создает семафор. В нем нет необходимости в Nucleus SE, так как семафоры создаются статически.
Прототип служебного вызова:
STATUS NU_Create_Semaphore(NU_SEMAPHORE *semaphore, CHAR *name, UNSIGNED initial_count, OPTION suspend_type);
Параметры:
semaphore – указатель на предоставленный пользователем блок управления семафором, он используется для управления семафорами в других API вызовах;
name – указатель на 8-символьное имя семафора, с включенным терминирующим нулевым байтом;
initial_count – начальное значение семафора;
suspend_type – указывает принцип приостановки задачи на семафоре. Может принимать значения NU_FIFO и NU_PRIORITY, соответствующие принципу FIFO (First-in-First-Out) и порядку приоритета приостановки задач.
Возвращаемое значение:
NU_SUCCESS – вызов был успешно завершен;
NU_INVALID_SEMAPHORE – говорит, что указатель на блок управления семафором нулевой (NULL) или уже используется;
NU_INVALID_SUSPEND – некорректный параметр suspend_type.
Удаление семафора
Этот вызов API удаляет ранее созданный семафор. В нем нет необходимости в Nucleus SE, так как семафоры создаются статически и не могут быть удалены.
Прототип служебного вызова:
STATUS NU_Delete_Semaphore(NU_SEMAPHORE *semaphore);
Параметры:
semaphore – указатель на блок управления семафором.
Возвращаемое значение:
NU_SUCCESS – вызов был успешно завершен;
NU_INVALID_SEMAPHORE – некорректный указатель на семафор.
Указатели на семафоры
Этот вызов API формирует последовательный список указателей на все семафоры в системе. В нем нет необходимости в Nucleus SE, так как семафоры идентифицируются простым индексом, а не указателем.
Прототип служебного вызова:
UNSIGNED NU_Semaphore_Pointers(NU_SEMAPHORE **pointer_list, UNSIGNED maximum_pointers);
Параметры:
pointer_list – указатель на массив указателей NU_SEMAPHORE, этот массив заполняется указателями на семафоры;
maximum_pointers – максимальное количество указателей в массиве.
Возвращаемое значение:
Количество указателей NU_SEMAPHORE в массиве.
Совместимость с Nucleus RTOS
Как и в случае со всеми другими объектами Nucleus SE, целью было обеспечение максимальной совместимости кода приложений с Nucleus RTOS. Семафоры не являются исключением и, с точки зрения пользователя, они реализованы также, как и в Nucleus RTOS. Есть и определенная несовместимость, которую я посчитал допустимой с учетом того, что финальный код станет более понятным и более эффективным с точки зрения объема требуемой памяти. В остальном, вызовы API Nucleus RTOS могут быть практически напрямую использованы как вызовы Nucleus SE.
Идентификаторы объектов
В Nucleus RTOS все объекты описываются структурами данных (блоками управления), имеющими определенный тип. Указатель на этот блок управления служит идентификатором семафора. Я решил, что в Nucleus SE для эффективного использования памяти необходим другой подход: все объекты ядра описываются набором таблиц в ОЗУ и/или ПЗУ. Размер этих таблиц определяется количеством сконфигурированных объектов каждого типа. Идентификатор конкретного объекта – индекс в этой таблице. Таким образом, я определил NUSE_SEMAPHORE в качестве эквивалента U8, переменная (а не указатель) этого типа служит идентификатором семафора. С этой небольшой несовместимостью легко справиться, если код портируется с Nucleus SE на Nucleus RTOS и наоборот. Обычно над идентификаторами объектов не выполняются никакие операции, кроме перемещения и хранения.
Nucleus RTOS также поддерживает присвоение имен семафорам. Эти имена используются только при отладке. Я исключил их из Nucleus SE, чтобы сэкономить память.
Размер счетчика
В Nucleus RTOS счетчик семафора имеет тип unsigned, который обычно представляет из себя 32-битную переменную. В Nucleus SE счетчик 8-битный, но это можно легко изменить. Обычно, в Nucleus RTOS проверка на переполнение семафора не производится. Вызов Nucleus SE API не позволит присвоить счетчику значения выше 255.
Нереализованные вызовы API
Nucleus RTOS поддерживает восемь служебных вызовов для работы с семафорами. Из них три не реализованы в Nucleus SE. Детали этих вызовов, а также решение об их исключении из Nucleus SE были описаны выше.
В следующей статье будут рассматриваться почтовые ящики.
Об авторе: Колин Уоллс уже более тридцати лет работает в сфере электронной промышленности, значительную часть времени уделяя встроенному ПО. Сейчас он — инженер в области встроенного ПО в Mentor Embedded (подразделение Mentor Graphics). Колин Уоллс часто выступает на конференциях и семинарах, автор многочисленных технических статей и двух книг по встроенному ПО. Живет в Великобритании. Профессиональный блог Колина, e-mail: colin_walls@mentor.com.