大家好,我是良许。
为什么Linux不是实时操作系统?

这个问题,我在做嵌入式的时候被问过很多次。
有人说:"Linux不是有实时调度策略吗?SCHED_FIFO、SCHED_RR这些,听起来很实时啊。"
还有人问:"如果把所有进程都设成实时调度,Linux能不能当实时系统用?"
答案是:不能。
或者说,不完全能。
咱先来搞清楚什么叫实时操作系统。
很多人以为实时就是"快",其实不是。实时的核心是"可预测性",也就是说,系统对事件的响应时间必须是确定的、有上限的。
举个例子。一个普通系统,处理一个中断,可能需要10微秒,也可能需要1毫秒,甚至更长。这个时间是不确定的。
但实时系统不行。实时系统必须保证,处理这个中断,最坏情况下也不会超过100微秒。这个"最坏情况"的上限,就是实时性的关键。
Linux为什么做不到?
因为Linux的设计目标,从一开始就不是实时系统。
Linux是通用操作系统,设计目标是高吞吐量、高并发、好的平均性能。为了达到这个目标,Linux做了很多优化:复杂的调度算法、动态内存管理、缓存机制、中断合并...
这些优化能提升平均性能,但会牺牲实时性。
比如说,Linux的调度器。Linux用的是CFS(完全公平调度器),它的目标是让所有进程公平地分享CPU时间。但"公平"和"实时"是矛盾的。
实时系统需要的是:高优先级的任务必须立即执行,不管其他任务等了多久。但CFS会考虑所有任务的等待时间,避免某个任务饿死。
这就导致,即使你设置了SCHED_FIFO,Linux也不能保证你的任务一定能在规定时间内执行。
为什么?因为Linux内核本身不是完全可抢占的。
什么意思?就是说,当内核在执行某些关键代码的时候,是不能被打断的。比如说,内核在操作某个数据结构,为了保证一致性,会加锁。这个锁持有期间,即使来了一个高优先级的实时任务,也得等着。
这个等待时间,就是不可预测的。
我当年做汽车电子的时候,就遇到过这个问题。我们有个控制模块,需要每10毫秒采集一次传感器数据。我用SCHED_FIFO把这个任务设成最高优先级,理论上应该没问题。
但实际测试的时候,发现偶尔会有延迟。大部分时候是10毫秒,但有时候会到15毫秒,甚至20毫秒。
为什么?后来分析发现,是因为内核在处理网络中断的时候,禁用了抢占。我的实时任务虽然优先级高,但也得等内核处理完网络中断才能执行。
这就是Linux的问题:它不能保证最坏情况下的响应时间。
那有没有办法改进呢?有,就是PREEMPT_RT补丁。
PREEMPT_RT是一个内核补丁,它的目标就是让Linux变成实时系统。它做了什么呢?
第一,让内核完全可抢占。原来那些不能被打断的代码段,现在都可以被打断了。
第二,把中断处理线程化。原来中断处理是在中断上下文执行的,优先级最高,不能被抢占。现在把中断处理变成内核线程,可以被更高优先级的任务抢占。
第三,把自旋锁改成可睡眠的互斥锁。原来持有自旋锁的时候,是不能睡眠的,也不能被抢占。现在改成互斥锁,持有锁的线程可以被抢占,等待锁的线程可以睡眠。
这些改动,大大降低了Linux的最大延迟。
我见过一个测试数据:标准Linux内核,最大延迟可能有几毫秒;打了PREEMPT_RT补丁之后,最大延迟可以降到100微秒以内。
这个数据,对很多应用来说,已经够用了。
但即使打了PREEMPT_RT补丁,Linux还是不能算严格意义上的硬实时系统。
为什么?因为还有一些因素是无法控制的。
比如说,内存管理。Linux用的是虚拟内存,有缺页中断。如果你的实时任务访问了一个不在内存中的页面,就会触发缺页中断,需要从磁盘加载数据。这个时间,可能是几毫秒,甚至更长。
怎么办?你得用mlock把实时任务的内存锁定在物理内存中,避免缺页。
再比如,动态内存分配。malloc/free的时间是不确定的,可能很快,也可能很慢。如果你在实时任务中调用malloc,就可能导致延迟。
怎么办?你得提前分配好内存,或者用内存池,避免在实时路径上调用malloc。
还有,中断。即使你的任务优先级再高,也得等中断处理完。如果系统中有很多中断,而且中断处理时间很长,你的实时任务就会被延迟。
怎么办?你得优化中断处理,把耗时的操作放到线程中;或者用CPU隔离,把实时任务绑定到一个专门的核上,这个核不处理中断。
所以你看,即使用了PREEMPT_RT,要让Linux达到实时性能,还需要做很多额外的工作。
这就是为什么,真正对实时性要求极高的场景,还是会用专门的实时操作系统,比如VxWorks、QNX、FreeRTOS...
这些系统,从设计上就是为实时而生的。内核小、可预测性强、最大延迟有保证。
但Linux也有它的优势:生态好、功能全、社区活跃。很多应用,不需要微秒级的实时性,毫秒级就够了。这种情况下,用Linux + PREEMPT_RT,是个很好的选择。
我现在做的很多项目,就是这个方案。汽车电子、工业控制,这些领域对实时性有要求,但不是极端的硬实时。用Linux + PREEMPT_RT,既能满足实时性要求,又能享受Linux的生态。
所以,回到最初的问题:为什么Linux不是实时操作系统?
因为它的设计目标不是实时。
但通过PREEMPT_RT补丁和一些优化手段,Linux可以达到软实时的性能,满足很多应用的需求。
至于"把所有进程都设成实时调度,能不能当实时系统用"?
答案是:不能。
因为实时调度只是解决了任务优先级的问题,但没有解决内核延迟、中断延迟、内存管理这些问题。
真正的实时性,需要从内核层面做优化,而不只是改调度策略。
这就是答案。

END
来源:良许Linux