以下转载自安富莱电子: 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. C# 窗体WinForm中动态显示radioButton实例

    一个项目中用到的实例,根据数据库查询出待显示的radioButton的个数,显示在一个新的窗口中. //动态显示radioButton public void showRadioButton(int ...

  2. shiro 授权介绍

    授权即访问控制,它将判断用户在应用程序中对资源是否拥有相应的访问权限.如,判断一个用户有查看页面的权限,编辑数据的权限,拥有某一按钮的权限,以及是否拥有打印的权限等等. 一.授权的三要素 授权有着三个 ...

  3. TabHost随着输入法软键盘出现而上浮的问题

    解决办法: 在androidMenifest.xml中的TabHost对应的activity中添加如下代码: android:screenOrientation="portrait" ...

  4. Android相机基础基于camera2API

    前言 最近,在使用Android做一个照相机的开发.因为不能使用系统提供的相机应用,所以只能自己写一个.Android以前提供相机的api叫camera,不过在level 21被Google抛弃了.网 ...

  5. 【Linux】head命令

    用途 它是用来显示开头或结尾某个数量的文字区块 全称 head的全称即为head 参数 -q :隐藏文件名 -v :显示文件名 -c :<字节>显示字节数 -n :<行数>显示 ...

  6. 20-spring学习-Spring MVC基本操作

    本次实现数据的CRUD功能,数据依然以VO类形式进行数据接收. 一,建立Message.java类操作,负责数据的接收操作. package com.SpringMVC.vo; public clas ...

  7. JDBC 关于Date格式

    package test; import java.sql.Connection; import java.util.Date; import java.sql.PreparedStatement; ...

  8. mysql GROUP_CONCAT 函数 将相同的键的多个单元格合并到一个单元格

    mysql GROUP_CONCAT 函数 将相同的键的多个单元格合并到一个单元格 MemberID MemberName FruitName -------------- ------------- ...

  9. Linux命令-压缩解压命令:tar

    tar [选项] [打包后文件名] [打包前的文件或者目录名称] -c表示创建(create-创建) -z表示压缩(gzip-压缩) -j表示压缩(bzip2-压缩) -v显示进度(verbose-冗 ...

  10. 完善String类([]、 +、 += 运算符重载)、>>和<<运算符重载

    在前面文章中使用过几次String类的例子,现在多重载几个运算符,更加完善一下,并且重载流类运算符. []运算符重载 +运算符重载 +=运算符重载 <<运算符重载 >>运算符重 ...