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