RTX——第15章 互斥信号量
以下内容转载自安富莱电子: http://forum.armfly.com/forum.php
本章节开始讲解 RTX 的另一个重要的资源共享机制---互斥信号量(Mutex,即 Mutual Exclusion
的缩写)。 注意,建议初学者学习完上个章节的信号量后再学习本章节的互斥信号量。
一定要多思考,二值信号会造成优先级翻转,所以在优先级有严格要求的场合,请使用互斥信号。
互斥信号量的概念及其作用
互斥信号量就是信号量的一种特殊形式,也就是信号量初始值为 1 的情况。 有些 RTOS 中也将信号量
初始值设置为 1 的情况称之为二值信号量。 为什么叫二值信号量呢?因为信号量资源被获取了,信号量值
就是 0,信号量资源被释放,信号量值就是 1,把这种只有 0 和 1 两种情况的信号量称之为二值信号量。
互斥信号量的主要作用就是对资源实现互斥访问。 下面举一个通过二值信号量实现资源独享,即互斥访问
的例子,让大家有一个形象的认识
运行条件:
让两个任务 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 得到互斥资源,
从而可以继续执行。
上面就是一个产生优先级翻转问题的现象。
RTX 互斥信号量的实现
RTX 互斥信号量是怎么实现的呢?其实相比二值信号量就是解决了一下优先级翻转的问题。 下面我们
通过如下的框图来说明一下 RTX 互斥信号量的实现,让大家有一个形象的认识。

运行条件:
创建 2 个任务 Task1 和 Task2,优先级分别为 1 和 3,也就是任务 Task2 的优先级最高
任务 Task1 和 Task2 互斥访问串口打印 printf。
使用 RTX 的互斥信号量实现串口打印 printf 的互斥访问。
运行过程描述如下:
低优先级任务 Task1 执行过程中先获得互斥资源 printf 的执行。 此时任务 Task2 抢占了任务 Task1
的执行,任务 Task1 被挂起。 任务 Task2 得到执行。
任务 Task2 执行过程中也需要调用互斥资源,但是发现任务 Task1 正在访问,此时任务 Task1 的优
先级会被提升到跟 Task2 同一个优先级,也就是优先级 3,这个就是所谓的优先级继承(Priority
inheritance),这样就有效的防止了优先级翻转问题。 任务 Task2 被挂起,任务 Task1 有新的优先
级继续执行。
任务 Task1 执行完毕并释放互斥资源后,优先级恢复到原来的水平。 由于互斥资源可以使用,任务
Task2 获得互斥资源后开始执行。
上面就是一个简单 RTX 互斥信号量的实现过程。
互斥信号量仅支持用在 RTX 的任务中,中断函数中不可使用。
互斥信号量 API 函数
使用如下 3 个函数可以实现 RTX 的互斥信号量:
os_mut_init
os_mut_release
os_mut_wait

函数 os_mut_init
函数原型:
void os_mut_init (
OS_ID mutex ); /* OS_MUT 类型变量 */
函数描述:
函数 os_mut_init 用于互斥信号量的初始化并设置初始值。
第 1 个参数填写数据类型为 OS_MUT 的变量,同时也作为 ID 标识
使用这个函数要注意以下问题:
1. 函数的参数必须是 OS_MUT 类型的。

函数 os_mut_wait
函数原型:
OS_RESULT os_mut_wait (
OS_ID mutex, /* OS_MUT 类型变量 */
U16 timeout ); /* 超时时间 */
函数描述:
函数 os_mut_wait 用于获取互斥信号量资源,如果互斥资源可用,那么调用函数 os_mut_wait 后可以成
功获取互斥资源,在此函数的源码将计数值加 1(互斥信号量源码的实现上跟信号量不同)。如果互斥资
源不可用,调用此函数的任务将由运行态转到挂起态,等待信号量资源可用,也就是计数值为 0 的时候。
如果一个低优先级的任务通过互斥信号量正在访问互斥资源,那么当一个高优先级的任务也通过互斥
信号量访问这个互斥资源的话,会将这个低优先级任务的优先级提升到和高优先级任务一样的优先级,这
就是所谓的优先级继承,通过优先级继承可以有效防止优先级翻转问题。 当低优先级任务释放了互斥资源
之后,重新恢复到原来的优先级。
第 1 个参数填写数据类型为 OS_MUT 的变量,同时也作为 ID 标识
第 2 个参数表示设在的等待时间,范围 0-0xFFFF,当参数设置为 0-0xFFFE 时,表示等待这么多个
时钟节拍,参数设置为 0xFFFF 时表示无限等待直到互斥资源可用。
函数返回 OS_R_MUT 表示函数设置的超时时间范围内收到互斥信号量可用资源。
函数返回 OS_R_TMO 表示超时。
函数返回 OS_R_OK 表示无需等待,立即获得互斥资源。
使用这个函数要注意以下问题:
1. 使用此函数前一定要调用函数 os_mut_init 进行初始化。
函数 os_mut_release
函数原型:
OS_RESULT os_mut_release (
OS_ID mutex ); /* OS_MUT 类型变量 */
函数描述:
函数 os_mut_release 用于释放互斥资源,调用此函数会将计数值减 1。只有当计数值减到 0 的时候其它
的任务才可以获取互斥资源。 也就是说如果用户调用 os_mut_wait 和 os_mut_release,需要配套使用。
通过函数 os_mut_wait 实现互斥信号量计数值加 1,通过函数 os_mut_release 实现互斥信号量计数值减
1 操作,这样的话,这两个函数可以实现嵌套调用,但是一定要保证成对调用,要不会造成互斥资源无法
正确释放。
如果拥有互斥资源的任务的优先级被提升了,那么此函数会恢复任务以前的优先级。
第 1 个参数参数填写数据类型为 OS_MUT 的变量,同时也作为 ID 标识。
返回值 OS_R_OK,表示互斥信号量成功释放。
返回值 OS_R_NOR,表示互斥信号量的内部计数值已经是 0 或者调用此函数的任务不是互斥资源的
拥有者。
使用这个函数要注意以下问题:
1. 使用此函数前一定要调用函数 os_mut_init 进行初始化。
实验练习场:
实验目的:
1. 学习 RTX 的互斥信号量
实验内容:
在调用 printf 函数的地方都加上互斥信号量,防止多个任务调用此函数造成冲突,以至于串口打印出现乱码。
可以看出我们的代码是为了保护printf函数这个共享函数(资源)的。

注意,互斥信号,创建的时候初始值为1。



这里要说明的是,都采取的是永久等待,可根据具体项目需要更改等待时间。获取(等待)互斥信号和释放互斥信号应该在同一个任务中成对出现(虽然这里可以有其他“黑科技”,但我并不想让更多的人知道,因为那样通常没有什么好处,按照官方的参考demo写,一定更规范和正确)。
简要说明程序流程:先创建互斥信号,初始化默认是1,这样其他任务调用wait函数(获取也叫等待)时,第一个调用wait函数并调用printf函数的任务一定会完整不受干扰执行printf的打印,其他也有调用printf函数的必须等待,在第一个调用任务执行完保护的函数之后,要释放互斥信号,即release函数,这样其他调用printf的任务才不至于永久等待。
程序输出:

RTX——第15章 互斥信号量的更多相关文章
- 【二代示波器教程】第15章 FreeRTOS操作系统版本二代示波器实现
第15章 FreeRTOS操作系统版本二代示波器实现 本章教程为大家讲解FreeRTOS操作系统版本的二代示波器实现.主要讲解RTOS设计框架,即各个任务实现的功能,任务间的通信方案选择,任务 ...
- Linux就这个范儿 第15章 七种武器 linux 同步IO: sync、fsync与fdatasync Linux中的内存大页面huge page/large page David Cutler Linux读写内存数据的三种方式
Linux就这个范儿 第15章 七种武器 linux 同步IO: sync.fsync与fdatasync Linux中的内存大页面huge page/large page David Cut ...
- 【RL-TCPnet网络教程】第15章 RL-TCPnet之创建多个TCP连接
第15章 RL-TCPnet之创建多个TCP连接 本章节为大家讲解RL-TCPnet的TCP多客户端实现,因为多客户端在实际项目中用到的地方还挺多,所以我们也专门开启一个章节做讲解.另外,学习 ...
- FreeRTOS 二值信号量,互斥信号量,递归互斥信号量
以下转载自安富莱电子: http://forum.armfly.com/forum.php 本章节讲解 FreeRTOS 任务间的同步和资源共享机制,二值信号量. 二值信号量是计数信号量的一种特殊形式 ...
- 第15章 LinkedList类(暂无)
第15章 LinkedList类 LinkedList类是
- ASM:《X86汇编语言-从实模式到保护模式》第15章:任务切换
15章其实应该是和14章相辅相成的(感觉应该是作者觉得14章内容太多了然后切出来了一点).任务切换和14章的某些概念是分不开的. ★PART1:任务门与任务切换的方法 1. 任务管理程序 14章的时候 ...
- 第15章 设备无关位图_15.3 DIB和DDB的结合
第15章 设备相关位图_15.3 DIB和DDB的结合 15.3.1 从DIB创建DDB (1)hBitmap =CreateDIBitmap(…)——注意这名称会误导,实际上创建的是DDB 参数 说 ...
- unix network programming(3rd)Vol.1 [第13~15章]《读书笔记系列》
第13章 守护进程和inetd 超级服务器 syslog() daemon_init() setuid() setgid() 第14章 高级IO 标准I/O函数库,支持3种缓冲 缓冲(读写存储设备(硬 ...
- 《Android开发艺术探索》读书笔记 (13) 第13章 综合技术、第14章 JNI和NDK编程、第15章 Android性能优化
第13章 综合技术 13.1 使用CrashHandler来获取应用的Crash信息 (1)应用发生Crash在所难免,但是如何采集crash信息以供后续开发处理这类问题呢?利用Thread类的set ...
随机推荐
- Java多线程系列目录(共43篇)(转)
Java多线程系列目录(共43篇) http://www.cnblogs.com/skywang12345/p/java_threads_category.html
- 【云栖大会】阿里巴巴集团CTO张建锋:用计算和数据去改变整个世界
摘要: 当浩瀚的数字化信息能够联网在线,在万物互联网的新世界中,所有东西都可能有感知.变智能,想象一下电表.冰箱.心电图监测仪等设备的信息都能数字化并联网,从城市管理到个人生活,都会迎来翻天覆地的变化 ...
- 腾讯开放平台 iOS应用URL schema、Bundle ID填写 (含微博、微信)
解释如下: qq比较麻烦点,需要两个 URL schemes 1.QQ+appID(注意:appID原本是10进制的,需要先转换16进制,网址:点击转换16进制) 2.tencent+appID 结束
- pandas 的数据结构Series与DataFrame
pandas中有两个主要的数据结构:Series和DataFrame. [Series] Series是一个一维的类似的数组对象,它包含一个数组数据(任何numpy数据类型)和一个与数组关联的索引. ...
- 如何在cmd命令行中查看、修改、删除与添加环境变量,语法格式例子:set path;echo %APPDATA%
如何在cmd命令行中查看.修改.删除与添加环境变量 首先明确一点: 所有的在cmd命令行下对环境变量的修改只对当前窗口有效,不是永久性的修改.也就是说当关闭此cmd命令行窗口后,将不再起作用.永久性修 ...
- 修改注册表值解决ie被恶意窜改的问题
修改注册表值解决ie被恶意窜改的问题 IE消失 运行—Regedit 主键HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\HideDes ...
- C# 小叙 Encoding (一)
前言 众所周知计算机只能识别二进制数字,如1010,1001.我们屏幕所看到的文字,字符都是和二进制转换后的结果.将我们的文字按照某种规则转换二进制存储在计算机上,这一个过程叫字符编码,反之就是解码. ...
- USB协议及认知
1.USB的拓扑结构决定了主机控制器就是最高统帅,没有主机控制器的要求设备永远不能主动发数据.所以主机控制器在USB 的世界里扮演着重要的角色,它是幕后操纵者. 2.数据包的发送, 这个过程包含很多信 ...
- oracle关键字大全--注意不要乱用哦
遇到怀疑可能使用了关键字,就来搜一搜吧 ... Oracle 关键字(保留字) 大全 其实这个东西可以在oracle 上输入一个sql语句就可以得到: select * from v$reserved ...
- jpa 批量插入
@Override @Transactional public <S extends E> List<S> save(Iterable<S> entities) { ...