






















2006 年内核峰会接受了将实时相关组件合并到主线的计划,随后作为实时补丁集(PREEMPT_RT)的一部分,rt_mutex在 Linux 2.6.18 版本中被合并到内核主线。
rt_mutex和普通的mutex相比,主要的特征是支持优先级继承,是为了解决优先级反转引入的。rt_mutex 运用优先级继承机制(见 rt_mutex_adjust_prio_chain)来避免优先级反转,这种机制同样适用于睡眠自旋锁(sleeping spinlocks)和读写锁(rwlocks)。
想要理解rt_mutex的作用,首先需要了解优先级反转相关的知识,这样才能明白它 是如何影响系统实时性能的,以及为什么优先级继承可以解决这个问题。
在实时操作系统(RTOS)中,优先级反转是指高优先级任务被低优先级任务间接阻塞,使得高优先级任务不能按照其优先级及时执行,而低优先级任务却能优先运行的异常现象。这种现象违背了 RTOS 正常的基于优先级的调度机制。
sequenceDiagram participant H as 高优先级任务 H participant M as 中优先级任务 M participant L as 低优先级任务 L participant R as 资源 R L ->> R: 请求并获取资源 R H ->> R: 请求资源 R R -->> H: 资源正被 L 使用,等待 M ->> L: 抢占 L 的执行权 M ->> M: 执行任务 H ->> H: 等待资源 R,无法执行 L ->> L: 等待 M 释放执行权 M ->> L: 释放执行权 L ->> R: 释放资源 R R -->> H: 资源 R 可用 H ->> R: 获取资源 R H ->> H: 执行任务
优先级继承:
sequenceDiagram participant H as 高优先级任务 H participant M as 中优先级任务 M participant L as 低优先级任务 L participant R as 资源 R L ->> R: 请求并获取资源 R H ->> R: 请求资源 R R -->> H: 资源正被 L 使用,等待 L ->> L: 优先级提升至 H 的优先级 M ->> L: 尝试抢占 L 的执行权 L -->> M: 拒绝,当前优先级与 H 相同 L ->> R: 继续执行并释放资源 R R -->> H: 资源 R 可用 H ->> R: 获取资源 R H ->> H: 执行任务 L ->> L: 优先级恢复原级别
优先级天花板:
sequenceDiagram participant H as 高优先级任务 H participant M as 中优先级任务 M participant L as 低优先级任务 L participant R as 资源 R R ->> R: 设置优先级天花板为 H 的优先级 L ->> R: 请求并获取资源 R,L 优先级提升至天花板(H 的优先级) H ->> R: 请求资源 R R -->> H: 资源正被 L 使用,等待 M ->> L: 尝试抢占 L 的执行权 L -->> M: 拒绝,当前优先级与 H 相同 L ->> R: 继续执行并释放资源 R R -->> H: 资源 R 可用 H ->> R: 获取资源 R H ->> H: 执行任务 L ->> L: 优先级恢复原级别
在Linux内核中,rt_mutex(实时互斥锁)是为满足实时应用对低延迟和避免优先级反转的需求而设计的同步原语。实时应用要求对事件的响应具有确定性,而传统的内核互斥锁在某些情况下可能导致优先级反转等问题,影响实时性,rt_mutex 则致力于解决这些问题。
rt_mutex 的核心特性之一。当高优先级任务尝试获取被低优先级任务持有的 rt_mutex 时,低优先级任务的优先级会临时提升到与高优先级任务相同。这样做的目的是让持有锁的低优先级任务尽快执行完毕并释放锁,从而减少高优先级任务的等待时间,避免优先级反转。例如,假设有高优先级任务 H、中优先级任务 M 和低优先级任务 L,L 持有 rt_mutex,H 试图获取该锁,此时 L 的优先级将提升到 H 的优先级,M 就无法抢占 L,L 可以尽快释放锁给 H。rt_mutex 具备死锁检测机制。内核通过跟踪任务对 rt_mutex 的获取和释放情况,以及任务之间的等待关系,能够检测到潜在的死锁情况。一旦检测到死锁,内核可以采取相应的措施,如打印死锁相关信息,帮助开发者定位和解决问题。rt_mutex 实现了快速路径。在常见情况下,即没有竞争的情况下,获取和释放锁的操作可以在非常低的开销下完成。这有助于减少同步操作对系统性能的影响,满足实时系统对高效性的要求。rt_mutex 之前,需要对其进行初始化。可以使用 rt_mutex_init 函数,示例如下:#include <linux/rtmutex.h>
struct rt_mutex my_rt_mutex;
static int __init my_module_init(void) {
rt_mutex_init(&my_rt_mutex);
// 其他初始化代码
return 0;
}
rt_mutex_lock 函数来获取 rt_mutex。如果锁当前可用,任务将立即获取锁并继续执行;如果锁被其他任务持有,任务会被加入等待队列,并可能触发优先级继承机制。rt_mutex_lock(&my_rt_mutex);
// 临界区代码,访问共享资源
rt_mutex_unlock(&my_rt_mutex);
rt_mutex_unlock 函数释放 rt_mutex。如果有其他任务在等待该锁,内核会唤醒等待队列中的最高优先级任务。rt_mutex_unlock(&my_rt_mutex);
rt_mutex 支持一定程度的嵌套获取,但过度嵌套会增加代码复杂度和死锁风险。在编写代码时,应尽量避免不必要的嵌套锁操作,保持代码逻辑清晰。rt_mutex 不能在中断上下文中使用。因为中断上下文不能睡眠,而获取 rt_mutex 可能会导致任务睡眠本作品采用 CC BY-NC-SA 4.0 协议
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。