目录
优先级翻转现象
什么是优先级翻转
互斥信号量
1、互斥信号量简介
2、创建互斥信号量
1、函数 xSemaphoreCreateMutex()
2、函数 xSemaphoreCreateMutexStatic()
3、释放互斥信号量
4、获取互斥信号量
优先级翻转现象
在使用二值信号量的时候会遇到很常见的一个问题——优先级翻转,优先级翻转在可剥夺
内核中是非常常见的,在实时系统中不允许出现这种现象,这样会破坏任务的预期顺序,可能
会导致严重的后果
什么是优先级翻转
注:当有一点FreeRTOS基础,就别看图了,直接看下面字的介绍,脑补画面,会很简单。
(1) 任务 H 和任务 M 处于挂起状态,等待某一事件的发生,任务 L 正在运行。
(2) 某一时刻任务 L 想要访问共享资源,在此之前它必须先获得对应该资源的信号量。
(3) 任务 L 获得信号量并开始使用该共享资源。
(4) 由于任务 H 优先级高,它等待的事件发生后便剥夺了任务 L 的 CPU 使用权。
(5) 任务 H 开始运行。
(6) 任务 H 运行过程中也要使用任务 L 正在使用着的资源,由于该资源的信号量还被任务
L 占用着,任务 H 只能进入挂起状态,等待任务 L 释放该信号量。
(7) 任务 L 继续运行。
(8) 由于任务 M 的优先级高于任务 L,当任务 M 等待的事件发生后,任务 M 剥夺了任务
L 的 CPU 使用权。
(9) 任务 M 处理该处理的事。
(10) 任务 M 执行完毕后,将 CPU 使用权归还给任务 L。
(11) 任务 L 继续运行。
(12) 最终任务 L 完成所有的工作并释放了信号量,到此为止,由于实时内核知道有个高
优先级的任务在等待这个信号量,故内核做任务切换。
(13) 任务 H 得到该信号量并接着运行。
在这种情况下,任务 H 的优先级实际上降到了任务 L 的优先级水平。因为任务 H 要一直
等待直到任务 L 释放其占用的那个共享资源。由于任务 M 剥夺了任务 L 的 CPU 使用权,使
得任务 H 的情况更加恶化,这样就相当于任务 M 的优先级高于任务 H,导致优先级翻转。
当一个低优先级任务和一个高优先级任务同时使用同一个信号量,而系统中还有其他中等优先级任务时。如果低优先级任务获得了信号量,那么高优先级的任务就会处于等待状态,但是,中等优先级的任务可以打断低优先级任务而先于高优先级任务运行(此时高优先级的任务在等待信号量 , 所以不能运行), 这是就出现了优先级翻转的现象。
既然优先级翻转是个很严重的问题,么有没有解决方法呢?有!这就要引出另外一种信号量——互斥信号量!
互斥信号量
1、互斥信号量简介
互斥信号量其实就是一个拥有优先级继承的二值信号量, 在同步的应用中(任务与任务或中
断与任务之间的同步)二值信号量最适合。 互斥信号量适合用于那些需要互斥访问的应用中。 在
互斥访问中互斥信号量相当于一个钥匙,当任务想要使用资源的时候就必须先获得这个钥匙,
当使用完资源以后就必须归还这个钥匙,这样其他的任务就可以拿着这个钥匙去使用资源。
互斥信号量使用和二值信号量相同的 API 操作函数,所以互斥信号量也可以设置阻塞时间,
不同于二值信号量的是互斥信号量具有优先级继承的特性。 当一个互斥信号量正在被一个低优
先级的任务使用,而此时有个高优先级的任务也尝试获取这个互斥信号量的话就会被阻塞。不
过这个高优先级的任务会将低优先级任务的优先级提升到与自己相同的优先级, 这个过程就是
优先级继承。优先级继承尽可能的降低了高优先级任务处于阻塞态的时间,并且将已经出现的
“优先级翻转”的影响降到最低。
优先级继承并不能完全的消除优先级翻转, 它只是尽可能的降低优先级翻转带来的影响。
硬实时应用应该在设计之初就要避免优先级翻转的发生,互斥信号量不能用于中断服务函数中。
原因:
● 互斥信号量有优先级继承的机制,所以只能用在任务中,不能用于中断服务函数。
● 中断服务函数中不能因为要等待互斥信号量而设置阻塞时间进入阻塞态。
2、创建互斥信号量
FreeRTOS 提供了两个互斥信号量创建函数
函数 | 描述 |
xSemaphoreCreateMutex() | 使用动态方法创建互斥信号量 |
xSemaphoreCreateMutexStatic() | 使用静态方法创建互斥信号量 |
1、函数 xSemaphoreCreateMutex()
此函数用于创建一个互斥信号量,所需要的内存通过动态内存管理方法分配。此函数本质
是一个宏,真正完成信号量创建的是函数 xQueueCreateMutex(),此函数原型如下:
2、函数 xSemaphoreCreateMutexStatic()
此函数也是创建互斥信号量的,只不过使用此函数创建互斥信号量的话信号量所需要的
RAM 需要由用户来分配,此函数是个宏,具体创建过程是通过函数 xQueueCreateMutexStatic ()
来完成的,函数原型如下:
3、释放互斥信号量
释 放 互 斥 信 号 量 的 时 候 和 二 值 信 号 量 、 计 数 型 信 号 量 一 样 , 都 是 用 的 函 数
xSemaphoreGive()(实际上完成信号量释放的是函数 xQueueGenericSend())。不过由于互斥信号量涉及到优先级继承的问题,所以具体处理过程会有点区别。 使用函数 xSemaphoreGive()释放信
号 量 最 重 要 的 一 步 就 是 将 uxMessagesWaiting 加 一 , 而 这 一 步 就 是 通 过 函 数
prvCopyDataToQueue() 来 完 成 的 , 释 放 信 号 量 的 函 数 xQueueGenericSend() 会 调 用
prvCopyDataToQueue()。互斥信号量的优先级继承也是在函数 prvCopyDataToQueue()中完成的。
函数原型:
参数:
返回值
4、获取互斥信号量
获 取 互 斥 信 号 量 的 函 数 同 获 取 二 值 信 号 量 和 计 数 型 信 号 量 的 函 数 相 同 , 都 是xSemaphoreTake()(实际执行信号量获取的函数是 xQueueGenericReceive()), 获取互斥信号量的过程也需要处理优先级继承的问题,函数 xQueueGenericReceive()在文件 queue.c 中有定义。
函数原型
参数
返回值
xSemaphoreTake ()的具体内容
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)