FreeRTOS是一个可剪裁的小型RTOS系统,其特点包括:
- FreeRTOS的内核支持抢占式,合作式和时间片调度
- 提供一个用于低功耗的Tickless模式
- 系统组件在创建时可以选择动态或静态的RAM,比如任务、消息队列、信号量、软件定时器等等
- 支持实时任务和协程(co-routines也有称作合作式、协同程序https://blog.csdn.net/lqk1985/article/details/6535598)
- 任务与任务/中断通信和同步(消息队列、二值信号量、计数型信号量、互斥信号量和递归互斥信号量、任务通知)
- 互斥信号量具有优先级继承特性(但不可在中断中使用)
- 堆栈溢出检测功能
- 任务数量和任务优先级不限
1. 互斥量、二值信号量:
- 二值信号量:用于任务间同步,用完丢弃,不再归还
- 互斥量:用于共享资源的访问,用完必须归还。互斥量自动提供了一个基本的”优先级继承”机制。
2. 优先级反转:
3.优先级继承:
4.死锁:
当两个任务都在等待被对方持有的资源时,两个任务都无法再继续执行,这种情况就被称为死锁。
考虑如下情形,任务 A 与任务 B 都需要获得互斥量 X 与互斥量 Y 以完成各自的工作:
1. 任务 A 执行,并成功获得了互斥量 X。
2. 任务 A 被任务 B 抢占。
3. 任务 B 成功获得了互斥量 Y,之后又试图获取互斥量 X——但互斥量 X 已经被任务 A 持有,所以对任务 B 无效。任务 B 选择进入阻塞态以等待互斥量 X 被释放。
4. 任务 A 得以继续执行。其试图获取互斥量 Y——但互斥量 Y 已经被任务 B持有而对任务 A 无效。任务 A 也选择进入阻塞态以等待互斥量 Y 被释放。
// 任务优先级反转
#include "user_task.h"
#include "queue.h"
#include "semphr.h"
#define DELAY_LOOP_CNT 1000000000
static TaskHandle_t Task1;
static TaskHandle_t Task2;
static TaskHandle_t Task3;
static xSemaphoreHandle xMutex;
void User_TickHook(void)
{
}
void Delay(int cnt)
{
int i = 0, j = 0;
for (j = 0; j < cnt; j++)
for (i = 0; i < DELAY_LOOP_CNT; i++)
{
;
}
}
static void vTaskHi(void *pvParam)
{
TickType_t tick;
vTaskDelay(10);
pvParam = pvParam;
tick = xTaskGetTickCount();
for (;;)
{
printf("vTaskHi1\r\n");
xSemaphoreTake(xMutex, portMAX_DELAY);
printf("vTaskHi2\r\n");
Delay(1);
printf("vTaskHi3\r\n");
xSemaphoreGive(xMutex);
printf("vTaskHi4\r\n");
vTaskDelay(1000);
}
}
static void vTaskMi(void *pvParam)
{
TickType_t tick;
vTaskDelay(10);
pvParam = pvParam;
tick = xTaskGetTickCount();
for (;;)
{
printf("vTaskMi\r\n");
vTaskDelay(500);
}
}
static void vTaskLo(void *pvParam)
{
TickType_t tick;
pvParam = pvParam;
tick = xTaskGetTickCount();
for (;;)
{
printf("vTaskLo1\r\n");
xSemaphoreTake(xMutex, portMAX_DELAY);
printf("vTaskLo2\r\n");
Delay(1);
printf("vTaskLo3\r\n");
xSemaphoreGive(xMutex);
printf("vTaskLo4\r\n");
}
}
void vTask_main(void)
{
srand(567);
xMutex = xSemaphoreCreateMutex();
if (xMutex != NULL)
{
printf("vTask_main\r\n");
xTaskCreate(vTaskLo, "Low", 1000, (void*)"TaskLo", 1, &Task3);
xTaskCreate(vTaskMi, "Middle", 1000, (void*)"TaskMi", 2, &Task2);
xTaskCreate(vTaskHi, "High", 1000, (void*)"TaskHi", 3, &Task1);
vTaskStartScheduler();
}
else
{
printf("xSemaphoreCreateMutex failed!\r\n");
}
for (;;) { ; }
}
运行结果:
//任务死锁
#include "user_task.h"
#include "queue.h"
#include "semphr.h"
#define DELAY_LOOP_CNT 1000000000
static TaskHandle_t TaskA;
static TaskHandle_t TaskB;
static xSemaphoreHandle xMutexX;
static xSemaphoreHandle xMutexY;
void User_TickHook(void)
{
}
void Delay(int cnt)
{
int i = 0, j = 0;
for (j = 0; j < cnt; j++)
for (i = 0; i < DELAY_LOOP_CNT; i++)
{
;
}
}
static void vTaskA(void *pvParam)
{
pvParam = pvParam;
for (;;)
{
printf("TaskA wait X\r\n");
xSemaphoreTake(xMutexX, portMAX_DELAY);
printf("TaskA take X\r\n");
vTaskDelay(1000);
printf("TaskA wait Y\r\n");
xSemaphoreTake(xMutexY, portMAX_DELAY);
printf("TaskA take Y\r\n");
vTaskDelay(100);
xSemaphoreGive(xMutexY);
printf("TaskA give Y\r\n");
xSemaphoreGive(xMutexX);
printf("TaskA give X\r\n");
}
}
static void vTaskB(void *pvParam)
{
pvParam = pvParam;
for (;;)
{
printf("TaskB wait Y\r\n");
xSemaphoreTake(xMutexY, portMAX_DELAY);
printf("TaskB take Y\r\n");
vTaskDelay(500);
printf("TaskB wait X\r\n");
xSemaphoreTake(xMutexX, portMAX_DELAY);
printf("TaskB take X\r\n");
xSemaphoreGive(xMutexX);
printf("TaskB give X\r\n");
xSemaphoreGive(xMutexY);
printf("TaskB give Y\r\n");
}
}
void vTask_main(void)
{
srand(567);
xMutexX = xSemaphoreCreateMutex();
xMutexY = xSemaphoreCreateMutex();
if ((xMutexX != NULL) && (xMutexY != NULL))
{
printf("vTask_main\r\n");
xTaskCreate(vTaskA, "TaskA", 1000, (void*)"TaskA", 1, &TaskA);
xTaskCreate(vTaskB, "TaskB", 1000, (void*)"TaskB", 2, &TaskB);
vTaskStartScheduler();
}
else
{
if (!xMutexX)
printf("Create xMutexX failed!\r\n");
if (!xMutexY)
printf("Create xMutexY failed!\r\n");
}
for (;;) { ; }
}
运行结果:
5.守护任务
守护任务提供了一种干净利落的方法来实现互斥功能,而不用担心会发生优先级反转和死锁。
守护任务是对某个资源具有唯一所有权的任务。只有守护任务才可以直接访问其守
护的资源——其它任务要访问该资源只能间接地通过守护任务提供的服务。