2. INTEGRITY内存管理基础
在INTEGRITY这类面向安全关键领域的实时操作系统中,内存管理并不是一个“可选项”,而是隔离、确定性与可验证性的基础。理解这一层,需要先把“地址”这件事拆成两个世界:物理地址空间与虚拟地址空间。物理地址空间是处理器总线实际访问的地址范围,直接对应DRAM、SRAM、Flash、外设寄存器等硬件资源;虚拟地址空间则是每个执行单元(任务、分区、进程)看到的地址视图,由MMU或MPU参与约束。INTEGRITY的典型部署中,分区(Partition)是隔离的基本单位,每个分区可以拥有独立的虚拟地址空间,而内核与各分区的映射关系由系统配置阶段静态确定,运行期尽量不发生变化,这正是实时性与可验证性的来源。
MMU与MPU的角色差异,是本节最容易被混淆的地方。MMU(Memory Management Unit)以页表为机制,把虚拟地址翻译为物理地址,并在此过程中检查权限与属性;它支持地址空间的重叠与隔离,适合需要独立地址视图的复杂系统。MPU(Memory Protection Unit)不进行地址翻译,只对物理地址区域做基址与边界检查,判断某次访问是否落在允许区域内、是否具备读/写/执行权限。INTEGRITY可以运行在带MMU的处理器上,也可以运行在只有MPU的处理器上;前者提供更彻底的地址空间隔离,后者提供更轻量、更可预测的保护。选择哪一种,取决于芯片能力、认证目标与实时性约束,而不是单纯看“谁更高级”。
地址转换机制的核心,是“翻译”与“检查”同时发生。以MMU为例,处理器发出虚拟地址后,MMU按页表层级逐级查找:先取页全局目录项,再取页中间目录项,最后取页表项,得到物理页帧号,与页内偏移拼接成物理地址。每一步都会检查表项的有效位;若无效,触发缺页异常。INTEGRITY在配置阶段就把分区所需的页表建好,运行期通常不进行按需分页,因此缺页异常往往意味着配置错误或非法访问,而不是正常的换页行为。页表项中除了物理页帧号,还包含访问权限位(读/写/执行)和内存属性位,这两类信息共同决定“这次访问能不能做”以及“这次访问以什么方式做”。
MPU的机制更直接:它维护若干区域描述符,每个描述符包含基址、大小和权限。处理器发出物理地址后,MPU并行比较该地址是否落在某个区域内;若命中,再检查访问类型是否被允许;若未命中任何区域,则按默认策略处理,通常是拒绝。MPU没有页表,因此没有多级查找开销,地址检查的时序更可预测,适合硬实时场景。但MPU的区域数量有限,通常只有8到16个,且区域不能重叠(或重叠规则受限),因此它更适合“少量、固定、粗粒度”的保护划分,而不是“大量、动态、细粒度”的地址空间隔离。
内存属性配置是容易被忽视、却直接影响正确性的一环。属性描述的是“这段内存该怎么访问”,而不是“能不能访问”。常见属性包括:Normal(可缓存、可合并、可乱序)、Device(不可缓存、可合并、可乱序,用于外设寄存器)、Strongly-Ordered(不可缓存、不可合并、严格顺序,用于对顺序敏感的控制寄存器)。在INTEGRITY中,代码段通常配置为Normal且可缓存,数据段配置为Normal,外设寄存器映射区配置为Device或Strongly-Ordered。若把外设寄存器错误地配置为Normal可缓存,写操作可能被合并或延迟,读操作可能返回缓存中的旧值,导致驱动行为异常;若把共享内存配置为可缓存但未做一致性维护,多核之间可能读到过期数据。因此,属性配置必须与硬件手册中的内存映射表逐项核对。
下面给出一个教学示例,用于说明配置阶段需要确定的关键参数。假设某处理器物理地址空间如下:DRAM从0x4000_0000开始,大小256MB;共享SRAM从0x2000_0000开始,大小64KB;外设寄存器从0xE000_0000开始,大小1MB。分区A的代码段映射到DRAM 0x4000_0000,大小1MB,权限R-X,属性Normal;分区A的数据段映射到DRAM 0x4002_0000,大小1MB,权限RW-,属性Normal;共享内存窗口映射到SRAM 0x2000_0000,大小64KB,权限RW-,属性Normal且需考虑一致性;外设寄存器映射到0xE000_0000,大小1MB,权限RW-,属性Device。这些参数在配置文件中以区域或页表项的形式表达,编译后由INTEGRITY在启动阶段加载到MMU或MPU。
/* 教学示例:内存区域配置片段(示意,非真实工程文件) */
region partition_A_code {
virtual_base = 0x00000000;
physical_base = 0x40000000;
size = 0x00100000; /* 1 MB */
access = READ | EXECUTE;
attribute = NORMAL;
};
region partition_A_data {
virtual_base = 0x00100000;
physical_base = 0x40020000;
size = 0x00100000; /* 1 MB */
access = READ | WRITE;
attribute = NORMAL;
};
region shared_sram {
virtual_base = 0x80000000;
physical_base = 0x20000000;
size = 0x00010000; /* 64 KB */
access = READ | WRITE;
attribute = NORMAL;
};
region device_regs {
virtual_base = 0xE0000000;
physical_base = 0xE0000000;
size = 0x00100000; /* 1 MB */
access = READ | WRITE;
attribute = DEVICE;
};
配置完成后,如何判断结果是否符合预期?可以从三个层面观察。第一,启动日志中通常会打印各分区的地址映射与属性摘要,检查虚拟基址、物理基址、大小、权限、属性是否与设计一致。第二,在分区内执行一次对自身代码段的写操作,预期应触发保护异常;执行一次对未映射地址的访问,预期应触发缺页或访问异常。第三,对外设寄存器执行一次写后读,若属性配置为Device,读回应为刚写入的值;若错误配置为Normal可缓存,可能读到旧值或出现写合并延迟。这些观察点不需要复杂工具,但能有效验证地址转换与属性配置是否真正生效。
需要强调的是,MMU与MPU并非互斥,也不是“有MMU就不需要MPU”。在带MMU的系统中,页表项本身已经承担了权限与属性检查;在只有MPU的系统中,MPU承担全部保护职责。INTEGRITY的设计允许在不同硬件上采用不同策略,但配置模型和验证方法保持一致:先明确物理资源,再划分虚拟视图,然后确定翻译或检查机制,最后逐项核对权限与属性。把这条链路走通,后续的分区隔离、共享内存通信和外设访问才有可靠基础。