优先级翻转示意图,如上图所示,定义:任务 H 为优先级最高的任务,任务 L 为优先级中 最低的任务,任务 M 为优先级在任务 H 与任务 L 之间的任务。
(1) 任务 H 和任务 M 为阻塞状态,等待某一事件发生,此时任务 L 正在运行。
(2) 此时任务 L 要访问共享资源,因此需要获取信号量。
(3) 任务 L 成功获取信号量,并且此时信号量已无资源,任务 L 开始访问共享资源。
(4) 此时任务 H 就绪,抢占任务 L 运行。
(5) 任务 H 开始运行。
(6) 此时任务 H 要访问共享资源,因此需要获取信号量,但信号量已无资源,因此任务 H 阻塞等待信号量资源。
(7) 任务 L 继续运行。
(8) 此时任务 M 就绪,抢占任务 L 运行。
(9) 任务 M 正在运行。
(10) 任务 M 运行完毕,继续阻塞。
(11) 任务 L 继续运行。
(12) 此时任务 L 对共享资源的访问操作完成,释放信号量,虽有任务 H 因成功获取信号 量,解除阻塞并抢占任务 L 运行。
(13) 任务 H 得以运行。 从上面优先级翻转的示例中,可以看出,任务 H 为最高优先级的任务,因此任务 H 执行的 操作需要有较高的实时性,但是由于优先级翻转的问题,导致了任务 H 需要等到任务 L 释放信 号量才能够运行,并且,任务 L 还会被其他介于任务 H 与任务 L 任务优先级之间的任务 M 抢 占,因此任务 H 还需等待任务 M 运行完毕,这显然不符合任务 H 需要的高实时性要求。
CountSemaphore = xSemaphoreCreateCounting( (UBaseType_t)1, (UBaseType_t)1);
#define TASK_L_PRIO 2
#define TASK_L_STK_SIZE 128
TaskHandle_t Task_L_Task_Handler;
void task_l_task(void *pvParameters);
#define TASK_M_PRIO 3
#define TASK_M_STK_SIZE 128
TaskHandle_t Task_M_Task_Handler;
void task_m_task(void *pvParameters);
#define TASK_H_PRIO 4
#define TASK_H_STK_SIZE 128
TaskHandle_t Task_H_Task_Handler;
void task_h_task(void *pvParameters);
void task_l_task(void *pvParameters)
{
unsigned char task_l_num = 0;
while(1)
{
printf("\r\nTask L ready to take semaphore\r\n");
xSemaphoreTake(CountSemaphore, portMAX_DELAY);
printf("\r\nTask L has taked semaphore\r\n");
for(task_l_num = 0; task_l_num < 5; task_l_num++)
{
printf("\r\nTask L running\r\n");
delay_xms(100);
}
printf("\r\nTask L give semaphore\r\n");
xSemaphoreGive(CountSemaphore);
vTaskDelay(1000);
}
}
void task_m_task(void *pvParameters)
{
unsigned char task_m_num = 0;
vTaskDelay(200);
while(1)
{
for(task_m_num = 0; task_m_num < 5; task_m_num++)
{
printf("\r\nTask M running\r\n");
delay_xms(100);
}
vTaskDelay(1000);
}
}
void task_h_task(void *pvParameters)
{
vTaskDelay(500);
while(1)
{
printf("\r\nTask H ready to take semaphore\r\n");
xSemaphoreTake(CountSemaphore, portMAX_DELAY);
printf("\r\nTask H has taked semaphore\r\n");
printf("\r\nTask H running\r\n");
printf("\r\nTask H give semaphore\r\n");
xSemaphoreGive(CountSemaphore);
vTaskDelay(100);
}
}
下载验证
拓展验证:
当钥匙不止一把时,即信号量初始不是一个,而是两个时:
CountSemaphore = xSemaphoreCreateCounting( (UBaseType_t)3, (UBaseType_t)2);
函数 xSemaphoreCreateMutex()
此函数用于使用动态方式创建互斥信号量,创建互斥信号量所需的内存,由 FreeRTOS 从 FreeRTOS 管理的堆中进行分配。该函数实际上是一个宏定义,在 semphr.h 中有定义,具体的 代码如下所示:
从 上 面 的 代 码 中 可 以 看 出 , 函 数 xSemaphoreCreateMutex() 实 际 上 是 调 用 了 函 数 xQueueCreateMutex(),函数 xQueueCreateMutex()在 queue.c 文件中有定义。
CountSemaphore = xSemaphoreCreateCounting( (UBaseType_t)1, (UBaseType_t)1);
CountSemaphore = xSemaphoreCreateMutex();
以上两个函数分别切换注释,下载验证。下面左图为互斥信号量,右图为计数型信号量。可以发现:
当一个互斥信号量正在被一个低优先级的任务持有时,如果此时有个高优先级的任务也尝试获取这个互斥信号量,那么这个高优 先级的任务就会被阻塞。不过这个高优先级的任务会将低优先级任务的优先级提升到与自己相同的优先级。
所以和上一个例程不同的是,在高优先级任务开始尝试获取资源后,中优先级任务就不再运行了,原因就在于把占有钥匙的低优先级任务拉到了和高优先级任务一样的优先级。