FROM_ISR).
Задачи-сторожа
(Gatekeeper tasks)
Задачи-сторожа предоставляют простой
и прозрачный метод реализации механизма
взаимного исключения, которому не прису-
щи проблемы инверсии приоритетов и вза-
имной блокировки.
Задача-сторож — это задача, которая
имеет единоличный доступ к разделяемо-
му ресурсу. Никакая другая задача в про-
грамме не имеет права обращаться к ре-
сурсу напрямую. Вместо этого все задачи,
разделяющие общий ресурс, обращаются
к задаче-сторожу, используя безопасные ме-
ханизмы межзадачного взаимодействия
FreeRTOS. Непосредственно действия с ре-
сурсом выполняет задача-сторож.
В отличие от мьютексов, работать с кото-
рыми могут только задачи, к задаче-сторожу
могут обращаться как задачи, так и обработ-
чики прерываний.
Рассмотрим использование задачи-
сторожа на примере учебной программы
№ 3. Как и в учебной программе № 1, здесь
разделяемым ресурсом выступает консоль.
В программе созданы две задачи, каждая
из которых выводит свое сообщение на кон-
соль. Кроме того, сообщения выводит функ-
ция, вызываемая каждый системный квант
времени, это демонстрирует возможность
обращения к разделяемому ресурсу из тела
обработчика прерывания:
#include “FreeRTOS.h”
#include “task.h”
#include “semphr.h”
#include
#include
/* Прототип задачи, которая выводит сообщения на консоль,
передавая их задаче-сторожу.
* Будет создано 2 экземпляра этой задачи */
static void prvPrintTask(void *pvParameters);
/* Прототип задачи-сторожа */
static void prvStdioGatekeeperTask(void *pvParameters);
/* Таблица строк, которые будут выводиться на консоль */
static char *pcStringsToPrint[] = {
“Task 1 ****************************************************\r\
n”,
“Task 2 ----------------------------------------------------\r\n”,
“Message printed from the tick hook interrupt ##############\r\n”
};
/*-----------------------------------------------------------*/
/* Объявить очередь, которая будет использоваться для передачи
сообщений от задач и прерываний к задаче-сторожу. */
xQueueHandle xPrintQueue;
int main(void) {
/* Создать очередь длиной макс. 5 элементов типа
“указатель на строку” */
xPrintQueue = xQueueCreate(5, sizeof(char *));
/* Проверить, успешно ли создана очередь. */
if (xPrintQueue != NULL) {
/* Создать два экземпляра задачи, которые будут выводить
строки на консоль, передавая их задаче-сторожу.
В качестве параметра при создании задачи передается
номер строки в таблице. Задачи создаются с разными
приоритетами. */
xTaskCreate(prvPrintTask, “Print1”, 1000, (void *) 0, 1, NULL);
xTaskCreate(prvPrintTask, “Print2”, 1000, (void *) 1, 2, NULL);
/* Создать задачу-сторож. Только она будет иметь
непосредственный доступ к консоли. */
xTaskCreate(prvStdioGatekeeperTask, “Gatekeeper”, 1000, NULL,
0, NULL);
/* Запуск планировщика. */
vTaskStartScheduler();
}
return 0;
}
/*-----------------------------------------------------------*/
static void prvStdioGatekeeperTask(void *pvParameters) {
char *pcMessageToPrint;
/* Задача-сторож. Только она имеет прямой доступ к консоли.
* Когда другие задачи “хотят” вывести строку на консоль,
они записывают указатель на нее в очередь.
* Указатель из очереди считывает задача-сторож
и непосредственно выводит строку */
for (;;) {
/* Ждать появления сообщения в очереди. */
xQueueReceive(xPrintQueue, &pcMessageToPrint, portMAX_
DELAY);
/* Непосредственно вывести строку. */
printf(“%s”, pcMessageToPrint);
fflush(stdout);
/* Вернуться к ожиданию следующей строки. */
}
}
/*-----------------------------------------------------------*/
/* Задача, которая автоматически вызывается каждый системный
квант времени.
* Макроопределение configUSE_TICK_HOOK должно быть равно 1. */
void vApplicationTickHook(void) {
static int iCount = 0;
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
/* Выводить строку каждые 200 квантов времени.
Строка не выводится напрямую, указатель на нее помещается
в очередь и считывается задачей-сторожем. */
iCount++;
if (iCount >= 200) {
/* Используется API-функция, предназначенная для вызова
из обработчиков прерываний!!! */
xQueueSendToFrontFromISR(xPrintQueue, &(pcStringsToPrint[2]),
&xHigherPriorityTaskWoken);
iCount = 0;
}
}
/*-----------------------------------------------------------*/
static void prvPrintTask(void *pvParameters) {
int iIndexToString;
/* Будет создано 2 экземпляра этой задачи. В качестве параметра
при создании задачи выступает номер строки в таблице строк. */
iIndexToString = (int) pvParameters;
for (;;) {
/* Вывести строку на консоль. Но не напрямую, а передав
указатель на строку задаче-сторожу.*/
xQueueSendToBack(xPrintQueue, &(pcStringsToPrint[iIndexT
oString]), 0);
/* Блокировать задачу на промежуток времени случайной
длины: от 0 до 500 квантов. */
vTaskDelay((rand() % 500));
/* Вообще функция rand() не является реентерабельной.
Однако в этой программе это неважно. */
}
}
Достарыңызбен бөлісу: |