大家好,我是良许。

这个问题让我想到了前两天有个做汽车电子的朋友问我:"Linux不是分时系统吗?怎么保证实时性?"
这问题问得我一愣。
不是因为问题太简单,而是因为这个问题背后,藏着整个嵌入式行业一个巨大的认知误区。很多人以为Linux就是不实时的,要实时就得上RTOS。
但现实是,满大街跑的汽车,很多都在用Linux做车载系统。
你说它不实时?那这些车是怎么开起来的?
咱先来看看什么是实时性。实时系统不是说运行得有多快,而是说响应时间可预测、可控制。比如说,一个刹车信号过来,系统必须在10毫秒内响应,这就是硬实时要求。如果是软实时,那可能允许偶尔超时,但大部分情况下要满足要求。
标准Linux确实不是为实时设计的。它的调度器是为吞吐量优化的,不是为延迟优化的。内核里有很多长时间不可抢占的代码段,中断处理也可能被延迟...这些都会影响实时性。
但这不代表Linux就不能做实时。
我在外企做汽车电子那几年,用的就是Linux。我们的系统要处理CAN总线数据,要响应各种传感器信号,延迟要求都在毫秒级。你猜我们怎么做的?
第一招,PREEMPT_RT补丁。
这是Linux社区维护的一个实时补丁集,把内核里大量的spinlock改成可抢占的mutex,把中断处理改成线程化,大幅减少了不可抢占的代码段。打上这个补丁,Linux的实时性能能提升一个数量级。
我记得当年我们测试过,标准内核的最大延迟可能有几百微秒,甚至上毫秒;打了RT补丁之后,能稳定在100微秒以内。对于大部分工控、汽车电子应用来说,这个延迟已经完全够用了。
第二招,CPU隔离和中断亲和性。
Linux支持把某些CPU核心隔离出来,专门跑实时任务,不让内核调度器去碰它们。同时可以把关键中断绑定到特定的CPU核心上,避免中断处理被其他任务干扰。
我们当时做的一个项目,用的是四核ARM处理器。我们把两个核心隔离出来,专门跑实时任务,另外两个核心跑普通的应用逻辑。这样一来,实时任务的响应时间非常稳定,基本不受其他任务的影响。
第三招,优先级和调度策略。
Linux支持多种调度策略,包括SCHED_FIFO和SCHED_RR这种实时调度策略。把关键任务的优先级设高,用实时调度策略,它就能抢占其他所有任务。
但这里有个坑。很多人以为把优先级设到99就万事大吉了,结果发现系统还是会卡。为什么?因为他们忽略了中断处理、内核线程这些东西的优先级。你得把整个系统的优先级体系理清楚,才能真正做到实时。
我当年就踩过这个坑。有次调试一个CAN总线驱动,发现数据接收总是有延迟。查了半天,发现是因为软中断处理的优先级太低,被其他任务抢占了。后来把软中断线程的优先级提上去,问题就解决了。
第四招,内存管理优化。
Linux的内存管理也会影响实时性。比如说page fault会导致不可预测的延迟,swap更是实时系统的大敌。所以实时应用一般都要做内存锁定,用mlock或者mlockall把关键代码和数据锁在物理内存里,避免被换出去。
还有就是要避免动态内存分配。malloc在实时路径上是大忌,因为你不知道它什么时候会触发内存整理或者系统调用。我们的做法是启动时预分配好内存池,运行时只从池子里取,保证延迟可控。
当然,如果你的实时要求特别苛刻,比如说要微秒级的响应,那可能就得考虑双系统方案了。
什么意思呢?就是在Linux之下再跑一个微内核或者hypervisor,把硬实时任务放到微内核上跑,软实时和非实时任务放到Linux上跑。这种方案在工业控制领域用得比较多,比如Xenomai、RTLinux这些。
但说实话,这种方案复杂度高,开发和维护成本都不低。如果不是真的有硬实时需求,一般不建议这么搞。
我见过太多项目,一开始就想着上双系统,结果搞得系统复杂度爆炸,调试困难,最后发现其实根本不需要那么高的实时性。需求分析没做好,技术选型就容易走偏。
回到最开始那个问题:Linux怎么保证实时性?
答案是,看你的实时要求是什么。如果是毫秒级的软实时,标准Linux加点优化就够了;如果是几百微秒的硬实时,上PREEMPT_RT补丁;如果是微秒级的硬实时,那可能得考虑双系统或者直接换RTOS。
技术选型从来不是非黑即白的。
关键是要搞清楚你的需求,然后选最合适的方案,而不是最高大上的方案。