FreeRTOS 信号量和互斥量

FreeRTOS 信号量和互斥量

信号量(Semaphore)也是一种任务间通信的常用方式,通常用于任务的同步。

#include <semphr.h>
 
BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore);
//产生一个信号,成功返回pdPASS
//信号量已满则失败,返回errQUEUE_FULL

BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore,TickType_t xBlockTime);
//获取一个信号,信号量为空则阻塞
//第二个参数是阻塞超时时间,成功返回pdTRUE,超时返回pdFALSE

值信号量是只有两个值(例如0或1、满或空)的信号量,FreeRTOS的二值信号量相当于只有一个元素的队列,用满和空表示信号量的值。

#include <semphr.h>
 
SemaphoreHandle_t xSemaphoreCreateBinary(void);
//创建一个二值信号量,成功返回信号量句柄,失败返回NULL
  • 二值信号量为空时调用xSemaphoreGive,信号量变满;为满时失败。
  • 二值信号量为满时调用xSemaphoreTake,信号量变空;为空时阻塞。

计数信号量顾名思义是计数用的信号量。

#include <semphr.h>
 
SemaphoreHandle_t xSemaphoreCreateCounting(void);
//创建一个计数信号量,成功返回信号量句柄,失败返回NULL
 
UBaseType_t uxSemaphoreGetCount(const QueueHandle_t xSemaphore);
//返回计数信号量的计数值
  • xSemaphoreGive使计数信号量加一。
  • xSemaphoreTake使计数信号量减一。

信号量通常用于任务同步,例如多个任务共享资源时,使用二值信号量进行访问控制,二值信号量为满则表示资源可用,二值信号量为空则表示资源正在被使用。也可以使用计数信号量计数有多少个任务在使用共享资源。

下面这个例子使用二值信号量标志串口是否被使用:

#include <stm32f4xx.h>
#include <FreeRTOS.h>
#include <task.h>
#include <queue.h>
#include <semphr.h>
#include <uart.h>
 
void task1(void* args);
void task2(void* args);
 
SemaphoreHandle_t binary;
 
int main()
{
    //配置USART1
    USART1_Config();
    //创建任务
    TaskHandle_t h1,h2;
    //创建二值信号量,用这个信号量标识串口是否可用
    binary = xSemaphoreCreateBinary();
    //使二值信号量变为满,表示资源可用
    xSemaphoreGive(binary);
    xTaskCreate(task1,"task1",configMINIMAL_STACK_SIZE,NULL,1,&h1);
    xTaskCreate(task2,"task2",configMINIMAL_STACK_SIZE,NULL,1,&h2);
    //开启任务调度
    vTaskStartScheduler();
    while(1);
}
 
 
void task1(void* args)
{
    int i = 0;
    while(1)
    {
        //循环五次后删除自己
        if(i >= 5)
        {
            vTaskDelete(NULL);
        }
        //使二值信号量变为空,表示资源被占用
        xSemaphoreTake(binary,100/portTICK_RATE_MS);
        USART_printf(USART1,"The task1 is running now.\n");
        //使二值信号量变为满,表示资源可用
        xSemaphoreGive(binary);
        i++;
    }
}
 
void task2(void* args)
{
    int i = 0;
    while(1)
    {
        //循环十次后删除自己
        if(i >= 10)
        {
            vTaskDelete(NULL);
        }
        //使二值信号量变为空,表示资源被占用
        xSemaphoreTake(binary,100/portTICK_RATE_MS);
        USART_printf(USART1,"The task2 is running now.\n");
        //使二值信号量变为满,表示资源可用
        xSemaphoreGive(binary);
        i++;
    }
}

Image

不过这种方式可能导致优先级反转:

(1)假设优先级task1>task2>task3  
(2)task1和task2阻塞或挂起  
(3)task3运行并通过二值信号量占用了某个公共资源  
(4)task1就绪并试图通过二值信号量使用被task3占用的资源,但由于资源被占用,task1阻塞  
(5)task2就绪,由于它优先级高于task3,因此task2优先执行  
(6)task2执行完之后task3继续执行  
(7)task3释放资源,task1解除阻塞并执行  

如此以来优先级相当于变成了task2>task1,原本优先级最高的task1不能及时运行,这在实时性要求高的场合是不被允许的。

互斥量(Mutex)是处理共享资源同步的常用方式。在FreeRTOS的实现中,互斥量是一种特殊的二值信号量,其操作方式和二值信号量一样,但互斥量具有优先级继承的特性。

(1)假设优先级task1>task2>task3  
(2)task1和task2阻塞或挂起  
(3)task3运行并通过互斥量占用了某个公共资源  
(4)task1就绪并试图通过互斥量使用被task3占用的资源,但由于资源被占用,task1阻塞  
(5)task3的优先级被提升到和task1一样  

如此一来就不会发生task2在task1之前运行的情况,提高了实时性。

xSemaphoreCreateMutex用于创建互斥量。

#include <semphr.h>
 
SemaphoreHandle_t xSemaphoreCreateMutex(void);
//创建一个互斥量,成功返回信号量句柄,失败返回NULL

假如任务中调用一个函数来使用公共资源,在调用这个函数前使用xSemaphoreTake占用该资源的互斥量,在这个函数返回后使用xSemaphoreGive来释放该资源的互斥量。但这个函数中可能也使用了xSemaphoreTakexSemaphoreGive来占用和释放资源。这种情况下就会导致错误。

递归互斥量可以让同一个任务多次占用资源,在释放同等次数互斥量后释放资源。

#include <semphr.h>
 
SemaphoreHandle_t xSemaphoreCreateRecursiveMutex(void);
//创建一个递归互斥量,成功返回信号量句柄,失败返回NULL
作者: PlanC
2024-12-18 20:03:48+08:00