Библиотека сайта 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.
Продолжение статьи: Очереди