学习笔记–RTOS信号量

本文基于正点原子RTOS开发指南,笔记自用,获取详细信息请关注正点原子官方账号

简介:信号量是一种解决同步问题的机制,可以实现对共享资源的有序访问。和linux中的信号量差不多,描述资源数量,在使用资源时看剩余资源的数量,使用时将资源数量减一。

信号量与队列的区别

队列 信号量
可以容纳多个数据; 创建队列有两部分内存:队列结构体+队列项存储空间 仅存放计数值,无法存放其他数据; 创建信号量,只需分配信号量结构体
写入队列:当队列满时,可阻塞; 释放信号量:不可阻塞,计数值++, 当计数值为最大值时,返回失败
读取队列:当队列为空时,可阻塞; 获取信号量:计数值–, 当没有资源时,可阻塞

二值信号量

简介:信号量的计数值有限制,描述资源的数量,如果其最大值限定为1,也就是其资源数量为1,那么其就是二值信号量如果最大值不是1,就是计数型信号量

二值信号量的本质是一个队列长度为 1 的队列 ,该队列就只有空和满两种情况,这就是二值。

用途:二值信号量通常用于互斥访问任务同步, 与互斥信号量比较类似,但是二值信号量有可能会导致优先级翻转的问题 ,所以二值信号量更适合用于同步

二值信号量API函数

使用二值信号量的过程:创建二值信号量 à 释放二值信号量 à 获取二值信号量

函数 描述
xSemaphoreCreateBinary() 使用动态方式创建二值信号量
xSemaphoreCreateBinaryStatic() 使用静态方式创建二值信号量
xSemaphoreGive() 释放信号量
xSemaphoreGiveFromISR() 在中断中释放信号量
xSemaphoreTake() 获取信号量
xSemaphoreTakeFromISR() 在中断中获取信号量

创建二值信号量函数

SemaphoreHandle_t   xSemaphoreCreateBinary( void ) /*函数实现*/#define   xSemaphoreCreateBinary( )                           \xQueueGenericCreate( 1 ,   semSEMAPHORE_QUEUE_ITEM_LENGTH  , queueQUEUE_TYPE_BINARY_SEMAPHORE )

实际上是创建一个队列,队列的类型时下面的5

#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 )  /* 递归互斥信号量 */

返回值:成功返回信号量的句柄,失败时返回NULL

释放二值信号量

BaseType_t   xSemaphoreGive( xSemaphore )
#define   xSemaphoreGive (  xSemaphore  )                           \
xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore )  ,   NULL  ,   semGIVE_BLOCK_TIME  , queueSEND_TO_BACK )

实际上是队列发送函数,只不过类型是queueSEND_TO_BACK

参数:xSemaphore :需要释放的信号量句柄 semGIVE_BLOCK_TIME 阻塞时间,默认为0

返回值:成功返回pdPASS,失败返回 errQUEUE_FULL

获取二值信号量

BaseType_t   xSemaphoreTake( xSemaphore, xBlockTime )

参数介绍:xSemaphore:要操作的信号量句柄 xBlockTime:阻塞等待时间,超出该时间内为获取到信号量则直接返回错误。

返回值:pdTRUE:获取信号量成功 pdFALSE:超时,获取信号量失败

示例

/*初始化信号量*/
QueueHandle_t sem;//创建信号量句柄sem = xSemaphoreCreateBinary();if(sem == NULL){printf("Create binary sem error!\r\n");}printf("Create binary sem success!\r\n");

获取信号量以及释放信号量

/*获取信号量*/
if(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin) == GPIO_PIN_SET){while(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin) == GPIO_PIN_SET);printf("key0 press!\r\n");/*Get binary sem*/err = xSemaphoreTake(sem,portMAX_DELAY);if(err == pdFALSE){printf("Get Binary sem error!\r\n");}printf("Get Binary sem success!\r\n");}
/*释放信号量*/
if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin) == GPIO_PIN_SET){while(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin) == GPIO_PIN_SET);printf("key1 press!\r\n");ret = xSemaphoreGive(sem);if(ret == pdFALSE){printf("Relax Binary sem error!\r\n");}printf("Relax Binary sem success!\r\n");}

计数型信号量

简介:计数型信号量相当于队列长度大于1的队列,表示有多个资源。

应用场景:

  • 【事件计数】当每次事件发生后,在事件处理函数中释放计数型信号量(计数值+1),其他任务

    会获取计数型信号量(计数值-1) ,这种场合一般在创建时将初始计数值设置为 0

  • 【资源管理】信号量表示有效的资源数目。任务必须先获取信号量(信号量计数值-1 )才能获取资源控制权。当计数值减为零时表示没有的资源。当任务使用完资源后,必须释放信号量(信号量计数值+1)。信号量创建时计数值应等于最大资源数目

我的理解:对于事件计数型信号量,初始化为0,每有一次资源,那么信号量就加一,

对于资源管理型信号量,初始化为资源的数目,资源在使用时信号量减一,用完时信号量加一

计数型信号量相关API函数

函数 描述
xSemaphoreCreateCounting() 使用动态方法创建计数型信号量。
xSemaphoreCreateCountingStatic() 使用静态方法创建计数型信号量
uxSemaphoreGetCount() 获取信号量的计数值

计数型信号量的释放和获取和二值型信号量的获取和释放方法相同

动态创建计数型信号量

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

参数:uxMaxCount:计数型信号量的数量最大值限定,uxInitialCount:计数型信号量的初值

返回值:创建成功返回信号量的任务句柄,失败时返回NULL

获取信号量的计数值

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

参数:要获取的信号量句柄

返回值:信号量的资源数目

示例

/*初始化信号量*/
QueueHandle_t sem;//信号量句柄
UBaseType_t SemMaxCount=10;//最大信号量数量
UBaseType_t SemInitialCount=1;//初始化信号量数量/*Init Semaphore for the type of count*/sem = xSemaphoreCreateCounting(SemMaxCount,SemInitialCount);if(sem == NULL){printf("Create count sem error!\r\n");}else{printf("Create count sem success!\r\n");}

计数型信号量的申请和释放

/*申请信号量*/
if(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin) == GPIO_PIN_SET){while(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin) == GPIO_PIN_SET);printf("key0 press!\r\n");/*Get count sem*/err = xSemaphoreTake(sem,Waittime);if(err == pdFALSE){printf("Get count sem error!\r\n");}else{printf("Get count sem success!\r\n");}}
/*释放信号量*/
if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin) == GPIO_PIN_SET){while(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin) == GPIO_PIN_SET);printf("key1 press!\r\n");ret = xSemaphoreGive(sem);if(ret == pdFALSE){printf("Relax count sem error!\r\n");}else{printf("Relax count sem success!\r\n");}}
/*查询信号量*/
UBaseType_t CurrentSemCount;
CurrentSemCount = uxSemaphoreGetCount(sem);
printf("CurrentSemCount:%d\r\n",CurrentSemCount);

效果

CurrentSemCount:1
CurrentSemCount:1
CurrentSemCount:1key0 press!
Get count sem success!
CurrentSemCount:0
CurrentSemCount:0
CurrentSemCount:0key1 press!
Relax count sem success!
CurrentSemCount:1key1 press!
Relax count sem success!
CurrentSemCount:2key1 press!
Relax count sem success!
CurrentSemCount:3key1 press!
Relax count sem success!
CurrentSemCount:4
CurrentSemCount:4key1 press!
Relax count sem success!
CurrentSemCount:5key0 press!
Get count sem success!
CurrentSemCount:4
CurrentSemCount:4key1 press!
Relax count sem success!
CurrentSemCount:5
CurrentSemCount:5

初始化的资源数为1,按下KEY0后资源数-1 按下KEY1后资源数加一

优先级翻转

简介:优先级翻转是指 高优先级的任务反而慢执行,低优先级的任务反而优先执行,

优先级翻转在抢占式内核中是非常常见的,但是在实时操作系统中是不允许出现优先级翻转的,因为优先级翻转会破坏任务的预期顺序,可能会导致未知的严重后果。

分析:任务L优先级低,首先获取到信号量,而此时H运行,准备抢占信号量,但是信号量在L手中未释放,于是H进入阻塞状态,而此时M已经处于就绪态,其优先级比L高,会抢占L 而此时L中的信号量并没有释放,所以一直等到M执行完毕,回到L执行完毕后释放信号量,然后H才能运行。

等于说,虽然是高优先级的H 但因为其把柄在L手中,而M又比L大,所以导致其最后运行,这就是优先级翻转。

互斥信号量

简介:互斥信号量其实就是一个拥有优先级继承二值信号量,在同步的应用中二值信号量最适合。互斥信号量适合用于那些需要互斥访问的应用中!

优先级继承:当一个互斥信号量正在被一个低优先级的任务持有时, 如果此时有个高优先级的任务也尝试获取这个互斥信号量,那么这个高优先级的任务就会被阻塞。不过这个高优先级的任务会将低优先级任务的优先级提升到与自己相同的优先级。

示例:在低优先级任务获取到信号量,高优先级任务想要获取信号量时,会把低优先级的任务的优先级提高到高优先级,此时L继续运行,因为与H同优先级,而M进入就绪状态,一直等到L运行完毕,释放出信号量,好处是避免了优先级反转中M 抢占L导致信号量迟迟不会被释放,因为L的优先级比M高,所以不会被抢占。

注意:

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

互斥信号量相关API

使用互斥信号量,首先将configUSE_MUTEXES设置为1

函数 描述
xSemaphoreCreateMutex() 使用动态方法创建互斥信号量。
xSemaphoreCreateMutexStatic() 使用静态方法创建互斥信号量。

互斥信号量的释放和获取函数与二值信号量相同 !**只不过互斥信号量不支持中断中调用 **

注意:**创建互斥信号量时,会主动释放一次信号量 **

互斥信号量实现

#define   xSemaphoreCreateMutex()      xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )

返回值:创建成功返回互斥信号量的句柄 失败返回NULL

示例

/*初始化信号量句柄*/
QueueHandle_t mutex_sem;/*初始话互斥信号量*/mutex_sem = xSemaphoreCreateMutex();if(mutex_sem == NULL){printf("Create mutex sem error!\r\n");}else{printf("Create mutex sem success!\r\n");}

任务中获取、释放信号量

/*task1的优先级为3 属于低优先级,通过安按键KEY0首先获取到了信号量,并在10s中后释放信号量*/
void task1(void *argv)
{TickType_t Waittime = 10;uint16_t times;BaseType_t ret;uint8_t flag=pdFALSE;printf("task1 runing!\r\n");while(1){if(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin) == GPIO_PIN_SET){while(HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin) == GPIO_PIN_SET);printf("task1 key0 press!\r\n");/*Get binary sem*/ret = xSemaphoreTake(mutex_sem,Waittime);if(ret == pdFALSE){printf("task1 Get mutex sem error!\r\n");}else{printf("task1 Get mutex sem success!\r\n");flag = pdTRUE;}}if(flag == pdTRUE){times++;}if(times==1000){printf("relax mutex sem\r\n");ret = xSemaphoreGive(mutex_sem);if(ret == pdFALSE){printf("task2 Relax mutex sem error!\r\n");}else{printf("task2 Relax mutex sem success!\r\n");}}vTaskDelay(10);}}
/*Task2 属于高优先级任务优先级为5 通过KEY1按键获取信号量*/
void task2(void *argv)
{BaseType_t ret;printf("task2 runing!\r\n");while(1){if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin) == GPIO_PIN_SET){while(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin) == GPIO_PIN_SET);printf("task2 key0 press!\r\n");/*Get binary sem*/ret = xSemaphoreTake(mutex_sem,portMAX_DELAY);if(ret == pdFALSE){printf("task2 Get mutex sem error!\r\n");}else{printf("task2 Get mutex sem success!\r\n");}}vTaskDelay(10);}}
/*Task3 属于中优先级的任务,优先级为4 负责打印优先级和信号量的数目*/
void task3(void *argv)
{UBaseType_t CurrentSemCount;UBaseType_t taskpriority;printf("task3 runing!\r\n");while(1){HeartBeatLED0();vTaskDelay(1000);CurrentSemCount = uxSemaphoreGetCount(mutex_sem);printf("CurrentSemCount:%d\r\n",CurrentSemCount);printf("Task1 priority:%d\t Task2 Priority:%d\r\n",uxTaskPriorityGet(task1_handler),uxTaskPriorityGet(task2_handler));}}

效果

/*初始化信号量*/
Create mutex sem success!
task2 runing!
task3 runing!
task1 runing!/*初始化信号量情况,信号量数目为1 Task1优先级为3 Task2优先级为5*/
CurrentSemCount:1
Task1 priority:3     Task2 Priority:5
CurrentSemCount:1
Task1 priority:3     Task2 Priority:5
CurrentSemCount:1
Task1 priority:3     Task2 Priority:5/*KEY0 按下低优先级的任务获取到了信号量,此时资源变为0 优先级不变*/
task1 key0 press!
task1 Get mutex sem success!
CurrentSemCount:0
Task1 priority:3     Task2 Priority:5
CurrentSemCount:0
Task1 priority:3     Task2 Priority:5
CurrentSemCount:0
Task1 priority:3     Task2 Priority:5
CurrentSemCount:0
Task1 priority:3     Task2 Priority:5/*key1按下,此时属于优先级的任务获取信号量,此时Task2 阻塞(超时时间设置为死等),并且调高了低优先级任务Task1的优先级 和自己Task2一样为5*/
task2 key1 press!
CurrentSemCount:0
Task1 priority:5     Task2 Priority:5
CurrentSemCount:0
Task1 priority:5     Task2 Priority:5
CurrentSemCount:0
Task1 priority:5     Task2 Priority:5
CurrentSemCount:0
Task1 priority:5     Task2 Priority:5
CurrentSemCount:0
Task1 priority:5     Task2 Priority:5/*10s时间到,低优先级释放了信号量*/
relax mutex sem
/*高优先级得到了信号量*/
task2 Get mutex sem success!
task2 Relax mutex sem success!/*被调高优先级的Task1优先级回到原来的3 因为资源被Task2 高优先级抢占,所以资源数目还是0*/
CurrentSemCount:0
Task1 priority:3     Task2 Priority:5
CurrentSemCount:0
Task1 priority:3     Task2 Priority:5

学习笔记--RTOS信号量相关推荐

  1. freeRtos学习笔记 (7)信号量

    freeRtos学习笔记 freeRtos信号量 信号量种类 信号量分为四种:二值信号量,互斥信号量,计数信号量和递归互斥信号量,其中计数信号量用于管理系统多个共享资源,用计数值表示可用资源数目;二值 ...

  2. Linux 学习笔记16 信号量

    Linux 学习笔记16 信号量Semaphore 信号量概念 信号量(或信号灯)是一种用于提供不同进程间或一个给定进程的不同线程间同步手段的原语. 信号量是控制进程(或线程)同步(谁先执行,谁后执行 ...

  3. RTOS学习笔记--RTOS系统介绍

    RTOS学习笔记–RTOS介绍 本文基于正点原子RTOS开发指南,笔记自用,获取详细信息请关注正点原子官方账号 RTOS特点:RTOS全称为:Real Time OS,就是实时操作系统,强调的是:实时 ...

  4. 哈工大操作系统学习笔记十——信号量与死锁

    哈工大os学习笔记十(信号量与死锁) 文章目录 哈工大os学习笔记十(信号量与死锁) 一. 信号量临界区保护 1.为什么要保护信号量 2.临界区 3.保护信号量的方法 3.1 轮换法 3.2 标记法 ...

  5. FreeRTOS学习笔记之信号量

    一.创建信号量: 1. 动态创建信号量(自动分配内存): xSemaphoreCreateMutex:具有优先级继承关系,创建互斥信号量(一般用于保护全局变量,防止被意外修改): xSemaphore ...

  6. RTT Nano学习笔记 8 - 信号量

    目录 1. 创建 2. 删除 3. 获取 4. 释放 5. 实例 信号量(Semaphore)的功能有一点与互斥量类似,同样是保护临界资源的一种有用方法.信号量可以但不一定实现互斥(不是说不能,一种情 ...

  7. RTOS学习笔记--时间片调度

    RTOS学习笔记–时间片调度 本文基于正点原子RTOS开发指南,笔记自用,获取详细信息请关注正点原子官方账号 介绍:在RTOS中,除了按优先级调度之外,还可以按照时间片调度,也就是给每个任务分配一个时 ...

  8. Windows事件等待学习笔记(四)—— 事件信号量互斥体

    Windows事件等待学习笔记(四)-- 事件&信号量&互斥体 要点回顾 事件 实验:验证SignalState 第一步:编译并运行以下代码 第二步:观察结果 第三步:修改代码并执行 ...

  9. FreeRTOS学习笔记——互斥型信号量

    来自:http://blog.csdn.net/xukai871105/article/details/43456985 0.前言 在嵌入式操作系统中互斥型信号量是任务间资源保护的重要手段.下面结合一 ...

最新文章

  1. Linux内核调试技术——kprobe使用与实现
  2. [ warning] [vmusr:vmtoolsd] Failed registration of app type 2 (Signals)
  3. 大厂首发:338页网易Java面试真题解析火爆全网
  4. Windows 配置Git 篇
  5. 强化学习组队学习task05—— 稀疏奖励及模仿学习
  6. SQL 中使用CONVERT转日期格式
  7. requests第三方库在测试中的使用
  8. 拓扑排序算法 C语言实现
  9. 三、Linux 教程-基础命令(181~完)
  10. 金田新材冲刺上交所:拟募资15.6亿 方文彬家族控制62%股权
  11. MPAndroidChart的详细使用——BarChart叠状条形图(四)
  12. 少儿编程--scratch编程--游来游去的鱼
  13. 酱油带你用cocos2dx3.0完成一款战棋游戏 (曹操传)(一)地图制作篇 1
  14. WebVR简介和常用资源链接
  15. 分布式强化学习之D4PG
  16. 计算机主机安装cpu,组装电脑教程:怎么安装CPU、散热器、电源、跳线?
  17. WinCC与Mysql原来可以这样玩
  18. java计算机毕业设计苗木购销系统源码+数据库+系统+lw文档+mybatis+运行部署
  19. 小票打印机开钱箱设置
  20. java占用CPU过高

热门文章

  1. 莪自己做嘚~~ppp协议文档
  2. pptv图标出现在计算机磁盘,求助,如何彻底删除PPTV
  3. 在偷懒的路上越走越远
  4. matplotlib画多个子图
  5. IM通讯 即时通讯 交友源码 聊天源码
  6. ubuntu小知识点--常用命令以及操作
  7. Postman接口自动化之postman脚本编写
  8. 高分辨率影像卫星之美国
  9. 11个精美网页——Web前端开发技术课程大作业,期末考试,Dreamweaver简单网页制作
  10. 甲骨文的CEO说 他眼里没有亚马逊和微软