2. 分区内存管理(Partition):分区概念与创建、静态分区 vs 动态分区、分区内存分配与释放API

2.1 分区概念:为什么需要它?

好,咱们直接切入正题。分区内存管理,说白了就是给特定任务或模块划一块“自留地”。

你想想看,在VxWorks这种实时系统里,最怕什么?最怕内存碎片。全局堆(heap)用久了,东一块西一块,分配大块内存时明明总量够,就是分配不出来。我早年在一个雷达信号处理项目里就吃过这个亏——系统跑了三天,突然一个关键任务malloc失败,整个系统直接挂掉。排查了整整两天,最后发现是内存碎片导致的。

分区(Partition)就是用来解决这个问题的。它从系统堆里预先划出一块连续区域,然后在这块区域内部做固定大小或可变大小的内存管理。说白了,就是“我的地盘我做主”。

核心思想:分区内的内存分配和释放,不会影响其他分区,也不会产生跨分区的碎片问题。

2.2 分区创建:从零开始搭一个

创建分区,VxWorks提供了两个核心API:partCreatepartLibInit。我个人习惯用 partCreate,因为它更直观。

先看一个最简单的例子:

/* 创建一个固定大小块的分区 */
PART_ID myPartition;
STATUS status;

/* 参数:分区名,内存池起始地址,内存池大小,块大小,对齐方式 */
myPartition = partCreate("MyPartition", NULL, 4096, 256, 4);

if (myPartition == NULL) {
    printf("分区创建失败!\n");
    return ERROR;
}

这里要注意几个点:

  • 内存池起始地址传NULL:表示让系统自动从堆里分配。你也可以传一个预先分配好的内存块地址。
  • 块大小256字节:意味着这个分区里每次分配都是256字节的整数倍。如果你要分配50字节,实际会给你256字节——有点浪费,但速度快。
  • 对齐方式4字节:对于ARM或x86处理器,4字节对齐基本够用。如果是DSP或者特殊硬件,可能需要8字节或16字节对齐。

我的经验:块大小最好选2的幂次方,比如32、64、128、256。这样内部管理效率最高。我曾经在一个项目中把块大小设为100字节,结果性能反而下降了——因为内部对齐和索引计算变复杂了。

2.3 静态分区 vs 动态分区:选哪个?

这个问题,我经常被问到。咱们直接上对比表:

特性 静态分区 动态分区
内存池大小 编译时固定 运行时可变(可扩展)
分配速度 极快(O(1)) 较快(O(n))
碎片问题 无碎片 可能有内部碎片
适用场景 固定大小数据包、任务栈 变长消息、动态数据结构
API示例 partCreate + 固定块大小 partCreate + 可变块大小(需额外配置)

嗯,这里要展开说一下。静态分区,说白了就是“一刀切”。所有分配请求都返回同样大小的内存块。好处是快,坏处是浪费——如果你要分配10字节,它还是给你256字节。

动态分区呢?它允许不同大小的分配请求。但代价是内部管理更复杂,而且可能出现“外部碎片”——虽然比全局堆好很多。

我个人建议:如果你的数据包大小是固定的(比如网络MTU 1500字节),用静态分区。如果数据大小变化很大(比如JSON解析),用动态分区。别混用,否则调试起来很痛苦。

避坑指南:我曾经在一个项目中,为了省内存,把静态分区的块大小设得刚好够用。结果后来需求变更,每个数据包多了8字节的头信息,整个分区全部溢出。从那以后,我习惯在块大小上留20%的余量。

2.4 分区内存分配与释放API:实战操作

好,到了最核心的部分。分区内存的分配和释放,主要用这三个API:

  • partAlloc:从分区分配一块内存
  • partFree:释放一块内存回分区
  • partRealloc:重新调整内存块大小(仅动态分区支持)

看个完整例子:

/* 假设我们已经创建了一个静态分区 myPartition,块大小256字节 */
void *pBuffer;

/* 分配内存 */
pBuffer = partAlloc(myPartition);

if (pBuffer == NULL) {
    printf("分区内存分配失败!可能分区已满。\n");
    return ERROR;
}

/* 使用内存... */
memset(pBuffer, 0, 256);
strcpy((char *)pBuffer, "Hello from Partition!");

/* 释放内存 */
status = partFree(myPartition, pBuffer);
if (status != OK) {
    printf("释放失败!可能指针不属于这个分区。\n");
}

这里有几个关键点,我得强调一下:

  1. 分配不需要指定大小:对于静态分区,partAlloc 自动返回块大小的内存。你不需要传size参数。
  2. 释放必须指定分区ID:这和 free() 不同。你必须告诉系统“这个内存块属于哪个分区”。
  3. 不要跨分区释放:从分区A分配的内存,不能释放到分区B。否则系统会崩溃——我亲眼见过。

小技巧:调试时可以用 partShow 函数查看分区状态。它会显示已用块数、空闲块数、碎片情况等。我每次做内存调优,第一件事就是跑 partShow 看看分区健康度。

2.5 性能调优:让分区飞起来

最后,分享几个实战调优经验:

  • 分区大小选2的幂次方:比如4KB、8KB、16KB。内部管理算法对2的幂次方有优化。
  • 块大小对齐到Cache Line:对于ARM Cortex-A系列,Cache Line通常是64字节。块大小设为64的倍数,能避免伪共享问题。
  • 避免频繁创建/删除分区:分区创建本身有开销。我建议在系统初始化阶段就把所有分区建好,运行时只做分配和释放。
  • 监控分区使用率:用 partShow 或自定义钩子函数,定期检查分区是否快满了。如果使用率超过80%,就该考虑扩容了。

嗯,分区内存管理就讲到这里。下一章咱们聊聊VxWorks的缓存一致性(Cache Coherency)问题——这可是多核系统里的老大难。到时候我会分享一个我在某通信设备项目里踩过的坑,保证让你印象深刻。