2. VxWorks内存管理架构总览
VxWorks 的内存管理并不是“一个 malloc 函数”那么简单,它由硬件内存管理单元(MMU)、内核内存分配器、区域(region/partition)划分机制以及任务上下文共同构成。理解这套架构的关键,是先分清“谁在管内存”“内存被切成什么”“内核和用户空间如何隔离”,最后才能谈实时性约束下该如何取舍。
一、MMU 在 VxWorks 中扮演什么角色
VxWorks 支持两种典型构建形态:无 MMU 的 UP/AMP 配置与带 MMU 的 RTP(Real-Time Process)配置。在无 MMU 时,所有任务共享同一个物理地址空间,虚拟地址等于物理地址,访问速度快、确定性高,但一个野指针就可能踩坏内核数据。带 MMU 时,MMU 负责虚拟地址到物理地址的转换,并提供页级保护属性(可读、可写、可执行、缓存策略)。
MMU 的核心作用有三点:地址翻译、访问权限检查、缓存属性控制。VxWorks 通过页表把地址空间划分为内核空间与用户空间,用户态 RTP 只能访问映射给自己的页,越界访问会触发异常而不是静默破坏内核。代价是每次地址翻译增加 TLB 查找开销,缺页和 TLB miss 会引入不确定性,这正是实时性设计中必须权衡的地方。
二、内存区域划分:从物理内存到可用池
系统启动时,VxWorks 首先识别物理内存布局,把内存划分为若干区域。典型划分包括:
- 内核映像区:存放 vxWorks 映像、只读代码段与初始化数据,通常映射为只读+可执行。
- 系统内存池(system memory pool):内核与驱动动态分配的主要来源,由 memPartLib 管理。
- RTP 用户空间:每个 RTP 拥有独立虚拟地址空间,其堆由用户态分配器管理。
- 共享内存区:用于 RTP 之间或核间通信,需显式映射并约定同步方式。
- 保留区:给 DMA、外设寄存器或 bootrom 预留,避免被分配器占用。
这些区域不是自动最优的,需要根据目标板内存大小、DMA 缓冲需求、RTP 数量手工规划。例如一块 512 MB DDR 的板卡,可能保留 64 MB 给 DMA 一致性缓冲,其余交给系统池和 RTP 堆。
三、内核空间与用户空间的边界
在带 MMU 的配置中,内核空间通常占据虚拟地址的高半区,用户 RTP 占据低半区。内核任务运行在特权模式,可以直接访问系统内存池;RTP 运行在非特权模式,只能访问映射到自己的页。两者之间的数据交换必须通过系统调用或共享内存完成。
这种隔离带来两个直接后果。第一,RTP 内的内存错误不会直接导致内核崩溃,系统可靠性提升。第二,每次系统调用都要做特权级切换和参数拷贝,延迟比内核内直接调用高。对于周期小于 1 ms 的硬实时任务,通常仍放在内核态执行;对于周期在 10 ms 以上、逻辑复杂且允许一定抖动的业务,放进 RTP 更合适。
四、实时性约束下的设计取舍
实时性对内存管理的核心要求是确定性:分配时间可预测、访问延迟可预测、不会因为缺页或碎片导致长尾延迟。围绕这一点,VxWorks 提供了多种机制,取舍如下:
| 机制 | 确定性 | 隔离性 | 适用场景 |
|---|---|---|---|
| memPartLib 固定分区 | 高(O(1)) | 低 | ISR、硬实时任务 |
| memPartLib 可变分区 | 中(碎片影响) | 低 | 内核驱动、启动阶段 |
| RTP 堆(malloc) | 低(缺页+锁) | 高 | 非关键业务、复杂逻辑 |
| 共享内存 + 信号量 | 中 | 中 | RTP 间大数据传递 |
实践中常见的做法是:在系统启动阶段一次性预分配所有关键缓冲,运行期不再调用动态分配。例如一个 1 kHz 的控制任务,可以在初始化时用 memPartAlloc 从固定分区取出 4 个 2 KB 缓冲循环使用,避免运行期分配带来的抖动。
/* 示例:启动时预分配固定分区缓冲,运行期零动态分配 */
#define BUF_SIZE 2048
#define BUF_COUNT 4
char *ctrlBuf[BUF_COUNT];
STATUS initCtrlBuffers(void)
{
int i;
for (i = 0; i < BUF_COUNT; i++) {
ctrlBuf[i] = (char *)memPartAlloc(ctrlPartId, BUF_SIZE);
if (ctrlBuf[i] == NULL) {
return ERROR; /* 分区不足,启动即失败,避免运行期才发现 */
}
}
return OK;
}
/* 运行期只做 ctrlBuf[idx] 的读写与索引轮转,不再 alloc/free */
总结来看,VxWorks 内存管理架构的本质是在“确定性”与“隔离性”之间做分层:内核态用固定分区保确定性,用户态用 MMU 保隔离性,共享内存做受控的数据交换。设计时先明确任务的周期与抖动上限,再决定它落在哪一层、用哪种分配器,最后把关键缓冲在启动阶段全部预分配完毕,运行期只做读写与轮转。这样得到的系统,内存行为才是可预测、可验证的。