前言
FreeRTOS是一款开源的实时操作系统,提供了许多基本的内核对象,其中包括互斥锁(Mutex)。互斥锁是一种常用的同步机制,用于确保在同一时间内只有一个任务可以访问共享资源,防止竞态条件等并发问题。本文将介绍FreeRTOS中的互斥锁的使用方法和注意事项。
一、互斥量是什么?
当多个任务同时操作一个共享资源时,就可能出现竞态条件(Race Condition)的问题,导致数据错乱或不一致的情况。为了解决这个问题,FreeRTOS提供了互斥量(Mutex)的概念。
可以将互斥量类比为一把锁,只有拿到这把锁的任务才能操作共享资源,其他任务需要等待。互斥量的作用是保护共享资源,确保在同一时间内只有一个任务可以访问它。
具体来说,当一个任务需要访问共享资源时,它首先要尝试获取互斥量。如果互斥量没有被其他任务占用,那么这个任务就可以获得互斥量,相当于拿到了锁,并且可以继续执行。而如果互斥量已经被其他任务占用,那么这个任务就会被阻塞,等待互斥量可用。
一旦任务完成了对共享资源的操作,它需要释放互斥量,相当于释放了锁,这样其他任务就有机会获取互斥量,继续执行对共享资源的操作。
互斥量还支持嵌套,即一个任务在持有互斥量时,可以再次尝试获取互斥量。这样可以在多个层次上获取和释放互斥量,确保资源的正确使用。但要注意,在释放互斥量之前,必须相同次数地释放它,否则会导致其他任务无法获取到互斥量,可能导致死锁问题。
当谈到互斥量,一个生活化的比喻可以是门锁。
想象一下,你和其他人住在同一栋公寓楼里,每个人都有自己的房间。这些房间是共享资源,大家都会使用公共的厨房。现在,你正在做晚餐,需要使用炉灶和炒锅。由于只有一个炉灶和炒锅可供使用,你就安装了一个门锁来保护炉灶和炒锅,让其他人知道你正在使用它们。
在这个比喻中,互斥量就好像是这个门锁。只有先到达的人可以拥有锁的使用权,其他人需要等待锁被释放才能使用炉灶和炒锅。这样做的好处是,可以确保一次只有一个人使用炉灶和炒锅,避免混乱和争夺,同时保证了大家能有有序地使用共享资源的机会。
类似地,FreeRTOS中的互斥量可用于保护共享资源,只有一个任务可以获取互斥量的使用权,其他任务需要等待互斥量释放才能访问共享资源。这种机制保护了共享资源的完整性,避免了并发访问可能导致的竞态条件和数据不一致性问题。
希望这个比喻能够让互斥量的概念更加具体和易于理解。
总之,互斥量是FreeRTOS提供的一种同步机制,用于确保在同一时间内只有一个任务可以访问共享资源,避免了竞态条件的问题。它就像一把锁,任务需要获取锁才能操作共享资源,其他任务需要等待。通过合理使用互斥量,可以保证系统数据的一致性和正确性。
二、互斥量的使用场景
FreeRTOS的互斥量(Mutex)是一种非常有用的同步机制,用于保护共享资源、避免竞态条件和确保数据的一致性。下面是一些常见的使用场景:
共享资源保护: 当多个任务需要同时访问共享资源(如全局变量、设备IO等)时,互斥量可以用于保护这些资源,以确保在同一时间内只有一个任务可以访问。其他任务必须等待互斥量可用时才能进行访问,从而避免竞态条件和数据不一致性。
任务间通信: 互斥量可以用于任务间的通信和同步。例如,一个任务负责生成数据,另一个任务负责处理数据。生成数据的任务可以在生成后使用互斥量释放锁,通知处理数据的任务可以获取锁,开始处理数据。这种方式可以实现任务间的协作和同步。
资源分配与释放: 当有多个任务需要访问有限的资源时,互斥量可以用于分配和释放资源。例如,一块只能同时由一个任务访问的内存区域,可以使用互斥量来管理访问权限。任务需要访问内存时,首先尝试获取互斥量,成功获取则可以访问内存,否则需要等待。
临界区保护: 互斥量可以用于保护临界区,即一段关键的代码,确保在同一时间内只有一个任务可以执行该代码段。这对于需要保护关键数据或临界资源的操作非常重要,以确保数据的完整性和正确性。
任务优先级反转解决方案: 在任务优先级反转的情况下,互斥量可以用于解决该问题。任务优先级反转是指高优先级任务被低优先级任务所阻塞的情况,可能导致系统性能下降。通过使用互斥量,高优先级任务可以在访问共享资源之前阻塞低优先级任务,从而避免任务优先级反转。
这些是FreeRTOS互斥量的常见使用场景,通过合理地应用互斥量,可以保护共享资源、实现任务间的同步和协作,确保数据的一致性和系统的正确性。
三、互斥量的使用
互斥量其实就是一种特殊的二进制信号量。
1.创建
使用互斥量时,先创建、然后去获得、释放它。使用句柄来表示一个互斥量。
创建互斥量的函数有2种:动态分配内存,静态分配内存,函数原型如下:
/* 创建一个互斥量,返回它的句柄。 * 此函数内部会分配互斥量结构体 * 返回值: 返回句柄,非NULL表示成功 */ SemaphoreHandle_t xSemaphoreCreateMutex( void ); /* 创建一个互斥量,返回它的句柄。 * 此函数无需动态分配内存,所以需要先有一个StaticSemaphore_t结构体,并传入它的指针 * 返回值: 返回句柄,非NULL表示成功 */ SemaphoreHandle_t xSemaphoreCreateMutexStatic( StaticSemaphore_t *pxMutexBuffer );
要想使用互斥量,需要在配置文件FreeRTOSConfig.h中定义:
#define configUSE_MUTEXES 1
比如下图步骤:
1、搜索文件
2、开启互斥量
2.删除互斥量
使用vSemaphoreDelete
删除互斥量
/* * xSemaphore: 信号量句柄,你要删除哪个信号量, 互斥量也是一种信号量 */ void vSemaphoreDelete( SemaphoreHandle_t xSemaphore )
3.give和take
他们的参数和我们的信号量差不多,所以我在这就不写了。
give函数原型如下:
/* 释放 */ BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );
take函数原型如下:
/* 获得 */ BaseType_t xSemaphoreTake( SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait );
要注意的是,互斥量不能在ISR中使用。
四、示例代码
#include "FreeRTOS.h" #include "task.h" #include "semphr.h" // 共享资源 int sharedResource = 0; // 互斥量句柄 SemaphoreHandle_t mutex; // 任务函数1 void Task1(void *pvParameters) { while (1) { // 获取互斥量 xSemaphoreTake(mutex, portMAX_DELAY); // 在临界区内对共享资源进行操作 sharedResource++; printf("Task1: sharedResource = %d\n", sharedResource); // 释放互斥量 xSemaphoreGive(mutex); // 等待一段时间 vTaskDelay(pdMS_TO_TICKS(1000)); } } // 任务函数2 void Task2(void *pvParameters) { while (1) { // 获取互斥量 xSemaphoreTake(mutex, portMAX_DELAY); // 在临界区内对共享资源进行操作 sharedResource--; printf("Task2: sharedResource = %d\n", sharedResource); // 释放互斥量 xSemaphoreGive(mutex); // 等待一段时间 vTaskDelay(pdMS_TO_TICKS(1000)); } } int main(void) { // 创建互斥量 mutex = xSemaphoreCreateMutex(); // 创建任务 xTaskCreate(Task1, "Task1", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL); xTaskCreate(Task2, "Task2", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL); // 启动调度器 vTaskStartScheduler(); // 如果启动成功,将不会执行到这里 while(1); }
在串口中我们就可以看到打印出来的东西了。
总结
互斥锁是FreeRTOS提供的一个重要的同步机制,用于控制对共享资源的访问。通过仔细使用互斥锁,可以防止并发问题如竞态条件、死锁等的发生。本文简要介绍了在FreeRTOS中使用互斥锁的基本方法,包括创建、获取、释放和处理错误。在实际应用中,需要根据具体的场景和需求来合理使用互斥锁,确保系统的正确性和性能。