【正点原子FreeRTOS学习笔记】————(12)信号量
这里写目录标题
- 一、信号量的简介(了解)
- 二、二值信号量(熟悉)
- 三、二值信号量实验(掌握)
- 四、计数型信号量(熟悉)
- 五、计数型信号量实验(掌握)
- 六、优先级翻转简介(熟悉)
- 七、优先级翻转实验(掌握)
- 八、互斥信号量(熟悉)
- 九、互斥信号量实验(掌握)
一、信号量的简介(了解)
信号量是一种解决同步问题的机制,可以实现对共享资源的有序访问

假设有一个人需要在停车场停车
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);}
}相关文章:
【正点原子FreeRTOS学习笔记】————(12)信号量
这里写目录标题 一、信号量的简介(了解)二、二值信号量(熟悉)三、二值信号量实验(掌握)四、计数型信号量(熟悉)五、计数型信号量实验(掌握)六、优先级翻转简介…...
【数据分享】1929-2023年全球站点的逐年平均露点(Shp\Excel\免费获取)
气象数据是在各项研究中都经常使用的数据,气象指标包括气温、风速、降水、能见度等指标,说到气象数据,最详细的气象数据是具体到气象监测站点的数据! 有关气象指标的监测站点数据,之前我们分享过1929-2023年全球气象站…...
PHP+MySQL开发组合:智慧同城便民信息小程序源码系统 带完整的安装代码包以及安装部署教程
当前,城市生活的节奏日益加快,人们对各类便民信息的需求也愈发迫切。无论是寻找家政服务、二手交易,还是发布租房、求职信息,一个高效、便捷的信息平台显得尤为重要。传统的信息发布方式往往存在信息更新不及时、查找困难等问题&a…...
Linux相关命令(1)
1、找出文件夹下包含 “aaa” 同时不包含 “bbb”的文件,然后把他们重新生成一下。要求只能用一行命令。 find ./ -type f -name "*aaa*" ! -name "*bbb*" -exec touch {} \;文件系统操作命令 df:列出文件系统的整体磁盘使用情况 …...
NO9 蓝桥杯单片机实践之串口通信的使用
1 回顾 串口通信的代码编写结构还是与中断一样,不同的是: 初始中断函数条件涉及到串口通信相关的寄存器和定时器1相关的寄存器(定时器1用于产生波特率),但初始条件中的中断寄存器只考虑串口通信而不考虑定时器1。 vo…...
数据库管理-第163期 19c重建ADG的两个方法(20240323
数据库管理163期 2024-03-23 数据库管理-第163期 19c重建ADG的两个方法(20240323)1 ORA-081032 新办法1 关闭MRP2 恢复备库3 其他操作4 启动备库5 启动MRP 3 老办法4 预告总结 数据库管理-第163期 19c重建ADG的两个方法(20240323)…...
8款常见的自动化测试开源框架
在如今开源的时代,我们就不要再闭门造车了,热烈的拥抱开源吧!本文针对性能测试、Web UI 测试、API 测试、数据库测试、接口测试、单元测试等方面,为大家整理了github或码云上优秀的自动化测试开源项目,希望能给大家带来…...
【QT】:基本框架
基本框架 一.创建程序二.初识函数1.main2.Widget.h3.Wight.cpp4.Wight.ui5.文件名.pro 三.生成的中间文件 本系列的Qt均使用Qt Creator进行程序编写。 一.创建程序 二.初识函数 1.main 2.Widget.h 3.Wight.cpp 4.Wight.ui 此时再点击编辑,就看到了ui文件的本体了。…...
【Python】定时更换clashx工具
An empty street An empty house A hole inside my heart I’m all alone The rooms are getting smaller I wonder how I wonder why I wonder where they are The days we had The songs we sang together Oh yeah And oh, my love I’m holding on forever Reaching for a l…...
2024年第16届大广赛新命题发布-爱华仕箱包
2024年3月27日,2024年第16届大广赛发布了新的命题,爱华仕箱包命题,自2017年起,爱华仕箱包已连续8年担任全国大学生广告艺术大赛命题单位。 爱华仕现已实现百货、超市、电商、礼品、投标、海外市场6大零售网络的全覆盖,…...
前端理论总结(js)——闭包和内存泄漏
闭包 什么是闭包? 函数内部和函数外部连接起来的桥梁,可以在一个内层函数中访问到其外层函数的作用域 为什么要用 封装变量 收敛权限 临时变量持久化 优点 1.保护函数内的变量安全 2.在内存中维持一个变量(用的太多就变成了缺点,…...
PHP页面如何实现设置独立访问密码
PHP网页如果需要查看信息必须输入密码,验证后才可显示出内容的代码如何实现? 对某些php页面设置单独的访问密码,如果密码不正确则无法查看内容,相当于对页面进行了一个加密。 如何实现这个效果,详细教程可以参考:PHP页面如何实现…...
M1 mac安装 Parallels Desktop 18 激活
M1 mac安装 Parallels Desktop 18 激活 下载安装Parallels Desktop 18.1.1 (53328) 激活1. 拷贝prl_disp_service2. 在终端打开Crack所在位置3. 输入命令,激活成功 下载 安装包和激活文件下载地址 链接: https://pan.baidu.com/s/1EjT7xeEDcntIIoOvvhBDfg?pwd9pue …...
嵌入式学习46——硬件相关2串口通信
串口: 端口: COM 波特率: 9600 115200 (bps) 每秒传输的数据…...
企业产品网络安全建设日志3月20
今天主要以下几个事情 确定了OCI当前情况下,更新某安全配置会导致链接中断 这问题说大不大,说小不小。 由此引申出各云服务器产品的完善度: AWS>OCI>其他 国内一些安全公司提供的WAF功能,缺点在于其全球的…...
BRICK POP展示了有趣的链上游戏玩法与奖励
新游戏BRICK POP将Sui区块链技术与低Gas费用,以及我们在Web3游戏开发方面的专业知识无缝结合。通过充分利用Sui和我们自己的INNO平台的优势,BRICK POP为玩家提供了一个融合了前沿技术和引人入胜游戏的沉浸式游戏体验。BRICK POP游戏设计为实时交易和高用…...
jetcache 2级缓存模式实现批量清除
需求 希望能够实现清理指定对象缓存的方法,例如缓存了User表,当User表巨大时,通过id全量去清理不现实,耗费资源也巨大。因此需要能够支持清理指定本地和远程缓存的批量方法。 分析 查看jetcache生成的cache接口,并没…...
【MD】激光驱动原子动力学的全尺寸从头算模拟
Zeng Q, Chen B, Zhang S, et al. Full-scale ab initio simulations of laser-driven atomistic dynamics[J]. npj Computational Materials, 2023, 9(1): 213.核心研究内容: 本文研究了激光驱动的原子动力学的全尺度从头算模拟。研究的重点是探讨在极端条件下材料…...
访问者模式(数据与行为解耦)
目录 前言 UML plantuml 类图 实战代码 SimpleFileVisitor FileVisitor 接口 删除指定文件夹 模板 IVisitor IVisitable Client 前言 一个类由成员变量和方法组成,成员变量即是类的数据结构,方法则是类的行为。 如果一个类的数据结构稳定&am…...
LeetCode:1319. 连通网络的操作次数(并查集 Java)
目录 1319. 连通网络的操作次数 题目描述: 实现代码与解析: 并查集 原理思路: 1319. 连通网络的操作次数 题目描述: 用以太网线缆将 n 台计算机连接成一个网络,计算机的编号从 0 到 n-1。线缆用 connections 表示…...
利用ngx_stream_return_module构建简易 TCP/UDP 响应网关
一、模块概述 ngx_stream_return_module 提供了一个极简的指令: return <value>;在收到客户端连接后,立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量(如 $time_iso8601、$remote_addr 等)&a…...
React Native 导航系统实战(React Navigation)
导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...
【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...
Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战
在现代战争中,电磁频谱已成为继陆、海、空、天之后的 “第五维战场”,雷达作为电磁频谱领域的关键装备,其干扰与抗干扰能力的较量,直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器,凭借数字射…...
浅谈不同二分算法的查找情况
二分算法原理比较简单,但是实际的算法模板却有很多,这一切都源于二分查找问题中的复杂情况和二分算法的边界处理,以下是博主对一些二分算法查找的情况分析。 需要说明的是,以下二分算法都是基于有序序列为升序有序的情况…...
基于 TAPD 进行项目管理
起因 自己写了个小工具,仓库用的Github。之前在用markdown进行需求管理,现在随着功能的增加,感觉有点难以管理了,所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD,需要提供一个企业名新建一个项目&#…...
Ubuntu Cursor升级成v1.0
0. 当前版本低 使用当前 Cursor v0.50时 GitHub Copilot Chat 打不开,快捷键也不好用,当看到 Cursor 升级后,还是蛮高兴的 1. 下载 Cursor 下载地址:https://www.cursor.com/cn/downloads 点击下载 Linux (x64) ,…...
Python 训练营打卡 Day 47
注意力热力图可视化 在day 46代码的基础上,对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...
[论文阅读]TrustRAG: Enhancing Robustness and Trustworthiness in RAG
TrustRAG: Enhancing Robustness and Trustworthiness in RAG [2501.00879] TrustRAG: Enhancing Robustness and Trustworthiness in Retrieval-Augmented Generation 代码:HuichiZhou/TrustRAG: Code for "TrustRAG: Enhancing Robustness and Trustworthin…...
