
上一篇文章我们讨论了一个问题从普通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此时也需要这个LockTask 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 affinityCPU isolationIRQ affinityhousekeeping CPUtimer tickRCU内核线程隔离实时任务绑核。这些技术共同解决一个问题如何减少实时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技术研究会越来越多地从“调度器”进入“资源管理”。五、真正理解实时LinuxPREEMPT_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真正有价值的技术问题才刚刚开始。