【正点原子FreeRTOS学习笔记】————(12)信号量

这里写目录标题

  • 一、信号量的简介(了解)
  • 二、二值信号量(熟悉)
  • 三、二值信号量实验(掌握)
  • 四、计数型信号量(熟悉)
  • 五、计数型信号量实验(掌握)
  • 六、优先级翻转简介(熟悉)
  • 七、优先级翻转实验(掌握)
  • 八、互斥信号量(熟悉)
  • 九、互斥信号量实验(掌握)

一、信号量的简介(了解)

信号量是一种解决同步问题的机制,可以实现对共享资源的有序访问
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/cb7e774a20324bed9077316aa32a3c37.png

假设有一个人需要在停车场停车
1、首先判断停车场是否还有空车位(判断信号量是否有资源);
2、停车场正好有空车位(信号量有资源),那么就可以直接将车开入空车位进行停车(获取信号量成功);
3、停车场已经没有空车位了(信号量没有资源),那么这个人可以选择不停车(获取信号量失败);
也可以选择等待(任务阻塞)其他人将车开出停车场(释放信号量资源), 然后再将车停入空车位 。

在这里插入图片描述
当计数值大于0,代表有信号量资源;
当释放信号量,信号量计数值(资源数)加一;
当获取信号量,信号量计数值(资源数)减一

信号量的计数值都有限制:限定最大值。
如果最大值被限定为1,那么它就是二值信号量;
如果最大值不是1,它就是计数型信号量。

信号量:用于传递状态

队列与信号量的对比
在这里插入图片描述

二、二值信号量(熟悉)

二值信号量的本质是一个队列长度为 1 的队列 ,该队列就只有空和满两种情况,这就是二值。
二值信号量通常用于互斥访问或任务同步, 与互斥信号量比较类似,但是二值信号量有可能会导致优先级翻转的问题 ,所以二值信号量更适合用于同步!
在这里插入图片描述
二值信号量相关API函数(熟悉)
使用二值信号量的过程:创建二值信号量 、 释放二值信号量 、 获取二值信号量
在这里插入图片描述

创建二值信号量函数:SemaphoreHandle_t xSemaphoreCreateBinary( void )

#define   xSemaphoreCreateBinary( )   						\
 xQueueGenericCreate( 1 ,   semSEMAPHORE_QUEUE_ITEM_LENGTH  ,   queueQUEUE_TYPE_BINARY_SEMAPHORE )
#define  semSEMAPHORE_QUEUE_ITEM_LENGTH                 ( ( uint8_t ) 0U )
#define queueQUEUE_TYPE_BASE                  			( ( uint8_t ) 0U )	/* 队列 */
#define queueQUEUE_TYPE_SET                  			( ( uint8_t ) 0U )	/* 队列集 */
#define queueQUEUE_TYPE_MUTEX                 			( ( uint8_t ) 1U )	/* 互斥信号量 */
#define queueQUEUE_TYPE_COUNTING_SEMAPHORE    	        ( ( uint8_t ) 2U )	/* 计数型信号量 */
#define queueQUEUE_TYPE_BINARY_SEMAPHORE     	        ( ( uint8_t ) 3U )	/* 二值信号量 */
#define queueQUEUE_TYPE_RECURSIVE_MUTEX       		    ( ( uint8_t ) 4U )	/* 递归互斥信号量 */
 

在这里插入图片描述

释放二值信号量函数:BaseType_t xSemaphoreGive( xSemaphore )

#define   xSemaphoreGive (  xSemaphore  )    						\
xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore )  ,   NULL  ,   semGIVE_BLOCK_TIME  ,   queueSEND_TO_BACK )
#define   semGIVE_BLOCK_TIME                  ( ( TickType_t ) 0U )

在这里插入图片描述

获取二值信号量函数:BaseType_t xSemaphoreTake( xSemaphore, xBlockTime )

在这里插入图片描述

三、二值信号量实验(掌握)

1、实验目的:学习 FreeRTOS 的二值信号量相关API函数的使用
2、实验设计:将设计三个任务:start_task、task1、task2
三个任务的功能如下:
在这里插入图片描述

QueueHandle_t semphore_handle;
/**
 * @brief       FreeRTOS例程入口函数
 * @param       无
 * @retval      无
 */
void freertos_demo(void)
{    
    semphore_handle = xSemaphoreCreateBinary();
    if(semphore_handle != NULL)
    {
        printf("二值信号量创建成功!!!\r\n");
    }
    xTaskCreate((TaskFunction_t         )   start_task,
                (char *                 )   "start_task",
                (configSTACK_DEPTH_TYPE )   START_TASK_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   START_TASK_PRIO,
                (TaskHandle_t *         )   &start_task_handler );
    vTaskStartScheduler();
}

/* 任务一,释放二值信号量 */
void task1( void * pvParameters )
{
    uint8_t key = 0;
    BaseType_t err;
    while(1) 
    {
        key = key_scan(0);
        if(key == KEY0_PRES)
        {
            if(semphore_handle != NULL)
            {
                err = xSemaphoreGive(semphore_handle);
                if(err == pdPASS)
                {
                    printf("信号量释放成功!!\r\n");
                }else printf("信号量释放失败!!\r\n");
            }
            
        }
        vTaskDelay(10);
    }
}

/* 任务二,获取二值信号量 */
void task2( void * pvParameters )
{
    uint32_t i = 0;
    BaseType_t err;
    while(1)
    {
        err = xSemaphoreTake(semphore_handle,portMAX_DELAY); /* 获取信号量并死等 */
        if(err == pdTRUE)
        {
            printf("获取信号量成功\r\n");
        }else printf("已超时%d\r\n",++i);
        
    }
}




四、计数型信号量(熟悉)

计数型信号量相当于队列长度大于1 的队列,因此计数型信号量能够容纳多个资源,这在计数型信号量被创建的时候确定的
计数型信号量适用场合:

事件计数:当每次事件发生后,在事件处理函数中释放计数型信号量(计数值+1),其他任务会获取计数型信号量(计数值-1) ,这种场合一般在创建时将初始计数值设置为 0
资源管理:信号量表示有效的资源数目。任务必须先获取信号量(信号量计数值-1 )才能获取资源控制权。当计数值减为零时表示没有的资源。当任务使用完资源后,必须释放信号量(信号量计数值+1)。信号量创建时计数值应等于最大资源数目

使用计数型信号量的过程:创建计数型信号量 、释放信号量 、获取信号量
在这里插入图片描述
计数型信号量的释放和获取与二值信号量相同 !

计数型信号量创建API函数

#define 	xSemaphoreCreateCounting(  uxMaxCount  ,  uxInitialCount  )   		\		xQueueCreateCountingSemaphore( (  uxMaxCount  ) , (  uxInitialCount  ) ) 

此函数用于创建一个计数型信号量 。
在这里插入图片描述

#define 	uxSemaphoreGetCount( xSemaphore ) 						\	uxQueueMessagesWaiting( ( QueueHandle_t ) ( xSemaphore ) )

此函数用于获取信号量当前计数值大小
在这里插入图片描述

五、计数型信号量实验(掌握)

1、实验目的:学习 FreeRTOS 的计数型信号量相关API函数的使用
2、实验设计:将设计三个任务:start_task、task1、task2
三个任务的功能如下:
在这里插入图片描述

QueueHandle_t count_semphore_handle;
/**
 * @brief       FreeRTOS例程入口函数
 * @param       无
 * @retval      无
 */
void freertos_demo(void)
{    
    count_semphore_handle = xSemaphoreCreateCounting(100 , 0);  /* 创建计数型信号量 */
    if(count_semphore_handle != NULL)
    {
        printf("计数型信号量创建成功!!!\r\n");
    }
    xTaskCreate((TaskFunction_t         )   start_task,
                (char *                 )   "start_task",
                (configSTACK_DEPTH_TYPE )   START_TASK_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   START_TASK_PRIO,
                (TaskHandle_t *         )   &start_task_handler );
    vTaskStartScheduler();
}
/* 任务一,释放计数型信号量 */
void task1( void * pvParameters )
{
    uint8_t key = 0;
    while(1) 
    {
        key = key_scan(0);
        if(key == KEY0_PRES)
        {
            if(count_semphore_handle != NULL)
            {
                xSemaphoreGive(count_semphore_handle);      /* 释放信号量 */
            }
        }
        vTaskDelay(10);
    }
}

/* 任务二,获取计数型信号量 */
void task2( void * pvParameters )
{
    BaseType_t err = 0;
    while(1)
    {
        err = xSemaphoreTake(count_semphore_handle,portMAX_DELAY); /* 获取信号量并死等 */
        if(err == pdTRUE)
        {
            printf("信号量的计数值为:%d\r\n",(int)uxSemaphoreGetCount(count_semphore_handle));
        }
        vTaskDelay(1000);
    }
}


六、优先级翻转简介(熟悉)

优先级翻转:高优先级的任务反而慢执行,低优先级的任务反而优先执行
优先级翻转在抢占式内核中是非常常见的,但是在实时操作系统中是不允许出现优先级翻转的,因为优先级翻转会破坏任务的预期顺序,可能会导致未知的严重后果。
在使用二值信号量的时候,经常会遇到优先级翻转的问题。

举个例子:
在这里插入图片描述
高优先级任务被低优先级任务阻塞,导致高优先级任务迟迟得不到调度。但其他中等优先级的任务却能抢到CPU资源。从现象上看,就像是中优先级的任务比高优先级任务具有更高的优先权(即优先级翻转)

七、优先级翻转实验(掌握)

1、实验目的:在使用二值信号量的时候会存在优先级翻转的问题,本实验通过模拟的方式实现优先级翻转,观察优先级翻转对抢占式内核的影响
2、实验设计:将设计四个任务:start_task、high_task、 middle_task , low_task
四个任务的功能如下:
在这里插入图片描述

QueueHandle_t semphore_handle;
/**
 * @brief       FreeRTOS例程入口函数
 * @param       无
 * @retval      无
 */
void freertos_demo(void)
{    
    semphore_handle = xSemaphoreCreateBinary();
    if(semphore_handle != NULL)
    {
        printf("二值信号量创建成功!!!\r\n");
    }
    xSemaphoreGive(semphore_handle);        /* 释放一次信号量 */
    
    xTaskCreate((TaskFunction_t         )   start_task,
                (char *                 )   "start_task",
                (configSTACK_DEPTH_TYPE )   START_TASK_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   START_TASK_PRIO,
                (TaskHandle_t *         )   &start_task_handler );
    vTaskStartScheduler();
}

/* 任务一,低优先级任务 */
void low_task( void * pvParameters )
{
    while(1) 
    {
        printf("low_task获取信号量\r\n");
        xSemaphoreTake(semphore_handle,portMAX_DELAY);
        printf("low_task正在运行!!!\r\n");
        delay_ms(3000);
        printf("low_task释放信号量\r\n");
        xSemaphoreGive(semphore_handle); 
        vTaskDelay(1000);
    }
}

/* 任务二,中优先级任务 */
void middle_task( void * pvParameters )
{
    while(1)
    {
        printf("middle_task正在运行!!!\r\n");
        vTaskDelay(1000);
    }
}

/* 任务三,高优先级任务 */
void high_task( void * pvParameters )
{
    while(1)
    {
        printf("high_task获取信号量\r\n");
        xSemaphoreTake(semphore_handle,portMAX_DELAY);
        printf("high_task正在运行!!!\r\n");
        delay_ms(1000);
        printf("high_task释放信号量\r\n");
        xSemaphoreGive(semphore_handle); 
        vTaskDelay(1000);
    }
}



八、互斥信号量(熟悉)

互斥信号量其实就是一个拥有优先级继承的二值信号量,在同步的应用中二值信号量最适合。互斥信号量适合用于那些需要互斥访问的应用中!
优先级继承:当一个互斥信号量正在被一个低优先级的任务持有时, 如果此时有个高优先级的任务也尝试获取这个互斥信号量,那么这个高优先级的任务就会被阻塞。不过这个高优先级的任务会将低优先级任务的优先级提升到与自己相同的优先级。

优先级继承示例
在这里插入图片描述
此时任务H的阻塞时间仅仅是任务L 的执行时间,将优先级翻转的危害降到了最低

优先级继承并不能完全的消除优先级翻转的问题,它只是尽可能的降低优先级翻转带来的影响
注意:互斥信号量不能用于中断服务函数中,原因如下:
(1) 互斥信号量有任务优先级继承的机制, 但是中断不是任务,没有任务优先级, 所以互斥信号量只能用与任务中,不能用于中断服务函数。
(2) 中断服务函数中不能因为要等待互斥信号量而设置阻塞时间进入阻塞态。

互斥信号量相关API函数(熟悉)
使用互斥信号量:首先将宏configUSE_MUTEXES置1
使用流程:创建互斥信号量 、(task)获取信号量 、(give)释放信号量
在这里插入图片描述
互斥信号量的释放和获取函数与二值信号量相同 !只不过互斥信号量不支持中断中调用
注意:创建互斥信号量时,会主动释放一次信号量

互斥信号量创建API函数

#define   xSemaphoreCreateMutex()      xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )

此函数用于创建互斥信号量
在这里插入图片描述

九、互斥信号量实验(掌握)

1、实验目的:在优先级翻转实验的基础,加入互斥信号量,解决优先级翻转问题
2、实验设计:将优先级翻转所用到的信号量函数,修改成互斥信号量即可,通过串口打印提示信息

QueueHandle_t mutex_semphore_handle;
/**
 * @brief       FreeRTOS例程入口函数
 * @param       无
 * @retval      无
 */
void freertos_demo(void)
{    
    mutex_semphore_handle = xSemaphoreCreateMutex();    /* 创建互斥信号量,并且主动释放一次信号量 */
    if(mutex_semphore_handle != NULL)
    {
        printf("互斥信号量创建成功!!!\r\n");
    }
    
    xTaskCreate((TaskFunction_t         )   start_task,
                (char *                 )   "start_task",
                (configSTACK_DEPTH_TYPE )   START_TASK_STACK_SIZE,
                (void *                 )   NULL,
                (UBaseType_t            )   START_TASK_PRIO,
                (TaskHandle_t *         )   &start_task_handler );
    vTaskStartScheduler();
}

/* 任务一,低优先级任务 */
void low_task( void * pvParameters )
{
    while(1) 
    {
        printf("low_task获取信号量\r\n");
        xSemaphoreTake(mutex_semphore_handle,portMAX_DELAY);
        printf("low_task正在运行!!!\r\n");
        delay_ms(3000);
        printf("low_task释放信号量\r\n");
        xSemaphoreGive(mutex_semphore_handle); 
        vTaskDelay(1000);
    }
}

/* 任务二,中优先级任务 */
void middle_task( void * pvParameters )
{
    while(1)
    {
        printf("middle_task正在运行!!!\r\n");
        vTaskDelay(1000);
    }
}

/* 任务三,高优先级任务 */
void high_task( void * pvParameters )
{
    while(1)
    {
        printf("high_task获取信号量\r\n");
        xSemaphoreTake(mutex_semphore_handle,portMAX_DELAY);
        printf("high_task正在运行!!!\r\n");
        delay_ms(1000);
        printf("high_task释放信号量\r\n");
        xSemaphoreGive(mutex_semphore_handle); 
        vTaskDelay(1000);
    }
}



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/495926.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[优选算法专栏]专题十五:FloodFill算法(一)

本专栏内容为:算法学习专栏,分为优选算法专栏,贪心算法专栏,动态规划专栏以及递归,搜索与回溯算法专栏四部分。 通过本专栏的深入学习,你可以了解并掌握算法。 💓博主csdn个人主页:小…

缓冲区溢出漏洞相关知识点汇总

1.缓冲区基础知识相关定义 缓冲区定义:缓冲区一块连续的内存区域,用于存放程序运行时,加载到内存的运行代码和数据。 缓冲区溢出:缓冲区溢出是指程序运行时,向固定大小的缓冲区写入超过其容量的数据。多余的数据会越…

DFS:从递归去理解深度优先搜索

一、深入理解递归 二、递归vs迭代 三、深入理解搜索、回溯和剪枝 四、汉诺塔问题 . - 力扣&#xff08;LeetCode&#xff09; class Solution { public: //笔试题&#xff0c;不讲武德&#xff0c;CAvoid move(int n,vector<int>& A, vector<int>& B, ve…

充值活动倒计时!快来get您的春日豪礼

春分已至 万物生辉 就在上周末 马拉松赛事霸屏朋友圈 不论是燃动全城的汉马 还是集结万人的锡马 马拉松精神给予我们的是 挑战自我、永不言弃 奋力前行、昂扬向上的力量 在这万物复苏的阳春三月 正是潜心钻研 奋力拼搏的好时节 神工坊为广大仿真行业科技工作者 送上春…

净化室内空气有妙招,约克VRF甲醛净化中央空调给全家人舒适守护

早春3月&#xff0c;春回大地&#xff0c;又到了万物复苏、草长莺飞的季节&#xff0c;但对于我们的呼吸道来说&#xff0c;这又是个高危时期。伴随气温的不断上升&#xff0c;各种细菌、病毒开始活跃起来&#xff0c;同时&#xff0c;春季也是花粉过敏的高发期。无论是甲醛、细…

因子分析全流程汇总

探索性因子分析&#xff08;以下简称因子分析&#xff09;&#xff0c;是毕业论文中常用的数据分析方法&#xff0c;用于研究多个变量之间的关系&#xff0c;并通过提取公共因子来简化数据集。 信息浓缩是因子分析最常见的应用&#xff0c;除此之外&#xff0c;因子分析还可以…

2.3 同步与互斥

1 2 3 4 5 6 7 8 9 10 11 12

【InternLM 实战营第二期笔记】书生·浦语大模型全链路开源体系及InternLM2技术报告笔记

大模型 大模型成为发展通用人工智能的重要途径 专用模型&#xff1a;针对特定任务&#xff0c;一个模型解决一个问题 通用大模型&#xff1a;一个模型应对多种任务、多种模态 书生浦语大模型开源历程 2023.6.7&#xff1a;InternLM千亿参数语言大模型发布 2023.7.6&#…

【ML】类神经网络训练不起来怎么办 5

【ML】类神经网络训练不起来怎么办 5 1. Saddle Point V.S. Local Minima(局部最小值 与 鞍点)2. Tips for training: Batch and Momentum(批次与 动量)2.1 Tips for training: Batch and Momentum2.2 参考文献:2.3 Gradient Descent2.4 Concluding Remarks(前面三讲)3.…

2024年AI威胁场景报告:揭示现今最大的AI安全挑战

AI正彻底改变每一个数据驱动的机会&#xff0c;有可能带来一个繁荣的新时代&#xff0c;让人类的生活质量达到难以想象的高度。但就像任何突破性的新技术一样&#xff0c;伟大的潜力往往蕴含着巨大的风险。 AI在很大程度上是有史以来部署在生产系统中的最脆弱的技术。它在代码…

寒冬继续!飞书发全员信 “适当精简团队规模”

多精彩内容在公众号。 3月26日飞书CEO谢欣发布全员信&#xff0c;宣布进行组织调整&#xff0c;同时为受到影响的“同学”提供补偿方案和转岗机会。 在致员工的一封信中&#xff0c;谢欣坦诚地指出&#xff0c;尽管飞书的团队人数众多&#xff0c;但组织结构的不够紧凑导致了工…

使用HarmonyOS实现图片编辑,裁剪、旋转、亮度、透明度

介绍 本篇Codelab是基于ArkTS的声明式开发范式的样例&#xff0c;主要介绍了图片编辑实现过程。样例主要包含以下功能&#xff1a; 图片的解码。使用PixelMap进行图片编辑&#xff0c;如裁剪、旋转、亮度、透明度、饱和度等。图片的编码。 相关概念 图片解码&#xff1a;读取…

经典机器学习模型(九)EM算法的推导

经典机器学习模型(九)EM算法的推导 1 相关数据基础 1.1 数学期望 1.1.1 数学期望的定义 根据定义&#xff0c;我们可以求得掷骰子对应的期望&#xff1a; E ( X ) X 1 ∗ p ( X 1 ) X 2 ∗ p ( X 2 ) . . . X 6 ∗ p ( X 6 ) 1 ∗ 1 6 2 ∗ 1 6 1 ∗ 1 6 3 ∗ 1 6 …

【考研数学】跟武忠祥,如何搭配汤家凤《1800》?

可以但不建议&#xff01;正所谓原汤化原食&#xff0c;你做1800&#xff0c;当然是听汤神的更合适&#xff01; 汤家凤与武忠祥的讲课风格真的大不相同&#xff01;汤老师特别注重基础和题量&#xff0c;让你在数理思维上打下扎实的根基。而武老师则更偏向于深厚的理论&#…

天地图如何获取多边形面积

目录 一、初始化地图 二、创建polygonTool 三、多边形获取面积 ​四、完整代码&#xff08;包括添加点、添加面、编辑面、获取面积&#xff09; 项目中提出在地图上绘制面并获取面积&#xff0c;如何实现&#xff1f; 在天地图官网的JavaScript API 中&#xff0c;链接如下…

午马传动已确定加入2024第13届生物发酵展

参展企业介绍 浙江午马传动有限公司&#xff0c;办公室地址位于中国长寿之乡、中国椪柑之乡、中国竹炭之乡丽水&#xff0c;浙江省丽水市青田县东源镇项村村前路99号四楼1号&#xff0c;我公司主要提供&#xff1a;齿轮及齿轮减、变速箱制造&#xff1b;机械设备销售&#xff1…

MySQL 8 索引原理详细分析

千山万水总是情, 问问索引行不行? 轻舟已过万重山, 有种尽管来发难。 索引是在数据库优化时的重要手段之一,今天 V 哥从索引的角度展开讲一讲索引的各个要点,希望可以通过这篇文章,帮助大家彻底搞透索引的关键点。 1.索引的定义与作用2.索引的类型3.索引原理4.二分查…

C#学生信息成绩管理系统

一、系统功能描述 本系统包括两类用户&#xff1a;学生、管理员。管理员可以通过系统来添加管理员信息、修改管理员信息、添加学生信息、修改学生信息&#xff1b;开设课程、查询课程、录入成绩、统计成绩、修改成绩、修改个人密码等&#xff0c;而学生则可以通过系统来选择课…

实现DevOps需要什么?

实现DevOps需要什么&#xff1f; 硬性要求&#xff1a;工具上的准备 上文提到了工具链的打通&#xff0c;那么工具自然就需要做好准备。现将工具类型及对应的不完全列举整理如下&#xff1a; 代码管理&#xff08;SCM&#xff09;&#xff1a;GitHub、GitLab、BitBucket、SubV…

智过网:考一级建造师证有什么用?可以从事哪些工作?

随着国家基础设施建设的不断推进&#xff0c;建筑行业在中国经济中占据了举足轻重的地位。在这样的背景下&#xff0c;一级建造师证成为了众多建筑从业者的追求目标。那么&#xff0c;考取一级建造师证究竟有哪些用处&#xff1f;又能从事哪些工作呢&#xff1f;本文将对此进行…