互斥量的主要知识是关于解锁和上锁的问题,通过解锁和上锁的配合实现任务之间的互斥,但是这个互斥量也会延伸出来很多的问题,例如下面的两个问题:
1、让A(优先级为1),B(优先级为2),C(优先级为3),当A开始执行的时候,B的优先级比较高,打断A的执行,然后C的优先级比B的高,打断B的执行,但是C任务中有获得锁函数,但是A上的锁已经被A给解开了,这时候没有锁可以解,那么就只能进入休眠阻塞状态,就轮到B任务开始执行。
上面这种情况就会导致优先级比较最高的C任务不能执行,这时候的解决办法就是:在C任务无法解锁不能执行的时候,与A任务函数进行优先级的继承。
2、就是解锁上锁的循环问题,也就是一个任务函数中上锁解锁的过程中间调用了一个子函数,这个子函数中也有解锁和上锁的过程,从任务函数中进入子函数,有再次进行上锁,这时候就会产生阻塞。
上面这种情况可以通过多次上锁的方式进行解决。
下面是FreeRTOS的一个缺点:
互斥量的创建之后,是不需要赋初始值,写入初始值,创建函数中最后会自己填入初始值,下面互斥量的简单使用代码,task1的任务优先级小于task2的任务优先级,刚开始的时候task2会先执行,但是task2需要获得锁,没有获得就需要进入休眠的阻塞状态,然后进入task1的任务函数进行执行,执行完任务之后,开始上锁,然后激活task2的任务函数,高优先级的任务函数抢占低优先级task1。
TaskHandle_t vCheckTask1_hander;
TaskHandle_t vCheckTask2_hander;
TaskHandle_t vCheckTask3_hander;
QueueHandle_t QueueUsart1;
QueueHandle_t QueueUsart2;
QueueSetHandle_t xQueueUsartHandle;
QueueHandle_t SemaphoreHandle;
QueueHandle_t xSemaMutexHandle;
static void vCheckTask1( void *pvParameters )
{
uint16_t data=1;
for( ;; )
{
// xQueueSend(QueueUsart1,&data,portMAX_DELAY);
printf("task1:%d\r\n",data);
// xSemaphoreGive(SemaphoreHandle);
xSemaphoreGive(xSemaMutexHandle);
// vTaskDelay(10);
}
}
static void vCheckTask2( void *pvParameters )
{
uint16_t data=2;
for( ;; )
{
// xQueueSend(QueueUsart2,&data,portMAX_DELAY);
// xSemaphoreTake(SemaphoreHandle,portMAX_DELAY);
xSemaphoreTake(xSemaMutexHandle,portMAX_DELAY);
printf("task2:%d\r\n",data);
// vTaskDelay(10);
}
}
//static void vCheckTask3( void *pvParameters )
//{
// uint16_t data;
// QueueSetMemberHandle_t hander;
// for( ;; )
// {
hander=xQueueSelectFromSet(xQueueUsartHandle,portMAX_DELAY);
xQueueReceive(hander,&data,portMAX_DELAY);
// printf("data:%d\r\n",data);
// vTaskDelay(10);
// }
//}
int main( void )
{
prvSetupHardware();
QueueUsart1=xQueueCreate(2,sizeof(uint16_t));
QueueUsart2=xQueueCreate(2,sizeof(uint16_t));
// SemaphoreHandle=xSemaphoreCreateCounting(10,0);
//
// xQueueUsartHandle=xQueueCreateSet(3);
// xQueueAddToSet(QueueUsart1,xQueueUsartHandle);
// xQueueAddToSet(QueueUsart2,xQueueUsartHandle);
xSemaMutexHandle=xSemaphoreCreateMutex();
xTaskCreate( vCheckTask1, "Check1", 100, NULL,1, &vCheckTask1_hander);
xTaskCreate( vCheckTask2, "Check2", 100, NULL,2, &vCheckTask2_hander);
// xTaskCreate( vCheckTask3, "Check3", 100, NULL,1, &vCheckTask3_hander);
vTaskStartScheduler();
return 0;
}
下图是debug的的调试结果:
FreeRTOS并没有实现“谁上锁就由谁开锁”的情况,只能额有程序员自己写代码的时候实现,上面的代码就验证了这个结论。
递归上锁实现了:谁上锁,就有谁解锁的功能。下面代码将证明任务函数是否能够打开不是自己的任务函数上的锁。
下面代码验证了这个问题,逻辑是:首先创建了一个递归锁,创建的时候会自动give一次(也就是传入一次),创建的任务函数task1的优先级大于task2的优先级,进入task1之后首先执行一次take(读出通道中的数据),执行了之后进入延时休眠函数,让出cpu资源,此时task2开始执行,首先执行一次take(读取通道中的数据,不添加等待时间),这个函数因为没有等待时间,会很快的退出, 如果退出了,就说明task2中不能解锁task1任务中上的锁,然后进入上锁的if判断语句中,之后开始解锁(向通道中发送数据),但是此时task1的锁还没有解开,不能进行解锁操作,所以就会卡死在后面的上锁操作,下面是测试代码:
static void vCheckTask1( void *pvParameters )
{
uint16_t data=1;
BaseType_t Taks_Flag;
for( ;; )
{
Taks_Flag=xSemaphoreTakeRecursive(xSemaRecursiveHandle,portMAX_DELAY);//刚开始创建的时候产生的锁数据
printf("take take1 one\r\n");
// xSemaphoreGiveRecursive(xSemaRecursiveHandle);
vTaskDelay(500);
for(int i=0;i<5;i++)
{
xSemaphoreGiveRecursive(xSemaRecursiveHandle);
printf("take take1 two\r\n");
xSemaphoreTakeRecursive(xSemaRecursiveHandle,portMAX_DELAY);
}
xSemaphoreGiveRecursive(xSemaRecursiveHandle);
}
}
static void vCheckTask2( void *pvParameters )
{
uint16_t data=2;
BaseType_t Taks_Flag;
if(xSemaphoreTakeRecursive(xSemaRecursiveHandle,0)!=pdTRUE) //刚开始创建的时候产生的锁数据
{
printf("take take2 error\r\n");
Taks_Flag=pdFALSE;
}
if(Taks_Flag!=pdTRUE)
{
if(xSemaphoreGiveRecursive(xSemaRecursiveHandle)!=pdTRUE);
printf("give take2 flas\r\n");
}
Taks_Flag=xSemaphoreTakeRecursive(xSemaRecursiveHandle,portMAX_DELAY);
printf("take take2 wate\r\n");
for( ;; )
{
printf(",,,,\r\n");
vTaskDelay(100);
}
}
int main( void )
{
prvSetupHardware();
xSemaRecursiveHandle=xSemaphoreCreateRecursiveMutex();
xTaskCreate( vCheckTask1, "Check1", 100, NULL,2, &vCheckTask1_hander);
xTaskCreate( vCheckTask2, "Check2", 100, NULL,1, &vCheckTask2_hander);
vTaskStartScheduler();
return 0;
}
下面的debug仿真:
如果task1中解锁上锁操作完了之后,task2就可以进行上锁解锁操作了,代码如下所示:
static void vCheckTask1( void *pvParameters )
{
uint16_t data=1;
BaseType_t Taks_Flag;
for( ;; )
{
Taks_Flag=xSemaphoreTakeRecursive(xSemaRecursiveHandle,portMAX_DELAY);//刚开始创建的时候产生的锁数据
printf("take take1 one\r\n");
xSemaphoreGiveRecursive(xSemaRecursiveHandle);
vTaskDelay(500);
for(int i=0;i<5;i++)
{
xSemaphoreGiveRecursive(xSemaRecursiveHandle);
printf("take take1 two\r\n");
xSemaphoreTakeRecursive(xSemaRecursiveHandle,portMAX_DELAY);
}
// xSemaphoreGiveRecursive(xSemaRecursiveHandle);
}
}
static void vCheckTask2( void *pvParameters )
{
uint16_t data=2;
BaseType_t Taks_Flag;
if(xSemaphoreTakeRecursive(xSemaRecursiveHandle,0)!=pdTRUE) //刚开始创建的时候产生的锁数据
{
printf("take take2 error\r\n");
Taks_Flag=pdFALSE;
}
if(Taks_Flag!=pdTRUE)
{
if(xSemaphoreGiveRecursive(xSemaRecursiveHandle)!=pdTRUE);
printf("give take2 flas\r\n");
}
Taks_Flag=xSemaphoreTakeRecursive(xSemaRecursiveHandle,portMAX_DELAY);
printf("take take2 wate\r\n");
for( ;; )
{
printf(",,,,\r\n");
vTaskDelay(100);
}
}
int main( void )
{
prvSetupHardware();
xSemaRecursiveHandle=xSemaphoreCreateRecursiveMutex();
xTaskCreate( vCheckTask1, "Check1", 100, NULL,2, &vCheckTask1_hander);
xTaskCreate( vCheckTask2, "Check2", 100, NULL,1, &vCheckTask2_hander);
vTaskStartScheduler();
return 0;
}
下面是debug的仿真测试: