Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

ОС реального времени FreeRTOS

Глава 3 из книги "Архитектура приложений с открытым исходным кодом", том 2.
Оригинал: FreeRTOS
Автор: Christopher Svec
Перевод: Н.Ромоданов

3.4. Задачи

Основная работа всех операционных систем состоит в запуске и координации работы пользовательских задач. Подобно многим операционным системам, основной единицей работы в системе FreeRTOS является задача. В системе FreeRTOS для представления каждой задачи используется блок управления задачей (Task Control Block - ТСВ).

Блок управления задачей TCB

Блок TCB определяется в tasks.c следующим образом:

typedef struct tskTaskControlBlock
{
  volatile portSTACK_TYPE *pxTopOfStack;                  /* Указывает на месторасположение
                                                             последнего элемента, размещенного
                                                             в стеке задач. ЭТО 
                                                             ДОЛЖЕН БЫТЬ ПЕРВЫЙ ЭЛЕМЕНТ
                                                             СТРУКТУРЫ STRUCT. */
                                                         
  xListItem    xGenericListItem;                          /* Элемент списка, используемый для 
                                                             помещения блока TCB в очереди 
                                                             готовых и заблокированных задач. */
  xListItem    xEventListItem;                            /* Элемент списка, используемый для 
                                                             помещения блока TCB в списки событий.*/
  unsigned portBASE_TYPE uxPriority;                      /* Приоритет задачи;
                                                             0 является низшим
                                                             приоритетом. */
  portSTACK_TYPE *pxStack;                                /* Указывает на начало
                                                             стека. */
  signed char    pcTaskName[ configMAX_TASK_NAME_LEN ];   /* Описательное имя, которое 
                                                             присваивается стеку, когда он 
                                                             создается. Используется только
                                                             для отладки. */

  #if ( portSTACK_GROWTH > 0 )
    portSTACK_TYPE *pxEndOfStack;                         /* Используется для проверки стека 
                                                             на переполнение в тех архитектурах,
                                                             где стек растет с младших
                                                             адресов памяти. */
  #endif

  #if ( configUSE_MUTEXES == 1 )
    unsigned portBASE_TYPE uxBasePriority;                /* Приоритет, назначенный задаче
                                                             последним - 
                                                             используется механизмом
                                                             наследования приоритетов. */
  #endif

} tskTCB;

В блоке TCB в переменной pxStack хранится адрес начала стека, а в переменной pxTopOfStack - текущая вершина стека. В нем также в переменной pxEndOfStack хранится указатель на конец стека для проверки стека на переполнение в случае, если стек растет «вверх» в сторону старших адресов. Если стек растет «вниз» к младшим адреса, то переполнение стека проверяется путем сравнения текущей вершины стека с началом стека, которое хранится в переменной pxStack.

В блоке TCB в переменных uxPriority и uxBasePriority хранится начальный приоритет задачи. Задача дается приоритет, когда она создается, и приоритет задачи может быть изменен. Если в системе FreeRTOS реализовано наследование приоритетов, то переменная uxBasePriority используется для вспоминания первоначального приоритета, когда временно возводится до «наследуемого» приоритета. Подробности, касающиеся наследования приоритетов, приведены ниже в обсуждении мютексов.

В каждой задаче есть два элемента списка для использования в различных списках планирования в системе FreeRTOS. Когда задача добавляется в список, система FreeRTOS не вставляет указатель непосредственно в блок TCB. Вместо этого, он вставляет указатель либо в переменную xGenericListItem, либо в переменную xEventListItem блока TCB. Эти переменные xListItem позволяют системе FreeRTOS организовывать списки более хитро, чем если бы в них был указатель на блок TCB. Мы увидим это на примере позже, когда будем обсуждать списки.

Задача может находиться в одном из четырех состояний: выполняться, готова к выполнению, приостановлена или блокирована. Можно было бы ожидать, что в каждой задаче есть переменная, которая сообщает системе FreeRTOS о том, в каком состоянии задача находится, но это не так. Вместо этого, система FreeRTOS отслеживает состояние задачи неявно, помещая задачи в соответствующий список: список задач, готовых для выполнения, список приостановленных задач и т.д. Присутствие задачи в конкретном списке указывает состояние задачи. Когда задача переходит из одного состояния в другое, система FreeRTOS просто перемещает ее из одного списка в другой.

Настройка задачи

Мы уже затронули вопрос о том как задача выбирается и как планируется на исполнение с массивом pxReadyTasksLists; теперь давайте посмотрим на то, как первоначально создается задача. Задача создается, когда вызывается функция xTaskCreate(). FreeRTOS использует только что выделенный блок TCB объект для хранения имени, приоритета и другие деталей, касающиеся задачи, а затем выделяет некоторое количество памяти из стека по запросам пользователя (если в наличии есть достаточно памяти) и запоминает начало стека в элементе pxStack блока пользователя TCB.

Стек инициализируется таким образом, как будто новая задача уже запущена и была прервана переключением контекста. Таким образом, планировщик может рассматривать новые только что созданные задачи точно так же, как те, что уже работали некоторое время; планировщику не требуется какой-либо специальный код для обработки новых задач.

Способ, с помощью которого стек задачи создается таким образом, чтобы он выглядел, как если бы задача была прервана переключением контекста, зависит от архитектуры, на которой работает система FreeRTOS; хорошим примером является следующая реализация для процессора ARM Cortex-M3:

unsigned int *pxPortInitialiseStack( unsigned int *pxTopOfStack, 
                                     pdTASK_CODE pxCode,
                                     void *pvParameters )
{
  /* Эмулирует фрейм стека как если бы он был создан прерыванием переключателя контекста. */
  pxTopOfStack--; /* Смещение добавляется к значению счетчика — так MCU использует стек при 
                     переходе на прерывание/выходе из прерывания. */
  *pxTopOfStack = portINITIAL_XPSR;  /* xPSR */
  pxTopOfStack--;
  *pxTopOfStack = ( portSTACK_TYPE ) pxCode;  /* PC */
  pxTopOfStack--;
  *pxTopOfStack = 0;  /* LR */
  pxTopOfStack -= 5;  /* R12, R3, R2 and R1. */
  *pxTopOfStack = ( portSTACK_TYPE ) pvParameters;  /* R0 */
  pxTopOfStack -= 8;  /* R11, R10, R9, R8, R7, R6, R5 and R4. */
  
  return pxTopOfStack;
}

Когда происходит прерывание задачи процессор ARM Cortex-M3 помещает регистры в стек, когда задача прерывается. Функция pxPortInitialiseStack() изменяет стек так, чтобы он выглядел как будто в него были помещены регистры, хотя в действительности задача даже не начала выполняться. Для регистров xPSR, PC, LR и R0 процессора ARM в стеке хранятся известные значения. Остальные регистры R1 - R12 получить в стеке память, выделенное для них путем уменьшения верхней части указателя стека, но для этих регистров в стеке не хранятся какие-либо конкретные данных. В архитектуре ARM определяется, что при перезагрузке значения этих регистров не определены, поэтому в программе (не имеющей ошибок) не следует считать, что там хранятся известные значения.

После того, как стек подготовлен, задача почти готова к запуску. Однако, во-первых, система FreeRTOS отключает прерывания: Мы собираемся начать с передачей готовых списков и других структур планировщика, и мы не хотим, чтобы кто-нибудь, кроме нас, их менял.

Если это первая задача, которую когда-либо была создана создан, то система FreeRTOS инициализация списки задач планировщика. В планировщике системы FreeRTOS есть массив списков готовых задач pxReadyTasksLists[], в котором для каждого возможного уровня приоритета есть один список готовых задач. В системе FreeRTOS также есть несколько других списков, используемых для отслеживания задач, которые были приостановлены, уничтожены или задержаны. Сейчас они все инициализируются.

После того, как будет сделана любая первая инициализация, новая задача добавляется в список готовых задач, соответствующий указанному в ней уровню приоритета. Будут активированы прерывания и создание новой задачи завершится.


Продолжение статьи: Списки