1. VxWorks内存管理概述:VxWorks内存架构、MMU与MPU的区别、内存管理单元基础概念

大家好,我是老李。做嵌入式系统这么多年,尤其是跟VxWorks打交道的时间,少说也有十几年了。今天咱们开始聊内存管理这块硬骨头。说实话,很多工程师对内存管理要么一知半解,要么干脆绕道走。但你要做BSP开发,这关必须过。

嗯,咱们先从最基础的说起。

1.1 VxWorks内存架构:它到底长什么样?

VxWorks的内存架构,说白了就是一张地图。这张地图告诉你,CPU能看到的地址空间是怎么划分的。

我个人习惯把VxWorks的内存架构分成三个层次来看:

  • 物理内存:就是板子上焊的那几颗DDR颗粒。实实在在的硬件资源。
  • 虚拟内存:CPU通过MMU映射出来的地址空间。每个任务看到的都是独立的虚拟地址。
  • 内核空间 vs 用户空间:VxWorks 7以后,这个划分越来越清晰了。

我在项目中遇到过一件事。有个同事调试一个网络驱动,死活找不到内存越界的源头。后来我让他把物理地址和虚拟地址的映射关系打印出来一看,好家伙,DMA缓冲区映射到了两个不同的物理页上。这种问题,不懂内存架构根本查不出来。

核心要点:VxWorks的内存架构核心就是「映射」二字。物理地址怎么映射到虚拟地址,谁有权访问哪块区域,这就是内存管理的全部秘密。

VxWorks的内存布局大致是这样的:

区域 起始地址(典型值) 用途
LOCAL_MEM_LOCAL_ADRS 0x80000000 本地物理内存起始
WDB_POOL 0x80100000 调试代理内存池
FREE_RAM 0x80200000 系统可用内存
KERNEL_HEAP 0x81000000 内核堆空间

你想想看,这些地址在配置文件中都能找到。但真正跑起来以后,MMU一开,你看到的地址就完全不一样了。

3.2 MMU与MPU的区别:别搞混了

这个问题我几乎每次培训都会被问到。MMU和MPU,名字就差一个字,但完全是两码事。

MMU(内存管理单元)

  • 支持虚拟地址到物理地址的转换
  • 支持页面映射、页面交换
  • 需要TLB(快表)配合
  • 典型应用:ARM Cortex-A系列、x86

MPU(内存保护单元)

  • 只做保护,不做地址转换
  • 定义内存区域的访问权限
  • 没有虚拟地址概念
  • 典型应用:ARM Cortex-R/M系列、RISC-V

我的经验:选型的时候一定要搞清楚。如果你用的是Cortex-A系列跑VxWorks,那必须用MMU。如果是Cortex-M系列跑VxWorks(比如VxWorks Micro),那就只能用MPU。我曾经见过一个项目,工程师在Cortex-M上硬要配MMU的驱动,折腾了两周才发现芯片根本不支持。

为什么会这样?因为MMU需要硬件支持页表遍历,这需要复杂的硬件逻辑。MPU就简单多了,它只是一组比较器,检查当前访问的地址落在哪个区域,然后决定是否放行。

说白了,MMU是「翻译+保安」,MPU只是「保安」。

1.3 内存管理单元基础概念:这些你必须懂

好,咱们来捋一捋内存管理单元的几个核心概念。这些概念你如果搞不清楚,后面写BSP代码会非常痛苦。

1. 页(Page)与帧(Frame)

页是虚拟内存的最小单位,帧是物理内存的最小单位。在VxWorks里,默认页大小通常是4KB。我建议你记住这个数字,因为很多配置都跟它有关。

2. 页表(Page Table)

页表就是一张映射表。虚拟地址查页表,找到对应的物理地址。VxWorks支持多级页表,ARM64下通常是3级或4级。

/* VxWorks中页表项的结构示例 */
typedef struct {
    UINT32   present    : 1;   /* 页面是否存在 */
    UINT32   writable   : 1;   /* 是否可写 */
    UINT32   user       : 1;   /* 用户态是否可访问 */
    UINT32   pwt        : 1;   /* 页级写透 */
    UINT32   pcd        : 1;   /* 页级缓存禁用 */
    UINT32   accessed   : 1;   /* 是否被访问过 */
    UINT32   dirty      : 1;   /* 是否被写过 */
    UINT32   pat        : 1;   /* 页属性表 */
    UINT32   global     : 1;   /* 全局页 */
    UINT32   ignored    : 3;   /* 忽略位 */
    UINT32   pfn        : 20;  /* 物理页帧号 */
} MMU_PTE;

3. TLB(快表)

TLB是页表的缓存。CPU每次访问内存都要查页表,如果每次都去内存里查,那性能就完蛋了。TLB就是用来加速这个过程的。

注意:TLB是有限资源。ARM Cortex-A72大概有几百个TLB条目。如果你跑的任务太多,TLB频繁失效,性能会急剧下降。我曾经在一个网络设备项目里遇到过这个问题,最后不得不调整页面映射策略,把常用的内存区域用大页映射。

4. 内存属性

每个内存区域都有属性:可读、可写、可执行、缓存策略等。VxWorks里用VM_STATE_*宏来定义这些属性。

/* VxWorks内存属性定义 */
#define VM_STATE_VALID     0x01  /* 有效 */
#define VM_STATE_WRITABLE  0x02  /* 可写 */
#define VM_STATE_CACHEABLE 0x04  /* 可缓存 */
#define VM_STATE_EXECUTABLE 0x08 /* 可执行 */
#define VM_STATE_USER      0x10  /* 用户态可访问 */

嗯,这里要注意。很多新手在配置MMU的时候,忘了设置缓存属性。结果DMA传输的数据一直不对,因为CPU读的是缓存里的旧数据。这种坑我踩过不止一次。

小结

今天咱们把VxWorks内存管理的骨架搭起来了。内存架构、MMU与MPU的区别、基础概念,这些都是后面实战的基石。你想想看,如果连页表是什么都不清楚,怎么去配置MMU?怎么去写BSP里的内存初始化代码?

下一章,我会带大家深入VxWorks的MMU初始化流程,手把手教你配置页表。到时候咱们直接上代码,不玩虚的。

一句话总结:内存管理就是「映射+保护」。搞懂了这两个词,你就抓住了VxWorks内存管理的命脉。

公众号:蓝海资料掘金营,微信deep3321