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

UnixForum





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

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

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

3.5. Списки

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

Рис.3.3: Полная схема списка готовности задач Ready List в системе FreeRTOS

Список в системе FreeRTOS является стандартным закольцованным двусвязным списком с парой интересных дополнений. Элементы списка следующие:

struct xLIST_ITEM
{
  portTickType xItemValue;                   /* Значение, помещаемое в список. В большинстве
                                                случае используется для сортировки 
                                                списка в порядке уменьшения значений */
  volatile struct xLIST_ITEM * pxNext;       /* Указатель на следующий элемент xListItem 
                                                в списке.  */
  volatile struct xLIST_ITEM * pxPrevious;   /* Указатель на предыдущий элемент xListItem 
                                                в списке. */
  void * pvOwner;                            /* Указатель на объект (обычно блок TCB),
                                                в котором находится элемент списка. Таким 
                                                образом, организуется двусвязный список 
                                                между объектами, хранящимися в списке, и  
                                                элементами самого списка. */
  void * pvContainer;                        /* Указатель на список (если таковой имеется),
                                                в который этот элемент списка помещается. */
};

В каждом элементе хранится номер, xItemValue, которое обычно является приоритетом задачи, который отслеживается, или значением таймера для планирования событий. Списки хранятся в порядке убывания приоритета, а это означает, что наивысший приоритет xItemValue (наибольшее число) находится в начале списка и самый низкий приоритет xItemValue (наименьшее число) находится в конце списка.

Указатели pxNext и pxPrevious являются стандартными указателями, связывающие элементы списке. Указатель pvOwner является указателем на владельца элемента списка. Обычно это указатель на блок TCB задачи. Указатель pvOwner используется для быстрого переключения задач в vTaskSwitchContext(): как только в pxReadyTasksLists[] будет найден элемент списка с наивысшим приоритетом, указатель pvOwner элемента списка позволит нам непосредственно перейти к блоку TCB, который нужен при планировании запусков задачи.

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

typedef struct xLIST
{
  volatile unsigned portBASE_TYPE uxNumberOfItems;
  volatile xListItem * pxIndex;           /* Используется для прохода по списку. Указывает
                                             на последний элемент, возвращенный 
                                             функцией pvListGetOwnerOfNextEntry (). */
  volatile xMiniListItem xListEnd;        /* Элемент списка, в котором находится максимально 
                                             возможное значение, означающее, что он всегда 
                                             находится в конце списка и, поэтому,  
                                             используется как маркер. */
} xList;

Размер списка в любое время хранится в переменной uxNumberOfItems, предназначенной для быстрого выполнения операций с размерами списков. Все новые списки инициализируются таким образом, что в них есть один элемент: элемент xListEnd. Значение xListEnd.xItemValue является контрольным значением, равным наибольшему значению для переменной xItemValue: 0xffff в случае, если portTickType представляет собой 16-битное значение, и 0xffffffff в случае, если portTickType представляет собой 32-битное значение. Другие элементы списка также могут иметь такое же значение; алгоритм вставки элементов списка гарантирует, что xListEnd всегда будет последним элементом в списке.

Поскольку списки сортируются в порядке убывания, элемент xListEnd используется как маркер начала списка. А поскольку список кольцевой, этот элемент xListEnd также является маркером конца списка.

В большинстве «традиционных» операций доступа к списку, которыми вы пользуетесь, вся работа выполняется в одном цикле for() или в функции, вызываемой следующим образом:

for (listPtr = listStart; listPtr != NULL; listPtr = listPtr->next) {
  // Что-то здесь делается с указателями listPtr ...
}

В системе FreeRTOS часто требуется получить доступ к спискам сразу с помощью нескольких циклов for() и while(), а также с помощью нескольких вызовов функций, и поэтому происходит обращение к функциям, в которых при обходе списка используется указатель pxIndex. Функция listGET_OWNER_OF_NEXT_ENTRY() выполняет операцию pxIndex = pxIndex->pxNext; и возвращает значение pxIndex. (Конечно, также осуществляется соответствующее определение конца списка). Таким образом, во время перемещения по списку с использованием pxIndex сам список отвечает за отслеживание «текущего положения», позволяя остальной части системы FreeRTOS не беспокоиться об этом.

Рис.3.4: Полная схема списка готовности задач Ready List в системе FreeRTOS после возникновения прерывания

То, что все действия со списком pxReadyTasksLists[] выполняются в функции vTaskSwitchContext(), является хорошим примером того, как используется указатель pxIndex. Давайте предположим, что у нас есть только один уровень приоритета, приоритет 0, и есть три задачи на этом уровне приоритета. Это похоже на общую схему списка готовности задач, которую мы рассмотрели ранее, но на этот раз мы будем рассматривать все структуры и поля данных.

Как видно на рис.3.3, pxCurrentTCB указывает, что в настоящее время выполняется Задача B. В следующий момент времени выполняется функция vTaskSwitchContext(), она вызывает функцию listGET_OWNER_OF_NEXT_ENTRY() с тем, чтобы перейти к запуску следующей задачи. Эта функция использует pxIndex->pxNext для того, чтобы выяснить, что следующей задачей является Задача С, и теперь pxIndex указывает на элемент списка Задачи C, а pxCurrentTCB указывает на блок TCB Задачи С так, как показано на рис.3.4.

Обратите внимание, что каждый объект struct xListItem является, на самом деле, объектом xGenericListItem из соответствующего блока TCB.


Продолжение статьи: Очереди