【正点原子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);}
}

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

23种设计模式之创建型模式 - 单例模式

文章目录 一、单例模式1.1单例模式定义1.2 单例模式的特点 二、实现单例模式的方式2.1 饿汉式2.2 懒汉式2.3 双重检查锁:2.4 静态内部类2.5 枚举实现(防止反射攻击): 一、单例模式 1.1单例模式定义 单例模式确保系统中某个类只有…

表格中的状态类型值(tag)

一&#xff1a;数字转换为简单的中文值 ** 不用转换直接用find()方法&#xff1a;在statusList里找&#xff1b; **lastHandleCode是对应的获取到的每行数据的code值&#xff1b; vue: <el-table-column label"执行状态" align"center"><templat…

Collection与数据结构 链表与LinkedList (一):链表概述与单向无头非循环链表实现

1.ArrayList的缺点 上篇文章我们已经对顺序表进行了实现,并且对ArrayList进行了使用,我们知道ArrayList底层是使用数组实现的. 由于其底层是一段连续空间&#xff0c;当在ArrayList任意位置插入或者删除元素时&#xff0c;就需要将后序元素整体往前或者往后搬移&#xff0c;时…

SpringMvc之映射器HandlerMapping

简介 在springmvc的处理流程中&#xff0c;第一步就是查询请求对应的映射器&#xff0c;然后组装成处理器链处理请求&#xff0c;本文意在梳理该过程 重要实现 HandlerMapping是一个接口&#xff0c;该接口用于通过HttpServletRequest寻找对应的处理器&#xff0c;接口介绍如下…

windows系统安装RabbitMQ

RabbitMQ是实现了高级消息队列协议&#xff08;AMQP&#xff09;的开源消息代理软件&#xff08;亦称面向消息的中间件&#xff09;。RabbitMQ服务器是用Erlang语言编写的&#xff0c;而集群和故障转移是构建在开放电信平台框架上的。所有主要的编程语言均有与代理接口通讯的客…

PL/SQL概述

oracle从入门到总裁:​​​​​​https://blog.csdn.net/weixin_67859959/article/details/135209645 PL/SQL概述 PL/SQL(Procedural Language extension to SQL)是 Oracle 对标准 SQL语言的扩充&#xff0c;是专门用于各种环境下对 Oracle 数据库进行访问和开发的语言。 由…

【有限状态机】- FSM详细讲解 【附Autoware有限状态机模型代码讲解】

参考博客&#xff1a; &#xff08;1&#xff09;FSM&#xff08;有限状态机&#xff09; &#xff08;2&#xff09;关于有限状态机(FSM)的一些思考 &#xff08;3&#xff09;状态设计模式 1 状态机简介 有限状态机FSM&#xff1a;有限个状态以及在这些状态之间的转移和动作…

文献阅读工具-->Adobe pdf + 有道词典

Adobe pdf 有道词典 最近一直在考虑用什么文献阅读工具&#xff0c;痛点无非就是想用翻译功能&#xff0c;Adobe pdf的添加注释已经很好用了&#xff0c;使用了zotero&#xff0c;感觉不行&#xff08;不能直接对原文件修改&#xff0c;有副本&#xff0c;麻烦&#xff09;。…

帆软报表在arm架构的linux

有朋友遇到一个问题在部署帆软报表时遇到报错。 问 我在 arm架构的linux服务器上部署帆软报表遇到了一个棘手的问题&#xff0c;你有空帮忙看下嘛。 我看后台日志报的错是 需要升级 gcc、libmawt.so &#xff0c;是系统中缺少Tomcat需要的依赖库&#xff0c;你之前处理过类似…

服务器监控软件夜莺采集监控(三)

文章目录 一、采集器插件1. exec插件2. rabbitmq插件3. elasticsearch插件 二、监控仪表盘1. 系统信息2. 数据服务3. NginxMQ4. Docker5. 业务日志 一、采集器插件 1. exec插件 input.exec/exec.toml [[instances]] commands ["/home/monitor/categraf/scripts/*.sh&q…

6.二叉树——2.重建树

已知先序和中序序列 根据先序序列找到树根根据树根和中序序列找到左右子树 同理根据后序序列和中序序列也能重构树&#xff0c;但前序和后序不可以 递归coding思路 设先序序列为preorder[n]&#xff0c;中序序列为midorder[n] 大事化小&#xff1a; 确定根&#xff0c;即树…

uniapp写小程序如何实现分包

众所众知小程序上传的过程中对包的大小有限制&#xff0c;正常情况下不允许当个包超过2M&#xff0c;所以需要分包 需要再pages.json这个文件夹中进行配置 "pages": [{"path": "pages/index/index","style": {"navigationBarTit…