【FreeRTOS学习 - 互斥量学习 (盗锁)】
跟着韦东山老师FreeRTOS教学资料的学习记录
FreeRTOS全部项目代码链接(更新中)
https://gitee.com/chenshao777/free-rtos_-study
了解互斥量的使用场景
例如多个任务对同一个数组或者变量进行操作时,往往会发生冲突,可能任务A访问arr数组的过程中被任务B打断,任务B恰好也对arr数组进行了操作,当任务A继续执行时,此时的arr数组已经不是当时的arr数组了,这就造成了访问冲突。
即非原子化访问,也就是操作的过程可能被打断
为了避免此问题,可以在访问变量前,上锁,访问之后,开锁
- 即A使用变量前上个锁
- A使用完再开锁
- 再此期间B必须等A开锁后才能使用该变量,这样就不会产生访问冲突了
- 但实际上FreeRTOS的代码并没有实现谁上的锁只能由谁来开
- 所以A上锁后,B实际上也是可以自己开锁的
- 所以谁上锁,谁才能开锁需要由程序员自己约定了
- 这个锁就是我们说的互斥量,互斥量是一种特殊的二进制信号量。
要想使用互斥量,需要在配置文件FreeRTOSConfig.h中定义:
#define configUSE_MUTEXES 1
一、创建互斥量
二、互斥量的操作函数(获取、释放、删除)
三、互斥量使用实例
四、盗锁
一、创建互斥量
QueueHandle_t xSemaphoreMutex; //互斥量句柄/* 创建互斥量,返回它的句柄 */
xSemaphoreMutex = xSemaphoreCreateMutex();
二、互斥量的操作函数(获取、释放、删除)
/** xSemaphore: 信号量句柄,你要删除哪个信号量, 互斥量也是一种信号量*/
void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );/* 释放 */
BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );/* 释放(ISR版本) */
BaseType_t xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore,BaseType_t *pxHigherPriorityTaskWoken);/* 获取 */
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore,TickType_t xTicksToWait);
/* 获取 (ISR版本) */
xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore,BaseType_t *pxHigherPriorityTaskWoken);
三、互斥量使用实例
创建两个任务
- 任务1一直发送0~99数字
- 任务2一直发送100~199数字
1. 不使用互斥量的情况(USE_Mutex_Flag 置0)
实验结果,发送数据混乱在一起:
2. 使用互斥量的情况(USE_Mutex_Flag 置1)
实验结果,发送数据不产生错乱了:
代码如下:
void systemInit(void); //各模块初始化QueueHandle_t xSemaphoreMutex; //互斥量句柄void systemInit(void); //各模块初始化QueueHandle_t xSemaphoreMutex; //互斥量句柄/* 使用互斥量标志 */
#define USE_Mutex_Flag 0 /** 任务1一直发送0~99数字
*/
void vSemphrMutex_Task1(void *pvParameters)
{const TickType_t xDelay = 50 / portTICK_PERIOD_MS;BaseType_t result;for(;;){#if (USE_Mutex_Flag == 1)/* 上锁 */xSemaphoreTake(xSemaphoreMutex, portMAX_DELAY);#endif/* 打印 0 ~ 99 */for(int i=0; i < 100 ; i++){printf("%d ", i);}printf("\\r\\n");#if (USE_Mutex_Flag == 1)/* 开锁 */xSemaphoreGive(xSemaphoreMutex);#endifvTaskDelay(xDelay);}
}/** 任务2一直发送100~199数字
*/
void vSemphrMutex_Task2(void *pvParameters)
{const TickType_t xDelay = 50 / portTICK_PERIOD_MS;BaseType_t result; //获取信号量结果变量for(;;){#if (USE_Mutex_Flag == 1)/* 上锁 */xSemaphoreTake(xSemaphoreMutex, portMAX_DELAY);#endif/* 打印 100 ~ 199 */for(int i=100; i < 200 ; i++){printf("%d ", i);}printf("\\r\\n");#if (USE_Mutex_Flag == 1)/* 开锁 */xSemaphoreGive(xSemaphoreMutex);#endifvTaskDelay(xDelay);}
}int main()
{systemInit(); //初始化各个模块// 互斥量任务一xTaskCreate(vSemphrMutex_Task1, "vSemphrMutex_Task1", 200, NULL, 2, NULL);// 互斥量任务二xTaskCreate(vSemphrMutex_Task2, "vSemphrMutex_Task2", 200, NULL, 2, NULL);vTaskStartScheduler(); //开启任务调度
}/** 初始化各个模块
*/
void systemInit(void)
{SysTick_Init(72);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);USART1_Init(115200);printf("串口初始化成功!\\r\\n");/* 创建互斥量 */xSemaphoreMutex = xSemaphoreCreateMutex();
}
四、盗锁
-
任务2上锁后,开始打印
-
任务1来上锁(Take),发现失败,不等待
-
任务1直接开锁(Give),简称盗锁(因为本来约定谁上的锁只能由谁开)
-
任务1开锁(Give)成功,开始打印
-
由于优先级相同,任务1和2交替打印
- 任务1盗锁代码如下:
- 盗锁结果: