
第15章 互斥信号量 本章节开始讲解RTX的另一个重要的资源共享机制---互斥信号量(Mutex,即Mutual Exclusion的缩写)。注意,建议初学者学习完上个章节的信号量后再学习本章节的互斥信号量。 本章教程配套的例子含Cortex-M3内核的STM32F103和Cortex-M4内核的STM32F407。 15.1 互斥信号量 15.2 互斥信号量API函数 15.3 实验例程说明 15.4 总结 15.1.1 互斥信号量的概念及其作用 互斥信号量就是信号量的一种特殊形式,也就是信号量初始值为1的情况。有些RTOS中也将信号量初始值设置为1的情况称之为二值信号量。为什么叫二值信号量呢?因为信号量资源被获取了,信号量值就是0,信号量资源被释放,信号量值就是1,把这种只有0和1两种情况的信号量称之为二值信号量。互斥信号量的主要作用就是对资源实现互斥访问。下面举一个通过二值信号量实现资源独享,即互斥访问的例子,让大家有一个形象的认识 运行条件: 1. 让两个任务Task1和Task2都有运行串口打印printf,这里我们就对函数printf通过二值信号量实现互斥访问。如果不对函数printf进行互斥访问,串口打印容易出现乱码。 2. 用信号量实现二值信号量只需将信号量的初始值设置为1即可。 代码实现: 创建二值信号量
通过二值信号量实现对printf函数互斥访问的两个任务
有了上面二值信号量的认识之后,互斥信号量跟二值信号量又有什么区别呢?互斥信号量可以防止优先级翻转,而二值信号量不支持,下面我们就讲解一下优先级翻转问题。 15.1.2 优先级翻转问题 下面我们通过如下的框图来说明一下优先级翻转的问题,让大家有一个形象的认识。 ![]() 运行条件: 1. 创建3个任务Task1,Task2和Task3,优先级分别为3,2,1。也就是Task1的优先级最高 2. 任务Task1和Task3互斥访问串口打印printf,采用二值信号实现互斥访问。 3. 起初Task3通过二值信号量正在调用printf,被任务Task1抢占,开始执行任务Task1,也就是上图的起始位置。 运行过程描述如下: 1. 任务Task1运行的过程需要调用函数printf,发现任务Task3正在调用,任务Task1会被挂起,等待Task3释放函数printf。 2. 在调度器的作用下,任务Task3得到运行,Task3运行的过程中,由于任务Task2就绪,抢占了Task3的运行。优先级翻转问题就出在这里了,从任务执行的现象上看,任务Task1需要等待Task2执行完毕才有机会得到执行,这个与抢占式调度正好反了,正常情况下应该是高优先级任务抢占低优先级任务的执行,这里成了高优先级任务Task1等待低优先级任务Task2完成。所以这种情况被称之为优先级翻转问题。 3. 任务Task2执行完毕后,任务Task3恢复执行,Task3释放互斥资源后,任务Task1得到互斥资源,从而可以继续执行。 上面就是一个产生优先级翻转问题的现象。 15.1.3 RTX互斥信号量的实现 RTX互斥信号量是怎么实现的呢?其实相比二值信号量就是解决了一下优先级翻转的问题。下面我们通过如下的框图来说明一下RTX互斥信号量的实现,让大家有一个形象的认识。 ![]() 运行条件: 1. 创建2个任务Task1和Task2,优先级分别为1和3,也就是任务Task2的优先级最高 2. 任务Task1和Task2互斥访问串口打印printf。 3. 使用RTX的互斥信号量实现串口打印printf的互斥访问。 运行过程描述如下: 1. 低优先级任务Task1执行过程中先获得互斥资源printf的执行。此时任务Task2抢占了任务Task1的执行,任务Task1被挂起。任务Task2得到执行。 2. 任务Task2执行过程中也需要调用互斥资源,但是发现任务Task1正在访问,此时任务Task1的优先级会被提升到跟Task2同一个优先级,也就是优先级3,这个就是所谓的优先级继承(Priorityinheritance),这样就有效的防止了优先级翻转问题。任务Task2被挂起,任务Task1有新的优先级继续执行。 3. 任务Task1执行完毕并释放互斥资源后,优先级恢复到原来的水平。由于互斥资源可以使用,任务Task2获得互斥资源后开始执行。 上面就是一个简单RTX互斥信号量的实现过程。 15.1.4 RTX中断方式互斥信号量的实现 互斥信号量仅支持用在RTX的任务中,中断函数中不可使用。 |
15.2.1 函数os_mut_init
15.2.2 函数os_mut_wait
15.2.3 函数os_mut_release
15.3.1 STM32F103开发板实验