☰
PREEMPT_RT到底改变了什么?从Linux内核抢占机制理解欧拉实时系统
2026/10/2 2:23:56 网站建设 项目流程

上一篇文章我们讨论了一个问题:

从普通Linux走向实时Linux,并不是简单地给操作系统增加一个“实时”标签。对于工业控制、机器人、智能制造、边缘计算等场景而言,真正重要的问题是:当一个高优先级任务需要立即执行时,操作系统能不能让它尽快获得CPU?当硬件产生中断时,系统会不会长时间停留在中断处理路径?当多个任务竞争同一个资源时,会不会因为锁竞争导致高优先级任务长时间等待?当CPU负载非常高时,实时任务还能不能保持相对稳定的响应时间?

这些问题最终都会指向Linux内核本身。

Linux拥有非常成熟的进程调度、内存管理、网络、文件系统和设备驱动体系,但这种高度通用的设计,也意味着内核中存在大量复杂执行路径。对于普通应用而言,这种复杂性通常不会构成明显问题;但是对于实时应用而言,真正需要关注的并不是Linux平均情况下运行得有多快,而是最坏情况下,一个高优先级任务究竟需要等待多久。

因此,在Linux实时化的发展过程中,PREEMPT_RT成为一个非常重要的技术路线。

简单来说,PREEMPT_RT并不是重新设计一个Linux,而是在尽可能保留Linux生态、驱动、系统调用和开发模型的基础上,对内核中的抢占、中断、锁以及部分执行路径进行实时化改造,使高优先级任务能够更加及时地获得CPU,从而降低系统最坏情况下的调度延迟。

openEuler Embedded目前也提供基于Preempt-RT的实时内核能力,官方文档中将其作为嵌入式Linux软实时能力的重要组成部分,并针对实时内核提供相应的构建和配置方式。

但如果只把PREEMPT_RT理解成“让Linux抢占得更快”,其实还远远不够。

真正理解它,需要从Linux原本的执行机制开始。


一、普通Linux为什么会产生实时延迟?真正的问题不是CPU慢,而是任务什么时候能够运行

先假设一个非常简单的场景。

系统里有两个任务:

Task A:普通任务 Task B:实时任务

Task B的优先级非常高,而且它有严格的周期要求:

每1ms执行一次 要求尽快响应

理想情况下:

Task A运行 ↓ Task B到达 ↓ CPU立即切换到Task B ↓ Task B执行

但现实中的Linux内核并不是任何时候都可以立即切换任务。

因为CPU正在运行的不一定只是用户态程序。

它可能处于:

用户态 ↓ 系统调用 ↓ 内核态 ↓ 中断处理 ↓ softirq ↓ 内核锁 ↓ 设备驱动 ↓ 内存管理

等各种执行路径。

于是,真正的问题就变成:

Task B什么时候可以真正获得CPU?

这就是Linux实时化最核心的问题之一。

如果一个高优先级任务已经进入就绪状态,但CPU迟迟不能切换过去,那么即使调度器最终把它安排执行,这段等待时间仍然会形成实时延迟。

可以把这个过程抽象成:

实时任务到达 ↓ 等待调度 ↓ 当前执行路径结束 ↓ 触发调度 ↓ 切换任务 ↓ 实时任务开始运行

其中最关键的就是:

“当前执行路径什么时候结束?”

如果当前路径非常短,那么延迟就比较小。

如果当前路径很长,而且中间存在无法抢占的区域,那么实时任务就可能等待较长时间。

这也是为什么理解PREEMPT_RT,必须先理解一个Linux内核的重要概念:

抢占。

所谓抢占,可以简单理解为:

当一个更重要的任务已经准备好运行时,操作系统是否允许它打断当前正在运行的任务,并立即获得CPU。

对于普通计算系统来说,抢占当然也很重要。

但是对于实时系统来说,抢占的意义更加直接。

例如:

低优先级任务 L ↓ 正在运行 高优先级任务 H ↓ 突然就绪

如果系统允许立即抢占:

L运行 ↓ H就绪 ↓ 立即抢占L ↓ H运行

那么H的响应时间就比较容易控制。

如果当前内核执行路径不能被抢占:

L运行 ↓ 进入不可抢占区域 ↓ H就绪 ↓ H等待 ↓ 不可抢占区域结束 ↓ 调度 ↓ H运行

那么H必须等待当前执行路径结束。

问题就在这里。

Linux内核中长期存在各种不能随意抢占的执行区域。

这并不是Linux设计得不好,而是因为内核需要保护共享数据结构、保证执行过程的一致性,并且很多底层操作本身就不适合在任意位置被打断。

例如:

修改内核数据结构 访问共享资源 持有某些锁 处理中断 执行关键临界区

这些操作如果允许任意抢占,可能导致数据结构处于不一致状态,甚至产生更加严重的系统错误。

因此,Linux必须在:

系统稳定性

和:

任务可抢占性

之间进行平衡。

而实时系统希望进一步把这个平衡向“可预测延迟”方向移动。

这就是PREEMPT_RT存在的重要原因。

它的目标并不是让Linux所有代码在任何时间、任何位置都可以被抢占,而是尽可能减少长时间不可抢占执行路径,让高优先级任务获得更加及时的响应。

所以,从最简单的角度理解:

普通Linux 重点: 系统功能 + 吞吐 + 通用性 PREEMPT_RT 进一步强调: 高优先级任务的响应时间 + 延迟可预测性

这也是为什么实时Linux并不是简单地“把CPU频率调高”。

CPU更快只能降低部分执行时间。

但是如果任务因为某个不可抢占区域等待了几百微秒甚至几毫秒,那么单纯提升CPU频率并不能从根本上解决问题。

实时性关注的是:

任务为什么没有在应该运行的时候运行。

而PREEMPT_RT解决的正是其中非常关键的一部分。


二、PREEMPT_RT到底改了什么?从内核抢占、中断线程化到实时锁

理解PREEMPT_RT,最容易出现的误区就是把它理解成一个简单的“实时补丁”。

实际上,它涉及Linux内核多个关键机制。

其中最值得理解的包括:

内核抢占、中断线程化、softirq处理、锁机制以及优先级继承。

这些机制共同决定了一个实时任务到底需要等待多久。

先看最重要的——内核抢占。

传统Linux内核中,有一些代码区域不能被普通方式直接抢占。

假设:

CPU正在执行内核代码 ↓ 高优先级实时任务突然就绪 ↓ 当前内核代码还没有到可以调度的位置 ↓ 实时任务继续等待

对于普通系统,这种情况可能完全可以接受。

但是对于实时任务来说,真正重要的是:

当前执行路径最长可能持续多久?

因为实时系统的最坏情况延迟,往往不是由平均执行时间决定,而是由这些“最长不能被打断的路径”决定。

PREEMPT_RT的重要思路之一,就是让更多内核执行路径能够被高优先级任务及时抢占,从而缩短不可抢占区域。

但这只是第一步。

另一个非常重要的问题是:

中断。

传统Linux系统中,硬件发生中断以后,CPU需要快速响应。

例如网卡收到数据:

网卡 ↓ 产生中断 ↓ CPU响应 ↓ 执行中断处理

中断的优先级天然很高,因为硬件事件需要及时处理。

但对于实时任务来说,这又形成了一个矛盾:

如果大量中断不断打断实时任务,那么实时任务本身的确定性怎么办?

因此,PREEMPT_RT非常重要的一项技术就是:

中断线程化。

可以把传统机制粗略理解为:

硬件中断 ↓ CPU立即执行中断处理 ↓ 处理完成

而经过实时化之后,很多中断处理工作会转变为线程上下文执行:

硬件中断 ↓ 快速响应 ↓ 唤醒对应IRQ线程 ↓ IRQ线程执行处理逻辑

这样做的重要意义是什么?

因为线程可以进入Linux调度体系。

一旦进入调度体系,就意味着可以对其进行:

  • 优先级管理;

  • 调度;

  • 抢占;

  • CPU绑定;

  • CPU隔离;

  • 资源管理。

这比一个完全脱离普通调度机制的硬件中断处理路径更加容易控制。

openEuler Embedded官方对于PREEMPT_RT机制的介绍中,也明确提到了中断线程化、软中断线程化、临界区抢占以及优先级继承等实时机制。

可以把这个变化简单画成:

传统Linux 硬件中断 ↓ IRQ Handler ↓ 执行较多处理逻辑 PREEMPT_RT 硬件中断 ↓ 快速响应 ↓ IRQ Thread ↓ 进入调度体系 ↓ 可管理优先级

这时候实时系统就获得了一个非常重要的能力:

可以更加系统地管理中断。

当然,这并不意味着中断对实时性的影响完全消失。

恰恰相反。

中断依然可能影响实时任务。

只是通过线程化之后,我们可以更加明确地控制:

哪个中断优先级更高?

哪个中断可以运行在哪个CPU?

哪个中断不应该进入实时CPU?

这些问题就开始从“硬件中断处理”进入“操作系统资源管理”。

这也正是实时Linux从单纯内核优化走向系统级资源隔离的重要一步。

除了中断之外,softirq也是实时Linux需要考虑的重要因素。

Linux中的网络、定时器以及其他内核子系统会产生softirq。

如果这些softirq在不合适的时间大量执行,也可能影响实时任务。

因此,PREEMPT_RT同样对softirq的处理方式进行了实时化调整,使其更多进入可调度的执行环境。

再往下,就是锁机制。

这是理解PREEMPT_RT非常关键的一环。

Linux内核中存在大量共享资源:

内核数据结构 设备 文件系统 网络栈 内存管理 驱动状态

多个执行单元同时访问这些资源时,需要使用锁进行保护。

例如:

Task A ↓ 获取Lock ↓ 访问共享资源 ↓ 释放Lock

如果Task B此时也需要这个Lock:

Task B ↓ 请求Lock ↓ 等待

对于普通系统来说,只要最终能够拿到锁就可以。

但实时系统需要进一步考虑:

这个锁最长可能让高优先级任务等待多久?

这就是实时锁机制与普通锁机制的重要区别。

PREEMPT_RT的一项重要变化,就是将很多传统自旋锁语义与实时调度机制结合,使锁竞争更加符合实时系统的要求。

这里又会引出一个非常经典的问题:

优先级反转。

例如:

高优先级任务 H ↓ 等待资源 低优先级任务 L ↓ 持有资源 中优先级任务 M ↓ 不断运行

结果就是:

H等L L等CPU M一直运行

最终:

高优先级任务反而被中优先级任务间接阻塞。

这就是优先级反转。

实时系统一般通过**优先级继承(Priority Inheritance)**等机制解决这一问题。

简单来说:

L持有H需要的锁 ↓ H等待L ↓ L临时继承H的高优先级 ↓ L获得更高执行机会 ↓ 尽快释放锁 ↓ H继续执行

这样就可以缩短高优先级任务因为锁竞争产生的等待时间。

这说明一个非常重要的事实:

实时系统的调度器并不是孤立存在的。

调度器、锁、中断、内核抢占实际上是一个整体。

如果只优化其中一个环节,实时性仍然可能受到其他环节限制。

所以,理解PREEMPT_RT最好的方式不是背几个概念,而是把它看成一套系统性的内核实时化机制:

PREEMPT_RT │ ┌──────────────┼──────────────┐ │ │ │ 内核抢占 中断线程化 实时锁 │ │ │ ↓ ↓ ↓ 减少不可抢占区 控制IRQ执行 减少锁等待 │ │ │ └──────────────┼──────────────┘ ↓ 降低最坏情况延迟

因此,PREEMPT_RT真正做的事情,并不是让Linux“跑得更快”。

而是:

让Linux内核中的执行路径更加容易被调度和控制。

这才是它对实时系统真正的意义。


三、PREEMPT_RT并不等于硬实时:为什么“Linux实时化”之后还需要继续优化

讲到这里,一个非常容易产生的问题是:

既然PREEMPT_RT已经解决了抢占、中断和锁的问题,那么Linux是不是已经变成了真正的硬实时操作系统?

答案不能简单地说“是”。

更准确地说:

PREEMPT_RT能够显著增强Linux的实时能力,但实时Linux与传统意义上的硬实时系统之间仍然存在技术边界。

这里最重要的一个词,就是:

确定性。

实时系统关注的不是:

大多数时候很快。

而是:

在规定条件下,最坏情况能够被控制。

而Linux最大的挑战之一,就是系统本身非常复杂。

一个真实Linux设备上可能同时存在:

网络 文件系统 USB PCIe GPU 日志 容器 后台服务 驱动 AI应用 数据库 远程管理 实时控制

这些功能都可能产生CPU活动、中断、内存访问以及内核执行。

即使PREEMPT_RT已经改善了内核抢占,如果实时任务和这些普通任务全部运行在同一个CPU核心上,那么系统仍然可能存在大量干扰。

例如:

CPU0 实时控制任务 普通业务任务 网络任务 内核线程 IRQ softirq 定时器 RCU

实时任务虽然优先级最高,但它仍然生活在一个非常拥挤的CPU环境中。

这就像一辆救护车拥有最高优先级。

即使所有车辆都知道它优先通行,如果道路上仍然存在大量车辆、施工、收费站和交叉路口,它仍然可能受到影响。

所以实时Linux进一步出现了一个非常重要的思想:

不能只提高实时任务的优先级,还需要减少实时任务运行环境中的干扰。

这就是CPU隔离和核心隔离开始发挥作用的地方。

例如一台8核设备:

CPU0 CPU1 CPU2 CPU3 CPU4 CPU5 CPU6 CPU7

可以进行这样的规划:

CPU0-CPU5 普通Linux业务 CPU6-CPU7 实时控制任务

进一步还可以考虑:

普通CPU ↓ 网络 文件系统 日志 后台服务 普通中断 实时CPU ↓ 实时任务 必要的实时中断 必要的系统活动

这时候实时任务的运行环境就发生了变化。

它不再只是:

“我是一个优先级很高的任务。”

而变成:

“我运行在一个专门为实时任务保留的CPU环境中。”

这两种思路有非常大的区别。

第一种是:

调度优先级隔离。

第二种是:

计算资源隔离。

而真正的实时系统往往需要二者结合。

这也是为什么从PREEMPT_RT继续往下研究,会自然进入:

  • CPU affinity;

  • CPU isolation;

  • IRQ affinity;

  • housekeeping CPU;

  • timer tick;

  • RCU;

  • 内核线程隔离;

  • 实时任务绑核。

这些技术共同解决一个问题:

如何减少实时CPU受到的外部干扰?

这时候,我们可以重新看待PREEMPT_RT。

它解决的是:

Linux内核 ↓ 提高可抢占性 ↓ 降低内核路径延迟

而CPU隔离解决的是:

整个系统 ↓ 减少实时任务运行环境中的干扰 ↓ 提升确定性

两者不是替代关系。

更准确地说:

PREEMPT_RT + CPU资源隔离 + IRQ隔离 + 任务调度 + 系统调优

共同构成实时Linux的完整技术体系。

这也是为什么工程实践中不能只看到:

“我们的Linux支持PREEMPT_RT。”

就直接得出:

“我们的系统一定具备优秀的实时性。”

真正需要回答的问题应该是:

实时任务运行在哪个CPU?

哪些普通任务可以进入这个CPU?

哪些中断可以进入这个CPU?

内核线程会不会进入这个CPU?

定时器会不会影响这个CPU?

实时任务发生锁竞争时需要等待多久?

系统负载变化以后,最坏情况延迟是否仍然可控?

这才是完整的实时系统问题。


四、从PREEMPT_RT到核心隔离:实时Linux为什么正在从“调度优化”走向“资源隔离”

如果把前面的内容串起来,会发现实时Linux的发展实际上存在一条非常清晰的技术路线。

最初的问题是:

Linux为什么不能保证实时响应?

于是出现:

内核抢占优化。

进一步发现:

中断也会影响实时任务。

于是出现:

中断线程化。

继续发现:

锁竞争会造成高优先级任务等待。

于是引入:

实时锁和优先级继承。

然后又发现:

即使这些问题都解决了,实时任务和普通任务仍然可能争夺同一个CPU。

于是进一步进入:

CPU affinity和CPU isolation。

再继续发现:

即使CPU已经隔离,中断、定时器、内核线程等系统活动仍然可能进入实时CPU。

于是又需要:

IRQ affinity、housekeeping CPU以及更细粒度的内核资源隔离。

最终形成:

普通Linux ↓ PREEMPT_RT ↓ 调度优化 ↓ 中断优化 ↓ 锁优化 ↓ CPU Affinity ↓ CPU Isolation ↓ IRQ Isolation ↓ 核心级资源隔离 ↓ 确定性实时环境

这条路线非常值得理解,因为它解释了为什么“实时Linux”并不是一个单独技术点。

它更像是一套系统工程。

而这也给国产实时操作系统带来了一个非常重要的技术方向:

从实时内核能力进一步走向实时资源管理能力。

openEuler Embedded的发展其实已经体现出类似趋势。

官方文档并没有把实时能力简单限定在PREEMPT_RT,而是进一步提供RTOS支持以及MICA混合关键性部署框架,使Linux和不同实时系统能够根据任务特性承担不同工作。官方资料介绍,MICA面向混合关键性场景,可以让Linux承担通用系统管理、文件系统、网络等任务,同时让RTOS承担实时控制和实时计算,并通过共享内存、OpenAMP等方式实现不同OS之间的通信。

这背后实际上也是一种资源隔离思想:

复杂任务 ↓ Linux 实时任务 ↓ RTOS 高实时任务 ↓ 独立资源域

从操作系统架构角度来看,这已经不再是简单的“Linux实时化”。

而是:

根据任务的实时等级、关键程度以及资源需求,为不同任务分配不同的执行环境。

这就是混合关键性系统的重要思想。

对于工业设备而言,一个设备里面可能同时存在:

一级任务: 运动控制 二级任务: 传感器处理 三级任务: 网络通信 四级任务: 数据分析 五级任务: 日志和远程运维

它们对实时性的要求显然不同。

如果所有任务都放在同一个CPU资源池里,就需要通过调度器不断协调。

而如果能够根据关键程度进行资源划分:

关键实时任务 ↓ 实时CPU 一般任务 ↓ 普通CPU 复杂业务 ↓ Linux 辅助控制 ↓ RTOS

整个系统就会更加容易进行确定性设计。

因此,今天重新理解PREEMPT_RT,可以得出一个很重要的结论:

PREEMPT_RT不是实时Linux的终点,而是实时Linux走向确定性计算的重要基础。

它解决了Linux内核实时化过程中的关键问题。

但真正面向工业控制、机器人、智能制造等复杂场景,还需要进一步解决:

CPU资源隔离。

中断隔离。

内核活动隔离。

实时任务隔离。

不同关键等级任务之间的资源边界。

这也是为什么下一阶段的实时Linux技术研究,会越来越多地从“调度器”进入“资源管理”。


五、真正理解实时Linux:PREEMPT_RT解决的是“能不能及时抢占”,核心隔离解决的是“谁拥有确定的CPU”

到这里,可以重新回答最开始的问题:

PREEMPT_RT到底改变了什么?

最简单的答案是:

它让Linux更加适合实时任务运行。

但如果进一步回答:

它究竟解决了实时系统中的什么问题?

答案就更加准确:

PREEMPT_RT通过内核抢占、中断线程化、实时锁以及优先级继承等机制,减少Linux内核中可能造成长时间阻塞的执行路径,让高优先级实时任务更加及时地获得CPU,从而降低最坏情况调度延迟。

这就是PREEMPT_RT最核心的技术价值。

但它同时也告诉我们:

实时性并不只存在于调度器里面。

一个任务能不能及时执行,至少取决于:

调度 + CPU + 中断 + 锁 + 内核 + 驱动 + 系统负载

所以,真正的实时Linux优化一定是系统级的。

对于开发者来说,可以把整个技术体系理解成四个层次。

第一层:

调度实时化。

解决:

谁先执行?

包括SCHED_FIFO、SCHED_RR、SCHED_DEADLINE等。

第二层:

内核实时化。

解决:

当前内核执行路径什么时候能够被抢占?

这就是PREEMPT_RT重点解决的问题。

第三层:

资源实时化。

解决:

实时任务运行在哪些CPU?

哪些任务和中断不能干扰它?

这就是CPU affinity、CPU isolation、IRQ affinity等技术发挥作用的地方。

第四层:

系统架构实时化。

解决:

不同关键等级的任务到底应该运行在哪一种操作系统和资源环境中?

这就进一步进入:

Linux + RTOS

以及:

混合关键性系统

等更高层次的架构设计。

如果从这四个层次看,openEuler Embedded的实时能力就更容易理解了。

它并不是单纯追求:

“让Linux变成一个RTOS。”

而是在尝试让Linux生态进入实时计算场景,并进一步通过PREEMPT_RT、RTOS、混合部署等技术覆盖不同实时等级的嵌入式应用。

这对于国产操作系统来说具有非常现实的意义。

因为今天的工业设备已经越来越不像传统意义上的“控制器”。

一台机器人可能同时需要:

实时运动控制 + 视觉处理 + AI推理 + 网络通信 + 设备管理 + 数据采集 + 远程运维

一台智能制造设备可能同时需要:

PLC控制 + 工业网络 + 实时数据采集 + 边缘计算 + AI + 设备管理

这些任务显然不可能拥有完全相同的实时要求。

因此,未来操作系统真正需要解决的不是:

“Linux还是RTOS?”

而是:

不同任务应该获得什么样的执行环境?

这也是实时操作系统技术继续发展的重要方向。

而从PREEMPT_RT继续往下,就会遇到一个无法绕开的技术问题:

如果我们已经知道实时任务需要更加稳定的CPU资源,那么如何真正把一个CPU核心从普通Linux环境中“隔离”出来?

这里的“隔离”并不是简单地执行一条taskset命令,把一个程序绑到某个CPU上。

因为:

CPU绑定 ≠ CPU隔离

一个任务不运行在CPU 7,并不代表CPU 7就没有其他干扰。

CPU 7上仍然可能存在:

IRQ softirq 内核线程 timer RCU 后台任务 系统调度活动

所以真正的核心隔离,需要回答一个更加复杂的问题:

如何让一个CPU核心尽可能只服务于实时任务?

这也是实时Linux从PREEMPT_RT继续向前发展的关键一步。

从这里开始,我们就进入一个比“实时调度”更加重要的概念:

核心隔离。

核心隔离解决的并不是:

“如何让实时任务拥有更高优先级?”

而是:

“如何让实时任务拥有更加独立、更加确定的CPU执行环境?”

对于工业控制、机器人、飞控、实时仿真以及智能制造等场景来说,这个问题甚至可能比单纯提高调度优先级更加重要。

因为一个真正的实时系统,最终需要解决的不是:

让实时任务跑得更快。

而是:

让影响实时任务运行时间的不确定因素尽可能少。

这也是从:

PREEMPT_RT

走向:

CPU隔离

再走向:

核心隔离

最终走向:

确定性实时系统

的技术演进路径。

对于国产实时操作系统而言,这同样是一条值得持续研究的路线。

openEuler Embedded已经通过PREEMPT_RT、RTOS以及混合关键性部署等技术探索Linux生态与实时计算之间的结合,而以望获OS为代表的国产实时操作系统,也可以从另一个角度继续研究实时任务的资源隔离、核心隔离以及确定性执行环境。

当我们不再只问:

“Linux能不能实时?”

而开始问:

“实时任务究竟应该拥有多少CPU资源?”

“哪些系统活动不能进入实时核心?”

“如何保证普通业务不会干扰实时任务?”

“如何让最坏情况延迟更加可控?”

那么,实时Linux真正有价值的技术问题才刚刚开始。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询