You are here

Blocking on Multiple RTOS Objects: наборы очередей

Перевод может содержать ошибки. Читайте первоисточник: Blocking on Multiple RTOS Objects

Назад: [Более подробно...] Вверх: [Более подробно...] Вперёд: [Более подробно...]

 

Наборы очередей. Введение.

Набор очередей - это возможность FreeRTOS, которая позволяет задаче ОС находиться в заблокированном состоянии, ожидая готовности нескольких очередей и/или семафоров одновременно. Очереди и семафоры группируются в наборы, тогда, вместо отдельных блокировок на каждой очереди и каждом семафоре, задача блокируется на наборе очередей.

Замечание: хотя иногда возникает необходимость блокировки на более чем одной очереди, если вы интегрируете FreeRTOS с устаревшим кодом сторонних разработчиков, проекты, которые свободны от таких ограничений, могут обычно достигать той же функциональности более эффективным способом, используя альтернативный шаблон проектирования, задокументированный внизу этой страницы. - дословно перевёл, а смысл до конца не понял.

 

Наборы очередей. Использование.

Наборы очередей используются способом, похожим на использование функции API select() и связанных с ней функций, которые являются частью стандартного API сокетов Беркли.

Наборы очередей могут содержать очереди и семафоры, которые вместе называются членами наборов очередей. Параметры функций API и возвращаемые значения, которые могут принимать либо хэндл очереди, либо хэндл семафора, используют тип QueueSetMemberHandle_t. Переменные, имеющие тип QueueHandle_t и SemaphoreHandle_t обычно могут неявно преобразовываться  в параметр, имеющий тип QueueSetMemberHandle_t. Аналогично может преобразовываться возвращаемое значение. При этом компилятор не будет выдавать предупреждений, т.е. явное преобразование к типу QueueSetMemberHandle_t и обратно не требуется.

Создание набора очередей

Перед тем, как набор очередей можно будет использовать, его необходимо создать, используя вызов функции API xQueueCreateSet(). После создания на набор очередей можно ссылаться переменной типа QueueSetHandle_t.

Добавление членов к набору очередей

Для добавления очереди или семафора к набору очередей используется функция API xQueueAddToSet().

Блокировка (ожидание) на наборе очередей

Функция API xQueueSelectFromSet() используется для проверки готовности к чтению любого элемента набора очередей, - где "чтение" означает либо "получение" в случае очереди, либо "забирание" в случае семафора.

Как и при использовании функций API xQueueReceive() и xSemaphoreTake(), xQueueSelectFromSet() позволяет вызывающей задаче дополнительно блокироваться, пока член набора очередей не будет готов к чтению.

Значение NULL будет возвращено функцией xQueueSelectFromSet() в случае таймаута. В противном случае xQueueSelectFromSet() вернёт хэндл члена набора очередей, который готов к чтению, позволяя вызывающей задаче немедленно  вызвать xQueueReceive() или xSemaphoreTake() (для хэндла очереди или хэндла семафора соответственно) с гарантией успешного завершения операции.

 

Примеры исходного кода

Страница с описанием функции API xQueueCreateSet() содержит пример исходного кода.

Стандартный демонстрационный файл QueueSet.c, расположенный в каталоге FreeRTOS/Demo/Common/Minimal из архива поставки FreeRTOS, содержит исчерпывающий пример.

 

Альтернативы использованию наборов очередей

Если нет каких-либо специфических проблем интеграции, которые требуют блокировки на нескольких очередях, похожий функционал обычно может быть достигнут с меньшим размером кода, меньшим объёмом ОЗУ и меньшими накладными расходами временеми выполнения, используя одну очередь. Реализация FreeRTOS+UDP предоставляет хороший пример того, как это можно сделать, и описана в следующей секции.

 

UDP/IP стек: постановка проблемы

Задача, которая контролирует стек FreeRTOS+UDP, управляется событиями. Имеется много источников событий. Некоторые события не имеют никаких дополнительных данных. С некоторыми событими связаны дополнительные данные произвольной (неизвестной заранее) длины. События могут быть:

  • Аппаратный модуль Ethernet принимает фрейм данных. Фрейм содержит данные различной (в том числе достаточно большой) длины.
  • Аппаратный модуль Ethernet завершает передачу фрейма, освобождает сеть и буфер ПДП.
  • Задача приложения передаёт пакет. Пакет может содержать данные различной (в том числе достаточно большой) длины.
  • Различные программные таймеры, например таймер ARP. Событие таймера не связано с какими-либо данными.

 

UDP/IP стек: решение

Стек UDP/IP could использовать различные очереди для каждого источника событий, а затем использовать набор очередей для блокировки на всех очередях сразу. Вместо этого стек UDP/IP:

  1. Определяет структуру, которая содержит член для хранения типа события, и другой член для хранения данных (или указателя на данные), которые связаны с этим событием.
  2. Использует одну очередь, созданную для хранения элементов с типом этой структуры. Каждый источник событий отправляет их в одну очередь.

Определение структуры показано ниже.

/*----------------------------------------------------------------------------*/
typedef struct IP_TASK_COMMANDS
{
    /* Сообщает принимающей задаче, что это за событие. */
    eIPEvent_t eEventType;
    /* Содержит либо сами данные, связанные с этим событием, либо указатель
    на них. */
    void *pvData;

} xIPStackEvent_t;

Пример использования этой структуры:

  • Когда истекает таймер ARP, он отправляет событие в очередь со значением в поле eEventType, установленным в eARPTimerEvent (это значение перечислимого типа). Событие таймера ARP не связано с какими-либо данными, поэтому поле pvData не устанавливается.
  • Когда драйвер Ethernet  принимает фрейм, он отправляет в очередь событие с полем eEventType, установленным в eEthernetRxEvent, и полем pvData, указывающим на буфер в ОЗУ, в котором содержится сам фрейм.
  • И так далее.

Задача обслуживания UDP/IP обрабатывает события, используя простой цикл:

/*----------------------------------------------------------------------------*/
/* Переменная, используемая для получения данных из очереди. */
xIPStackEvent_t xReceivedEvent;

for( ;; )
{
    /* Ждём поступления события. */
    xQueueReceive( xNetworkEventQueue, &xReceivedEvent, portMAX_DELAY );

    /* Для каждого типа события выполняем соответствующие действия. */
    switch( xReceivedEvent.eEventType )
    {
        case eNetworkDownEvent :
            prvProcessNetworkDownEvent();
            break;

        case eEthernetRxEvent :
            prvProcessEthernetFrame( xReceivedEvent.pvData );
            break;

        case eARPTimerEvent :
            prvAgeARPCache();
            break;

        case eStackTxEvent :
            prvProcessGeneratedPacket( xReceivedEvent.pvData );
            break;

        case eDHCPEvent:
            vDHCPProcess();
            break;

        default :
            /* Should not get here. */
            break;
    }
}

 

Hobby's category: