C语言 Freertos的递归锁详解
时间:2022-07-11 10:14:35|栏目:C代码|点击: 次
1.死锁的概念
假设有 2 个互斥量 M1、 M2, 2 个任务 A、 B: A 获得了互斥量 M1 B 获得了互斥量 M2 A 还要获得互斥量 M2 才能运行,结果 A 阻塞 B 还要获得互斥量 M1 才能运行,结果 B 阻塞 A、 B 都阻塞,再无法释放它们持有的互斥量 死锁发生!
2.自我死锁
任务 A 获得了互斥锁 M 它调用一个函数 函数要去获取同一个互斥锁 M,于是它阻塞:任务 A 休眠,等待任务 A 来释放互斥锁! 死锁发生!
3.递归锁
1.任务 A 获得递归锁 M 后,它还可以多次去获得这个锁
2."take"了 N 次,要"give"N 次,这个锁才会被释放
3.谁上锁就由谁解锁。
递归锁的函数根一般互斥量的函数名不一样
递归锁 | 一般互斥量 | |
---|---|---|
创建 | xSemaphoreCreateRecursive Mutex |
xSemaphoreCreateMutex |
获得 | xSemaphoreTakeRecursive |
xSemaphoreTake |
释放 | xSemaphoreGiveRecursive |
xSemaphoreGive |
4.代码
main
/* 递归锁句柄 */ SemaphoreHandle_t xMutex; int main( void ) { prvSetupHardware(); /* 创建递归锁 */ xMutex = xSemaphoreCreateRecursiveMutex( ); if( xMutex != NULL ) { /* 创建2个任务: 一个上锁, 另一个自己监守自盗(开别人的锁自己用) xTaskCreate( vTakeTask, "Task1", 1000, NULL, 2, NULL ); xTaskCreate( vGiveAndTakeTask, "Task2", 1000, NULL, 1, NULL ); /* 启动调度器 */ vTaskStartScheduler(); } else { /* 无法创建递归锁 */ } return 0; }
任务1
static void vTakeTask( void *pvParameters ) { const TickType_t xTicksToWait = pdMS_TO_TICKS( 100UL ); BaseType_t xStatus; int i; /* 无限循环 */ for( ;; ) { /* 获得递归锁: 上锁 */ xStatus = xSemaphoreTakeRecursive(xMutex, portMAX_DELAY); printf("Task1 take the Mutex in main loop %s\r\n", \ (xStatus == pdTRUE)? "Success" : "Failed"); /* 阻塞很长时间, 让另一个任务执行, * 看看它有无办法再次获得递归锁 */ vTaskDelay(xTicksToWait); for (i = 0; i < 10; i++) { /* 获得递归锁: 上锁 */ xStatus = xSemaphoreTakeRecursive(xMutex, portMAX_DELAY); printf("Task1 take the Mutex in sub loop %s, for time %d\r\n", \ (xStatus == pdTRUE)? "Success" : "Failed", i); /* 释放递归锁 */ xSemaphoreGiveRecursive(xMutex); } /* 释放递归锁 */ xSemaphoreGiveRecursive(xMutex); } }
任务2
static void vGiveAndTakeTask( void *pvParameters ) { const TickType_t xTicksToWait = pdMS_TO_TICKS( 10UL ); BaseType_t xStatus; /* 尝试获得递归锁: 上锁 */ xStatus = xSemaphoreTakeRecursive(xMutex, 0); printf("Task2: at first, take the Mutex %s\r\n", \ (xStatus == pdTRUE)? "Success" : "Failed"); /* 如果失败则监守自盗: 开锁 */ if (xStatus != pdTRUE) { /* 无法释放别人持有的锁 */ xStatus = xSemaphoreGiveRecursive(xMutex); printf("Task2: give Mutex %s\r\n", \ (xStatus == pdTRUE)? "Success" : "Failed"); } /* 如果无法获得, 一直等待 */ xStatus = xSemaphoreTakeRecursive(xMutex, portMAX_DELAY); printf("Task2: and then, take the Mutex %s\r\n", \ (xStatus == pdTRUE)? "Success" : "Failed"); /* 无限循环 */ for( ;; ) { /* 什么都不做 */ vTaskDelay(xTicksToWait); } }
5.运行流程分析
1.任务 1 优先级最高,先运行,获得递归锁
2.任务 1 阻塞,让任务 2 得以运行
3.任务 2 运行,看看能否获得别人持有的递归锁: 不能
4.任务 2 故意执行"give"操作,看看能否释放别人持有的递归锁:不能
5.任务 2 等待递归锁
6.任务 1 阻塞时间到后继续运行,使用循环多次获得、释放递归锁
6.运行结果
总结
谁持有递归锁,必须由谁释放。