Лабораторная работа №11. Очереди FreeRTOS (на основе мьютексов и семафоров)
11.1 Цели и задачи
Целью работы является освоение механизма блокировок во FreeRTOS на основе мьютексов и семафоров.
Для достижения поставленной цели требуется решить следующие задачи:
- Изучить назначение мьютексов и семафоров.
- Ознакомиться с процессом использования мьютексов во FreeRTOS.
- Разработать задачи с блокировкой, выполняемые во FreeRTOS.
Презентация к блоку "Операционные системы реального времени RISC-V"
11.2 Основные теоретические сведения
Мьютекс (Mutex: MUTual EXception) является разновидностью семафора. Семафор - примитив синхронизации работы процессов и потоков, в основе которого лежит счётчик, над которым можно производить две операции: увеличение и уменьшение значения на единицу, при этом операция уменьшения для нулевого значения счетчика является блокирующейся. Служит для построения более сложных механизмов синхронизации и используется для синхронизации параллельно работающих задач, для защиты передачи данных через разделяемую память, для защиты критических секций, а также для управления доступом к аппаратному обеспечению. Параметром семафора является максимальное значение его счетчика. Мьютекс - семафор, максимальное значение которого равно единице.
FreeRTOS разделяет понятия мьютекса и бинарного семафора. Отличие заключается в том, что у бинарных семафорах отсутствует механизм модификации приоритетов, который есть в мьютексах. Это проявляется тем, что у задачи, повышается приоритет на время владения мьютексом. Таким образом двоичные семафоры лучше подходят для синхронизации задач, а мьютексы - для блокировки ресурсов. В данной работе рассматриваются только двоичные семафоры и мьютексы. Существуют счетные семафоры и рекурсивные мьютексы, у которых максимальное значение счетчика больше единицы.
Для использования мьютексов и семафоров нужно создать макрос configSUPPORT_DYNAMIC_ALLOCATION, равный 1. Для использования мьютексов нужно также создать макрос configUSE_MUTEXES, равный 1. Мьютексы создаются функцией SemaphoreHandle_t xSemaphoreCreateMutex( void ), а семафоры - SemaphoreHandle_t xSemaphoreCreateBinary( void ).
Взятие семафора выполняется функцией xSemaphoreTake( SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait ), а освобождение - xSemaphoreGive( SemaphoreHandle_t xSemaphore ). В функции взятия используется параметр времени ожидания xTicksToWait. Он определяет количество системных тиков, в течение которых задача ждет получение семафора.
В более старых версиях FreeRTOS для определения мьютекса используется объект xSemaphoreHandle. Функционально он ничем не отличается от SemaphoreHandle_t.
Пример кода работы с мьютексами:
SemaphoreHandle_t xSemaphore = NULL; void vATask( void * pvParameters ) { xSemaphore = xSemaphoreCreateMutex(); if( xSemaphore != NULL ) { if( xSemaphoreGive( xSemaphore ) != pdTRUE ) // will fail! { … } if( xSemaphoreTake( xSemaphore, ( TickType_t ) 0 ) ) { … if( xSemaphoreGive( xSemaphore ) != pdTRUE ) { … } } } }
11.3 Задание к лабораторной работе
Для выполнения работы требуется:
- Подготовить проект с настроенной FreeRTOS для QEMU.
- Реализовать задачи с блокировкой, согласно требованиям.
- Запустить программу в эмуляторе и убедиться в работоспособности задач.
В лабораторной работе требуется разработать набор задач для доступа к общему ресурсу. В данной работе общим ресурсом является группа светодиодов на плате. В ходе работы нужно ограничить максимальное количество включенных светодиодов. Для этой цели предлагается использовать мьютексы в качестве механизма блокировки задач.
- Количество светодиодов n = max(k, 2 + ((a+1) % 4));
- Период зажигания светодиода T[i] = (a * t + b * t * (i));
- Скважность 50%.
- Количество одновременно горящих светодиодов: 1 + ((b)%(n/2))
где а = номер варианта, b = количество букв в фамилии, t - базовый период, принимаемый как 200 тиков процессора, k - количество доступных для работы светодиодов.
Если светодиод не зажигается из-за указанных ограничений, то программа пропускает период горения и ждет полупериод для повторной попытки.
11.3.1 Описание последовательности выполнения работы
Для выполнения задания необходимо:
- Настроить среду для работы согласно инструкции из п. 11.2.1.
- Вычислить параметры вашего варианта задания.
- С помощью функций управления задачами FreeRTOS модифицировать поведение светодиодов.
- Реализовать блокировки доступа к общему ресурсу с помощью мьютекса
- Выполнить сборку и запуск модифицированного примера, зафиксировать результат работы в отчете.
11.3.2 Пример выполнения работы
typedef struct { int num; char* text; char* text_fail } lab_task_data_w_fail; char* names[] = {"Toggle 1", "Toggle 2", "Toggle 3"}; char* names_fail[] = {"Toggle 1 failed", "Toggle 2 failed", "Toggle 3 failed"}; lab_task_data_w_fail data_fail[3]; void mutex_task_function(void *pvParameters) { lab_task_data_w_fail* config = (lab_task_data_w_fail*)pvParameters; while(1) { if(xSemaphoreTake(mutex,0)){ vSendString(config->text); vTaskDelay(100*(250<<config->num)); vSendString(config->text); xSemaphoreGive(mutex); } else { vSendString(config->text_fail); } vTaskDelay(100*(250<<config->num)); } } int main_mutex(){ mutex = xSemaphoreCreateMutex(); for(int i=0;i<3;i++){ data_fail[i].num = i; data_fail[i].text = names[i]; data_fail[i].text_fail = names_fail[i]; xTaskCreate((TaskFunction_t )mutex_task_function, (const char* )names[i], (uint16_t )512, (void* )&data_fail[i], (UBaseType_t )2, (TaskHandle_t* )NULL); } vTaskStartScheduler(); return 0; } int main(){ main_mutex(); while(1); }
В ходе работы программы максимальное количество включенных светодиодов равно 1, так как созданный мьютекс блокирует включение других светодиодов. По этой причине при зажигании светодиода с самым большим периодом, остальные не могли зажечься.
11.5 Вопросы для контроля:
- Какое основное назначение мьютексов и семафоров?
- В чем отличие семафоров и мьютексов во FreeRTOS?
- Какие существуют основные параметры семафоров и мьютексов?
- Что означает параметр времени ожидания при взятии мьютекса?