当前位置: 首页 > article >正文

立志最细,FreeRtos中的信号量Semaphore教程详解!!!

                        前言:本篇教程,参考韦东山,开发文档,连接放在最后   

目录

Semaphore基本概念

二值信号量(Binary Semaphore)

计数信号量(Couting Semaphore)

互斥信号量(Mutex)

信号量释放和使用函数


Semaphore基本概念

        信号量(Semaphore)是一种实现任务间通信的机制,可以实现任务之间的同步或者互斥访问,主要分为,二值信号量(Binary Semaphore)计数信号量(Couting Semaphore)互斥信号量(Mutex)。

        与queue传输数据不同的是Semaphore利用本身信号量计数值用来传递决定Task能不能进行访问的状态,同时用这种状态来完成,Task的同步,互斥,限制访问资源Task数量。

        信号量的API函数实际上使用的都是宏,使用来自现有的队列机制,二者也是有相似的地方的。

FreeRtos机制队列(Queue)信号量(Semaphore)
容纳数据可以容纳多个数据只能存放信号量计数值,对其进行操作
写操作Queue没有空间写数据可以阻塞计数值最大时,返回不进入阻塞状态
读操作Queue中没有数据可以阻塞没有信号量时可以阻塞

        表格里面需要注意的是,信号量(Semaphore)在进行释放的时候,如果达到规定值,这个时候是没有阻塞状态的,只有等待信号量的时候有阻塞状态

二值信号量(Binary Semaphore)

        这种类型信号量,调用函数创建,信号量计数值默认初始化为0,同时计数值,取值只有0 或1 ,通常用来实现Task之间的互斥。

SemaphoreHandle_t xSemaphoreCreateBinary( void ); //动态创建
SemaphoreHandle_t xSemaphoreCreateBinaryStatic( StaticSemaphore_t *pxSemaphoreBuffer );//静态创建 需要结构体类型存储信号量

        同样的Semaphore(信号量)的创建也分为动态创建和静态创建,区别也是静态创建需要结构体来存储数据。需要注意,这里在创建使用的时候,同时需要声明句柄,来创建,和后续通过句柄来完成对信号量的释放和使用操作。

static SemaphoreHandle_t Binary_Semaphore_Handle;//创建句柄承接创建函数的返回值
Binary_Semaphore_Handle = xSemaphoreCreateBinary();//使用函数创建二进制信号量

计数信号量(Couting Semaphore)

        技术型信号量最大的特定是,创建的时候可以确定,信号量最大值,与初始值,计数信号量,通常用来限制访问互斥资源的数量。

SemaphoreHandle_t xSemaphoreCreateCounting(UBaseType_t uxMaxCount, UBaseType_t uxInitialCount);//  uxMaxCount: 信号量最大值  uxInitialCount 信号量初始值
SemaphoreHandle_t xSemaphoreCreateCountingStatic( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount, StaticSemaphore_t *pxSemaphoreBuffer );

        同样的计数型信号量的创建也分为动态创建和静态创建,区别也是静态创建需要创建存放信号量的数据结构。

static SemaphoreHandle_t counting_Semaphore_Handle; //创建句柄
counting_Semaphore_Handle = xSemaphoreCreateCounting(3,2);//信号量最大值为3,初始值为2的计数型信号量

        这里调用需要注意的是,最大值是不能小于信号量的初始值的。

信号量二进制信号量量计数型信号量
动态创建xSemaphoreCreateBinary xSemaphoreCreateCounting
静态创建xSemaphoreCreateBinaryStaticxSemaphoreCreateCountingStatic
信号量初始值二进制信号量初始值为0计数型信号量初始值自己设定
释放申请删除操作相同操作相同

优先级反转问题

        优先级反转指的是,高优先级任务因为等待低优先级任务释放信号量进入阻塞,低优先级任务被中优先级任务抢占了CPU,没有办法执行任务释放,信号量导致了整体的系统延迟的情况。

        任务A(高优先级)

        任务B(低优先级)

        任务C(中优先级)

        任务B持有资源:任务B(低优先级)首先获得了一个共享资源,比如某个互斥信号量,用于保护一个共享数据。

        任务A请求资源:此时,任务A(高优先级)需要使用相同的共享资源,但由于任务B正在使用该资源,任务A必须等待任务B释放互斥信号量。于是,任务A进入阻塞状态,等待资源变得可用。

        任务C抢占CPU:在任务A等待资源的同时,任务C(中优先级)不依赖该资源,但由于它的优先级高于任务B,它会在任务A和任务B之间获得CPU的执行机会,抢占任务B的CPU时间。

        上面这种情况就会导致了,优先级反转问题的产生。

        使用二进制信号量在多任务系统访问资源中,有概率会出现这个问题,需要防止这个问题的出现,当创建Task的时候可以尽量避免,中优先级的任务创建,或者使用互斥信号量后者的优先级继承问题可以有效的避免,优先级反转问题的出现。

#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "stdio.h"

// 任务句柄
TaskHandle_t xHighPriorityTaskHandle, xMediumPriorityTaskHandle, xLowPriorityTaskHandle;

// 信号量句柄
SemaphoreHandle_t xMutex;

// 低优先级任务,持有共享资源
void vLowPriorityTask(void *pvParameters) {
    // 获取互斥信号量,模拟持有共享资源
    xSemaphoreTake(xMutex, portMAX_DELAY);
    printf("Low Priority Task: Obtained the mutex and using the resource.\n");

    // 模拟任务占用资源较长时间
    vTaskDelay(pdMS_TO_TICKS(2000));

    printf("Low Priority Task: Releasing the mutex.\n");
    // 释放互斥信号量
    xSemaphoreGive(xMutex);

    // 任务结束
    vTaskDelete(NULL);
}

// 高优先级任务,等待获取共享资源
void vHighPriorityTask(void *pvParameters) {
    vTaskDelay(pdMS_TO_TICKS(500)); // 延迟以确保低优先级任务先执行

    printf("High Priority Task: Attempting to obtain the mutex...\n");

    // 试图获取互斥信号量
    xSemaphoreTake(xMutex, portMAX_DELAY);
    printf("High Priority Task: Obtained the mutex and using the resource.\n");

    // 模拟资源使用时间
    vTaskDelay(pdMS_TO_TICKS(1000));

    // 释放互斥信号量
    xSemaphoreGive(xMutex);
    printf("High Priority Task: Released the mutex.\n");

    // 任务结束
    vTaskDelete(NULL);
}

// 中优先级任务,不需要共享资源,只是执行一段时间
void vMediumPriorityTask(void *pvParameters) {
    for (int i = 0; i < 5; i++) {
        printf("Medium Priority Task: Executing...\n");
        vTaskDelay(pdMS_TO_TICKS(500)); // 模拟占用CPU时间
    }
    
    // 任务结束
    vTaskDelete(NULL);
}

int main(void) {
    // 创建互斥信号量
    xMutex = xSemaphoreCreateMutex();

    if (xMutex != NULL) {
        // 创建低优先级任务,优先级为1
        xTaskCreate(vLowPriorityTask, "Low Priority Task", configMINIMAL_STACK_SIZE, NULL, 1, &xLowPriorityTaskHandle);

        // 创建高优先级任务,优先级为3
        xTaskCreate(vHighPriorityTask, "High Priority Task", configMINIMAL_STACK_SIZE, NULL, 3, &xHighPriorityTaskHandle);

        // 创建中优先级任务,优先级为2
        xTaskCreate(vMediumPriorityTask, "Medium Priority Task", configMINIMAL_STACK_SIZE, NULL, 2, &xMediumPriorityTaskHandle);

        // 启动调度器
        vTaskStartScheduler();
    }

    // 如果代码运行到这里,说明调度器未能启动
    for(;;);
}

互斥信号量(Mutex)

        互斥信号量(Mutex)是信号量的一种特殊类型,专门用来保护临界资源,确保只有一个任务可以访问该资源,特别的是互斥信号量拥有优先级继承机制,这种机制是二进制信号量和计数信号量没有的

        优先级继承机制特点在于,当低优先级人物持有互斥信号量,同时高优先级任务等待获取该信号量,低优先级任务会短暂提升自身优先级与高优先级任务优先级相同,直到释放互斥信号量,自身优先级会被取消,这个机制来保证因为任务优先级导致释放资源延迟程序的情况。

        通常用于保护共享资源,防止任务并发访问。特别适合用于需要防止数据竞争的场景,并且系统中可能存在优先级反转问题的情况。

        这种优先级集成机制,可以有效的避免优先级反转问题造成的,Task被阻止或者严重延迟。

SemaphoreHandle_t xSemaphoreCreateMutex( void );
SemaphoreHandle_t xSemaphoreCreateMutexStatic( StaticSemaphore_t *pxMutexBuffer );

        上面是创建Mutex的原型函数,同样的静态创建需要定义结构体。

SemaphoreHandle_t Mutex_Handle; //声明句柄
Mutex_Handle =  xSemaphoreCreateMutex();//创建Mutex

信号量操作函数

        上文提及了,信号量不同的类型,当创建完信号量之后还要通过对信号量进行释放,使用,删除来完成特定任务。

BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );//参数xSemaphore信号量句柄
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore,TickType_t xTicksToWait);//xTicksToWait 0:不阻塞, portMAX_DELAY: 一直阻塞直到成功 

        这里需要注意的是使用信号量函数,多一个参数可选,可以选择阻塞等待信号量不等待信号量,如果是在中断中使用则是下面的函数。

BaseType_t xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore,BaseType_t *pxHigherPriorityTaskWoken);
BaseType_t xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore,BaseType_t *pxHigherPriorityTaskWoken);

        需要注意的是,在二进制信号量中,多次使用释放信号量函数,二进制信号量的值依旧是1,不会因为多次调用函数,从而导致信号量值的提高。

        创建的信号量,在不需要的时候,同样的可以通过函数来删除,这里函数的参数是需要删除信号量的句柄。

void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );//里面的参数填写句柄

                                        欢迎指正,希望对你,有所帮助!!!

[9-2-2]_信号量实验_优先级反转_哔哩哔哩_bilibili


http://www.kler.cn/news/341240.html

相关文章:

  • Python 全栈开发从入门到实战进阶课程
  • AtCoder Beginner Contest 374 题A-D 详细题解(C++, Python)
  • 计算机视觉的应用36-人工智能时代计算机视觉技术在电力系统中的应用
  • Web Service Sender ParseError Invalid byte 1 of 1-byte UTF-8 sequence
  • 书籍第4章-SAP项目中的数据迁移
  • GO网络编程(六):海量用户通信系统4:读写数据包与登录消息处理
  • 运维问题0004:MM模块-操作MIGO过账报错“对象OFN_YR 2840 WE2840 的编码范围没有找到”
  • js 深入理解函数(二):扩展操作符、函数的内部对象、属性和方法
  • 【C/C++】错题记录(七)
  • 道可云入围2024元宇宙“数据要素”全国大赛
  • 【GESP】C++一级练习BCQM3035,实数计算,国庆七天乐
  • LeetCode讲解篇之79. 单词搜索
  • R语言绘制三维散点图
  • Ubuntu下v4l2采集摄像头视频
  • YOLO11改进|卷积篇|RFAConv创新空间注意力和标准卷积操作
  • OpenCV高级图形用户界面(4)获取鼠标滚轮事件的增量值函数getMouseWheelDelta()的使用
  • 毕业设计项目——基于RISC-V的标签化跨层调度应用任务管理(论文/代码)
  • SpringCloud面试题-SpringCloud主要的组件有哪些
  • 系统端口号被占用问题处理(WindowsLinux系统)
  • T8332FN凯钰Tmtech LED驱动芯片车规级AEC-Q100