Pull to refresh

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

Reading time9 min
Views1.7K
Original author: Colin Walls


В этой статье продолжается обзор семафоров.

Вспомогательные службы семафоров


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.
Tags:
Hubs:
Total votes 5: ↑4 and ↓1+3
Comments0

Articles