以下转载自安富莱电子: http://forum.armfly.com/forum.php

本章节讲解 FreeRTOS 任务间的同步和资源共享机制,二值信号量。 二值信号量是计数信号量的一种特殊形式,即共享资源为 1 的情况。

FreeRTOS 分别提供了二值信号量和计数信号量,其中二值信号量可以理解成计数
信号量的一种特殊形式,即初始化为仅有一个资源可以使用,只不过 FreeRTOS 对这两种都提供了 API
函数,而像 RTX,uCOS-II 和 III 是仅提供了一个信号量功能,设置不同的初始值就可以分别实现二值信
号量和计数信号量。 当然,FreeRTOS 使用计数信号量也能够实现同样的效果。 另外,为什么叫二值信号
量呢?因为信号量资源被获取了,信号量值就是 0,信号量资源被释放,信号量值就是 1,把这种只有 0
和 1 两种情况的信号量称之为二值信号量。

函数 xSemaphoreCreateBinary
函数原型:
SemaphoreHandle_t xSemaphoreCreateBinary(void)
函数描述:
函数 xSemaphoreCreateBinary 用于创建二值信号量。
 返回值,如果创建成功会返回二值信号量的句柄,如果由于 FreeRTOSConfig.h 文件中 heap 大小不
足,无法为此二值信号量提供所需的空间会返回 NULL。

FreeRTOS 重要的资源共享机制---互斥信号量(Mutex,即 Mutual Exclusion 的缩写)。
注意,建议初学者学习完前两个信号量后再学习本章节的互斥信号量。

互斥信号量的主要作用是对资源实现互斥访问,使用二值信号量也可以实现互斥访问的功能,不过互
斥信号量与二值信号量有区别。 下面我们先举一个通过二值信号量实现资源独享,即互斥访问的例子,让
大家有一个形象的认识,进而引出要讲解的互斥信号量。
运行条件:
 让两个任务 Task1 和 Task2 都运行串口打印函数 printf,这里我们就通过二值信号量实现对函数
printf 的互斥访问。 如果不对函数 printf 进行互斥访问,串口打印容易出现乱码。
 用计数信号量实现二值信号量只需将计数信号量的初始值设置为 1 即可。
代码实现:

有了上面二值信号量的认识之后,互斥信号量与二值信号量又有什么区别呢?互斥信号量可以防止优
先级翻转,而二值信号量不支持,下面我们就讲解一下优先级翻转问题。

运行条件:
 创建 3 个任务 Task1,Task2 和 Task3,优先级分别为 3,2,1。 也就是 Task1 的优先级最高。
 任务 Task1 和 Task3 互斥访问串口打印 printf,采用二值信号实现互斥访问。
 起初 Task3 通过二值信号量正在调用 printf,被任务 Task1 抢占,开始执行任务 Task1,也就是上图的起始位置。 
运行过程描述如下:
 任务 Task1 运行的过程需要调用函数 printf,发现任务 Task3 正在调用,任务 Task1 会被挂起,等
待 Task3 释放函数 printf。
 在调度器的作用下,任务 Task3 得到运行,Task3 运行的过程中,由于任务 Task2 就绪,抢占了 Task3
的运行。 优先级翻转问题就出在这里了,从任务执行的现象上看,任务 Task1 需要等待 Task2 执行
完毕才有机会得到执行,这个与抢占式调度正好反了,正常情况下应该是高优先级任务抢占低优先级
任务的执行,这里成了高优先级任务 Task1 等待低优先级任务 Task2 完成。 所以这种情况被称之为
优先级翻转问题。
 任务 Task2 执行完毕后,任务 Task3 恢复执行,Task3 释放互斥资源后,任务 Task1 得到互斥资源,
从而可以继续执行。

FreeRTOS 互斥信号量的实现
FreeRTOS 互斥信号量是怎么实现的呢?其实相对于二值信号量,互斥信号量就是解决了一下优先级
翻转的问题。 下面我们通过如下的框图来说明一下 FreeRTOS 互斥信号量的实现,让大家有一个形象的认识。

运行条件:
 创建 2 个任务 Task1 和 Task2,优先级分别为 1 和 3,也就是任务 Task2 的优先级最高。
 任务 Task1 和 Task2 互斥访问串口打印 printf。
 使用 FreeRTOS 的互斥信号量实现串口打印 printf 的互斥访问。
运行过程描述如下:

 低优先级任务 Task1 执行过程中先获得互斥资源 printf 的执行。 此时任务 Task2 抢占了任务 Task1
的执行,任务 Task1 被挂起。 任务 Task2 得到执行。
 任务 Task2 执行过程中也需要调用互斥资源,但是发现任务 Task1 正在访问,此时任务 Task1 的优
先级会被提升到与 Task2 同一个优先级,也就是优先级 3,这个就是所谓的优先级继承(Priority
inheritance),这样就有效地防止了优先级翻转问题。 任务 Task2 被挂起,任务 Task1 有新的优先
级继续执行。
 任务 Task1 执行完毕并释放互斥资源后,优先级恢复到原来的水平。 由于互斥资源可以使用,任务
Task2 获得互斥资源后开始执行。

FreeRTOS 中断方式互斥信号量的实现
互斥信号量仅支持用在 FreeRTOS 的任务中,中断函数中不可使用。

互斥信号量 API 函数
函数 xSemaphoreCreateMutex
函数原型:
SemaphoreHandle_t xSemaphoreCreateMutex( void )
函数描述:
函数 xSemaphoreCreateMutex 用于创建互斥信号量。
 返回值,如果创建成功会返回互斥信号量的句柄,如果由于 FreeRTOSConfig.h 文件中 heap 大小不
足,无法为此互斥信号量提供所需的空间会返回 NULL。

使用这个函数要注意以下问题:
1. 此函数是基于函数 xQueueCreateMutex 实现的:
#define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )
函数 xQueueCreateMutex 的实现是基于消息队列函数 xQueueGenericCreate 实现的。
2. 使用此函数要在 FreeRTOSConfig.h 文件中使能宏定义:
#define configUSE_MUTEXES 1

互斥信号量, xSemaphoreTake 和 xSemaphoreGive 一定要成对的调用

应用举例:

经过测试,互斥信号量是可以被其他任务释放的,但是我们最好不要这么做,因为官方推荐的就是在同一个任务中接收和释放。如果在其他任务释放,不仅仅会让代码整体逻辑变得复杂,还会给使用和维护这套API的人带来困难。遵守规范,总是好的。

裸机编程的时候,我经常想一个问题,就是怎么做到当一个标志位触发的时候,立即执行某个操作,如同实现标志中断一样,在os编程之后,我们就可以让一个优先级最高任务一直等待某个信号量,如果获得信号量,就执行某个操作,实现类似标志位中断的作用(当然,要想正真做到中断效果,那就需要屏蔽所有可屏蔽中断,而临界区就可以做到)。

再说一下递归互斥信号量:递归互斥信号量,其实就是互斥信号量里面嵌套互斥信号量

eg:

static void vTaskMsgPro(void *pvParameters)
{
TickType_t xLastWakeTime;
const TickType_t xFrequency = ; /* 获取当前的系统时间 */
xLastWakeTime = xTaskGetTickCount(); while()
{
/* 递归互斥信号量,其实就是互斥信号量里面嵌套互斥信号量 */
xSemaphoreTakeRecursive(xRecursiveMutex, portMAX_DELAY);
{
/* -------------------------------------- */
//假如这里是被保护的资源,第1层被保护的资源,用户可以在这里添加被保护资源
/* ---------------------------------------------------------------------------- */
printf("任务vTaskMsgPro在运行,第1层被保护的资源,用户可以在这里添加被保护资源\r\n"); /* 第1层被保护的资源里面嵌套被保护的资源 */
xSemaphoreTakeRecursive(xRecursiveMutex, portMAX_DELAY);
{
/* ------------------------------------------------------------------------ */
//假如这里是被保护的资源,第2层被保护的资源,用户可以在这里添加被保护资源
/* ------------------------------------------------------------------------ */
printf("任务vTaskMsgPro在运行,第2层被保护的资源,用户可以在这里添加被保护资源\r\n"); /* 第2层被保护的资源里面嵌套被保护的资源 */
xSemaphoreTakeRecursive(xRecursiveMutex, portMAX_DELAY);
{
printf("任务vTaskMsgPro在运行,第3层被保护的资源,用户可以在这里添加被保护资源\r\n");
bsp_LedToggle();
bsp_LedToggle();
}
xSemaphoreGiveRecursive(xRecursiveMutex);
}
xSemaphoreGiveRecursive(xRecursiveMutex);
}
xSemaphoreGiveRecursive(xRecursiveMutex); /* vTaskDelayUntil是绝对延迟,vTaskDelay是相对延迟。*/
vTaskDelayUntil(&xLastWakeTime, xFrequency);
}
}

可以前面的那个官方文档那样用if判断传递共享量:

也可以用我们递归互斥信号量里面的portMAX_DELAY指定永久等待,即xSemaphoreTakeRecursive返回的不是pdTRUE时,会一直等待信号量,直到有信号量来才执行后面的语句。

FreeRTOS 二值信号量,互斥信号量,递归互斥信号量的更多相关文章

  1. Python进阶----线程基础,开启线程的方式(类和函数),线程VS进程,线程的方法,守护线程,详解互斥锁,递归锁,信号量

    Python进阶----线程基础,开启线程的方式(类和函数),线程VS进程,线程的方法,守护线程,详解互斥锁,递归锁,信号量 一丶线程的理论知识 什么是线程:    1.线程是一堆指令,是操作系统调度 ...

  2. FreeRTOS二值信号量

    API函数 #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) #define xSemaphoreCreateBinary() xQueueGenericCre ...

  3. FreeRTOS 任务通知模拟二值信号量

    FreeRTOS官方统计,使用任务通知替代二值信号量的时候,任务解除阻塞的时间要快45%,并且需要的RAM也更少 举例 void DataProcess_task(void *pvParameters ...

  4. freertos知识点笔记——队列、二值信号量、计数信号量

    队列1.队列queue通常用于任务之间的通信,一个任务写缓存,另一个任务读缓存.队列还会有等待时间,2.阻塞超时时间.如果在发送时队列已满,这个时间即是任务处于阻塞态等待队列空间有效的最长等待时间.如 ...

  5. 15.4-uC/OS-III资源管理(二值信号量)

    互斥信号量是 uC/OS 操作系统的一个内核对象, 与多值信号量非常相似,但它是二值的,只能是 0 或 1,所以也叫二值信号量, 主要用于保护资源. 1.如果想要使用互斥信号量,就必须事先使能互斥信号 ...

  6. 多线程锁:Mutex互斥体,Semaphore信号量,Monitor监视器,lock,原子操作InterLocked

    Mutex类 “mutex”是术语“互相排斥(mutually exclusive)”的简写形式,也就是互斥量.互斥量跟临界区中提到的Monitor很相似,只有拥有互斥对象的线程才具有访问资源的权限, ...

  7. 互斥锁Mutex与信号量Semaphore的区别

    转自互斥锁Mutex与信号量Semaphore的区别 多线程编程中,常常会遇到这两个概念:Mutex和Semaphore,两者之间区别如下: 有人做过如下类比: Mutex是一把钥匙,一个人拿了就可进 ...

  8. freeRTOS中文实用教程4--资源管理互斥

    1.前言 访问一个被多任务共享,或是被任务与中断共享的资源时,需要采用”互斥”技术以保证数据在任何时候都保持一致性.这样做的目的是要确保任务从开始访问资源就具有排它性,直至这个资源又恢复到完整状态 F ...

  9. 同步锁 死锁与递归锁 信号量 线程queue event事件

    二个需要注意的点: 1 线程抢的是GIL锁,GIL锁相当于执行权限,拿到执行权限后才能拿到互斥锁Lock,其他线程也可以抢到GIL,但如果发现Lock任然没有被释放则阻塞,即便是拿到执行权限GIL也要 ...

随机推荐

  1. JNI/NDK开发指南(十)——JNI局部引用、全局引用和弱全局引用

    转自:http://blog.csdn.net/xyang81/article/details/44657385   这篇文章比较偏理论,详细介绍了在编写本地代码时三种引用的使用场景和注意事项.可能看 ...

  2. IE6BUG汇总篇(不断更新)

    1.IE6双倍边距bug 当页面内有多个连续浮动时,如本页的图标列表是采用左浮动,此时设置li的左侧margin值时,在最左侧呈现双倍情况.如外边距设置为10px, 而左侧则呈现出20px,解决它的方 ...

  3. WinForm 窗口缩放动画效果

    using System; using System.Collections.Generic; using System.Text; using System.Threading; using Sys ...

  4. Python写的简陋版一对一聊天工具,全双工

    好该睡觉了,明天还要上班~~~直接上代码,后面再总结 import threading import os import socket def RecvProcess ( UDP_Socket, Lo ...

  5. shell中的字符串操作

    SHELL字符串操作 bash Shell提供了多种字符串处理的命令: awk命令 expr命令 字符串长度 ${#..} expr length awk的length(s) 实例: string=& ...

  6. LoadRunner录制:事务

    背景 LoadRunner 会对事务的性能指标进行记录. 添加事务也是为了在测试的时候统计这段脚本运行时用的时间等等,方便定位性能瓶颈. 一个事务可以包含一个请求,也可以包含多个请求.一般把完成一件事 ...

  7. PHP 正则表达式(PCRE)

    PHP 正则表达式(PCRE) 正则表达式(regular expression)描述了一种字符串匹配的模式,可以用来检查一个串是否含有某种子串.将匹配的子串做替换或者从某个串中取出符合某个条件的子串 ...

  8. Java基础语法(第1章变量)

    今日内容介绍 1.变量 2. 运算符   变量 1.1.变量概述 前面我们已经学习了常量,接下来我们要学习变量.在Java中变量的应用比常量的应用要多很多.所以变量也是尤为重要的知识点! 什么是变量? ...

  9. mac下的git的安装与简单的配置

    git 本地操作 git 简单介绍 .Git是分布式的SCM,SVN是集中式的 2.Git每一个历史版本号存储完整的文件,SVN存储文件差异 3.Git可离线完毕大部分操作,SVN则相反 4.Git有 ...

  10. 31、Arrays数组排序(续)——自定义排序

    自定义的类要按照一定的方式进行排序,比如一个Person类要按照年龄进行从小到大排序,比如一个Student类要按照成绩进行由高到低排序. 这里我们采用两种方式,一种是使用Comparable接口:让 ...