Linux RT 内核 (2) - 工作原理 [译]

原文:Theory of operation

1. 前言

PREEMPT_RT 将 Linux 内核转变为实时内核。其实现方式是:用一种称为 rtmutex 的可抢占且支持优先级继承的实现方案替换锁定原语(如 spinlock_t),并强制使用线程化中断。由此,除少数关键代码路径(包括入口代码、调度器及底层中断处理例程)外,内核实现了完全抢占性

该改造使绝大多数内核执行上下文置于调度器控制之下,并显著增加了抢占点数量。由此有效缩短高优先级任务从可运行状态到实际在 CPU 上执行的延迟

2. 调度

Linux 调度机制的核心原则及其对应的用户空间 API 详见手册页 sched(7)默认情况下,Linux 内核采用 SCHED_OTHER 调度策略。在此策略下,当调度器判定某个任务相对于其他可运行任务已消耗公平份额的 CPU 时间时,该任务将被抢占。但该策略并不保证当新的 SCHED_OTHER 任务进入可运行状态立即进行抢占当前运行任务可能继续执行

此行为与 SCHED_FIFO 等实时调度策略存在差异。当采用实时策略的任务具备可运行状态时若其优先级高于当前运行任务,调度器将立即选定该任务执行。该任务将持续运行直至主动释放 CPU 资源(通常通过事件阻塞实现)。

3. 睡眠自旋锁

各类锁类型及其在实时配置下的行为已在 Lock types and their rules 中详细描述。在非 PREEMPT_RT 配置中,获取 spinlock_t 的方式是先禁用抢占,然后持续自旋直至锁可用锁释放后会重新启用抢占。从实时角度看,这种方式存在缺陷:禁用抢占阻碍调度器切换至更高优先级任务,可能增加延迟

为解决此问题,PREEMPT_RT 采用睡眠式自旋锁替代传统自旋锁,该机制不禁用抢占。在 PREEMPT_RT 环境中,spinlock_t 通过 rtmutex 实现。当任务尝试获取竞争锁时不再进行自旋操作,而是禁用 CPU 迁移功能将自身优先级让渡给锁持有者(优先级继承机制),并在等待锁释放期间主动退出调度

禁用 CPU 迁移功能实现了与禁用抢占效果,同时仍保持抢占机制有效确保任务在持有睡眠锁期间持续运行于同一 CPU 上[1]

4. 优先级继承

在启用 PREEMPT_RT 的内核中,诸如 spinlock_tmutex_t 之类的锁类型是在 rtmutex 之上实现的,后者提供了对优先级继承(PI)的支持。当任务在该类锁上阻塞时PI 机制会将阻塞任务的调度参数临时传递给锁持有者

例如,当 SCHED_FIFO 任务 ASCHED_OTHER 任务 B 持有的锁上阻塞时,任务 B临时继承任务 A 的调度策略和优先级[2]继承完成后,任务 A 进入睡眠状态等待锁释放,而任务 B 实质上成为系统中最高优先级的任务。这使得 B继续执行、推进进程并最终释放锁

B 释放锁后,其调度参数恢复原状,任务 A可继续执行

5. 线程化中断

中断处理程序是另一类在禁用抢占不受调度器控制的情况下执行的代码。为使中断处理置于调度器控制之下,PREEMPT_RT 强制采用线程化中断处理程序

通过强制线程化中断处理拆分为两个阶段第一阶段(主处理程序)在 IRQ 上下文中执行,此时中断处于禁用状态。其唯一职责唤醒关联的线程化处理程序第二阶段即中断线程化处理程序,是作为中断处理函数传递给 request_irq() 的函数。该函数在进程上下文中运行,由内核调度执行。

从唤醒中断线程到线程化处理完成期间,中断源在中断控制器中保持屏蔽状态,这确保设备中断保持待处理状态但不会重新触发 CPU,从而使系统能够退出 IRQ 上下文并在调度线程中处理中断。

6. 摘要

通过使用睡眠锁强制线程中断PREEMPT_RT 显著减少禁用中断抢占的代码段,从而允许调度器抢占当前执行上下文并切换至更高优先级的任务。

默认情况下,线程化处理程序采用 SCHED_FIFO 调度策略,优先级为 50(即 MAX_RT_PRIO / 2),该值介于实时优先级的最小值与最大值之间[3]

线程化中断处理程序执行期间触发软中断,相关软中断例程在线程化处理程序完成后于同一线程内调用。软中断处理程序执行期间,抢占机制保持启用状态。


  1. 禁用 CPU 迁移,保证了任务在同一 CPU 等待,这和原本的 spinlock 语义相同;但在 PREEMPT_RT 下不同的是,自旋(spin)等待变成了睡眠等待,这样更好的保证了系统的实时性。 ↩︎

  2. 这在一定程度上缓解了优先级反转的问题,可以提高 PREEMPT_RT 系统的实时性。 ↩︎

  3. 这是一个折衷策略:一方面,线程化中断不会因为中断处理程序耗时过长,影响实时任务的调度(未线程化的中断处理程序处于屏蔽中断的情况下执行,屏蔽中断隐式的关闭了当前 CPU 上的抢占);另一方面,给线程化中断设定一个中间值的实时优先级,既不会让中断处理延迟过久(中断通常都应该快速被响应),也不会和高优先级实时任务的抢占 CPU,从而影响实时性。 ↩︎

posted @ 2026-03-05 17:52  JiMoKuangXiangQu  阅读(41)  评论(0)    收藏  举报