从访存的角度理解RTOS的实时性
RTOS(Real Time Operation System)的一大特性就是实时性强,那么什么是实时性呢?笼统的来说,不是任务处理的快就叫做实时,实时的核心是确定性,又叫可预测性,那么进一步来说,如何理解所谓的确定性呢?这篇文章以内存访问为例,通过对比通用系统中内存访问的过程和实时系统中内存访问的过程,来具体的理解什么叫做确定性。
下图是linux中内存访问的概略流程图,可以看到根据目标内存当前的状态,访问内存的步骤是不一样的:
Linux是多进程系统,用户程序访问的都是虚拟地址,所以用户程序发起内存访问时,访问的都是虚拟地址,CPU需要根据虚拟地址找到对应的物理地址,才能真正实现内存访问。在linux上,这个查找物理地址的过程就如上图所示(这个过程看起来很复杂,其本质是为了获取复杂软件功能下硬件平台的极致性能,后续有机会另开一篇文章详细介绍这个机制的设计,这里先略过,先关注这个过程中的行为):
1. MMU触发地址转换,先查询目标地址是否在TLB(Translation Lookaside Buffer,地址转换旁路缓冲器)中
- TLB 命中:直接从 TLB 中拿到虚拟地址对应的物理页框号(PFN),跳过页表查询,直接进入 Cache 访问阶段;
- TLB 未命中:MMU 会遍历内存中的页表,查找该虚拟地址对应的页表项。
2. 检查页表项有效性:遍历页表找到对应的 PTE 后,检查内存页是否在物理内存中:
- 页在物理内存中,提取物理页框号,拼接偏移量得到完整物理地址,准备访问实际内存
- 页不在物理内存中,触发缺页中断,CPU进入异常处理。
3.缺页中断处理:内核的缺页中断处理有3中情况:
- 页未分配:分配空闲物理页,此时目标地址获得对应的物理地址,可以准备访问
- 页被换出(Swap):分配空闲物理页,从swap分区读回物理页数据,准备访问
- 权限错误:进入错误处理
Swap 是 Linux 应对物理内存不足的机制,内核的页面回收器(kswapd)会把不常用的物理页数据写入 Swap 分区(一般在磁盘中),同时释放物理内存以回收空闲物理内存;当访问这些页时,就会触发换入缺页中断,从磁盘读回数据。
4.物理地址的 Cache 访问:拿到物理地址后,CPU 会访问 CPU Cache:
- Cache 命中:直接从 Cache 中读写数据,流程结束;
- Cache 未命中:CPU 发起总线请求,访问物理内存(DRAM),把数据加载到 Cache 中(同时返回给 CPU),流程结束。
可以看到,在上面的内存访问模型里,同样的内存访问,最快的情况cpu可以直接从tlb中获取物理地址,然后从cache里拿到实际数据,但是最慢的情况cpu需要处理缺页中断,从磁盘读取数据,我们知道磁盘访问和cache访问的速度差了好几个数量级,也就是说,对同一个访存语句(例如int a = *ptr),最快的情况可能只需要几纳秒(直接tlb + cache获取数据),最慢的情况可能需要几毫秒(swap获取数据需要访问磁盘,io访问极其耗时),这个就是不确定性,也就是我们常说的为什么Linux不是硬实时操作系统的一个原因。
那么回到RTOS,从访存的实时性来说,如何保证访存速率的一致性就是保证实时性的一项要求,以我的了解有几种不同的方案:
- 以VxWorks和FreeRTOS这种宏内核系统为代表,VxWorks和FreeRTOS其实可以看作是单进程多线程的系统,所有任务包括内核都工作在同一个地址空间下,这种情况下就不需要考虑虚拟内存,所有的访存操作直接访问物理内存,那么从虚拟内存到物理内存的映射过程中带来的不确定性就没有了(VxWorks下依然保留了虚拟内存到物理内存映射的API,但是实际上虚拟地址和物理地址是一一对应的,保留接口只是为了兼容性),带来的后果就是系统的稳定性会差一些,因为用户任务与内核工作在同一地址空间,且都是特权模式,就意味着用户任务crush会直接导致系统崩溃,当然,另一个优势就是系统效率会更高。
- 以QNX这种微内核系统为代表,微内核的核心即是内核独立且简单,其他用户程序包括驱动和文件系统都以独立的进程存在,以此保证系统的稳定性,也就是说多进程是QNX这种微内核系统的核心特性,那么在QNX上要保证访存的实时性,虚拟内存的映射就是无法绕开的问题,所以QNX上更多的是针对虚拟内存映射的过程进行优化,比如通过禁用swap,禁止在周期任务内动态分配内存等方式来尽量减小内存寻址过程中的不确定性,以保证应用程序访问内存的确定性。
相对来说,VxWorks这种宏内核的处理相对简单粗暴,但是结合其系统特性也非常有效;QNX的处理就要更精细一些,两者的侧重点略有不同,但是目的都是一样,尽量保证同种操作的一致性。
另一方面,在有些异构平台上运行VxWorks时,可能还会关闭cache访问以保证数据的一致性,比如在FPGA+ARM的SOC上,为了保证VxWorks与FPGA之间数据交互的可靠性,会需要关闭cache访问以免VxWorks读到脏数据,这种情况下VxWorks相当于直接访问物理内存,由于失去了cache的高速访问特性加速内存访问,程序的访存速率整体上是降低的,但是访存的一致性上却是更高了(因为没有了cache命中和不命中带来的差别),某种程度上这算是一种追求实时性或者说确定性而做出的性能上的妥协,我觉得这一点也能帮助大家更好的理解实时性:实时系统更需要的是确定性,而并非更快更高效。