返回内存管理模块
HRTOS / MEMORY MANAGEMENT

内存碎片与连续块分配

在资源受限的 8051 平台上,分配器不仅要判断总空间是否充足, 还需要找到满足任务栈深度要求的连续空闲区域。 理解位图、分配粒度与任务生命周期之间的关系,是评估内存可用性的关键。

Bitmap 位图 连续块搜索 任务栈分配 8051 内核
分配粒度
2 Bytes
按当前页面所述的分配模型
分配依据
连续空闲位
满足请求所需的连续块数量
重点检查
最大连续块
总空闲量不等于最大可分配量
01

分配模型概述

位图描述分配单元状态,连续扫描用于定位候选空间。

根据当前提供的实现说明,HRTOS 使用 OS_MEMORY 作为内存映射位图,并以连续空闲块搜索的方式为任务栈寻找空间。 每一位对应一个最小分配单元,位图状态反映该单元是否已经占用。

当前描述的分配模型
分配粒度:2 bytes
映射区域:OS_USER_RAM_INIT → OS_USER_RAM_EXIT

OS_MEMORY[x] bit = 1  // 已占用
OS_MEMORY[x] bit = 0  // 空闲
已占用单元 空闲单元
关键区别: 位图可以直接记录每个分配单元的状态,但能否成功分配仍取决于搜索规则、 请求的连续块数量以及当前空闲区域的分布。
02

连续块搜索与任务创建

关注连续空闲单元,而不是只累计全部空闲空间。

按你提供的 os_task_create 逻辑描述,分配器扫描位图:遇到空闲位就累计连续长度; 遇到已占用位则清零。连续长度达到任务所需块数后,便可以确定候选区域。

连续空闲块搜索 · 示意伪代码
k = 0;

for (i = 0; i < total_blocks; i++) {
    if (bitmap[i] == 0) {
        k++;
    } else {
        k = 0;
    }

    if (k >= required_blocks) {
        /* 已找到满足连续长度的候选区域 */
        /* 实际分配、标记与循环退出行为以源码为准 */
    }
}
STEP 01 读取位图

检查当前分配单元的状态。

STEP 02 累计连续长度

空闲则递增,占用则重置计数。

STEP 03 比较请求大小

检查连续块数量是否达到要求。

STEP 04 处理分配结果

按实际实现记录地址并更新位图。

变量语义: k 表示当前连续空闲单元计数; required_blocks 对应所需连续块数量。 你给出的 sd 被描述为栈深度加一后的需求值, 但最终块数换算和边界条件仍应以真实分配代码为准。
03

栈空间映射与增长模型

分配位置由起始地址、索引与分配单元粒度共同决定。

当前提供的地址计算表达式
i = OS_USER_RAM_INIT + (index * 2) + 1;
SP = i;

OS_INSIDE_STACK_USE_MAXIMUM = i + sd * 2;

这组表达式体现了按两字节粒度计算位置的思路。 index 决定分配单元在映射区中的偏移, SP 由计算出的地址设置, 而 OS_INSIDE_STACK_USE_MAXIMUM 则根据栈需求计算另一个边界值。

地址含义需要与实现核对: 仅凭这三行表达式,无法确定变量记录的是栈顶、栈底还是边界哨兵地址, 也无法确认实际分配区间的首尾是否包含在内。 因此这里保留表达式的原始语义,不直接推导未经确认的可用栈字节数。

为什么任务生命周期可能影响空间分布?

如果任务栈从一个连续区域中分配,任务创建与删除会改变位图中的占用分布。 当不同生命周期的任务交错创建和删除时,可能形成多个分离的空闲区间。 是否能再次利用这些区间,取决于删除时是否清除相应位图标记,以及后续搜索能否找到足够长的连续区域。

04

连续空间碎片如何形成

空闲空间可能仍然存在,但无法满足当前连续分配请求。

连续空闲
存在一段较长的连续空闲区,较大的连续分配请求更容易满足。
空闲区分散
即使空闲单元总数不少,最长连续空闲区也可能很短。

需要区分的三个指标

指标 含义 回答的问题
总空闲空间 所有空闲分配单元的总量 系统一共还剩多少空间?
最长连续空闲区 最大的一段连续空闲单元 单次最多能申请多大的连续区域?
当前请求大小 此次任务创建需要的连续空间 当前是否存在足够大的空闲区?
判断原则: 当总空闲空间大于请求所需空间,但最长连续空闲区仍小于请求大小时, 连续块分配可能失败。这是分析外部碎片时最有用的判据之一。
05

碎片来源与实现边界

需要结合分配、删除与复用路径判断,而不是仅根据位图推断。

可能因素 对空间分布的影响 需要检查的实现
不同大小的栈请求 空闲区间长度可能不匹配后续请求。 栈深度换算与连续块搜索条件。
任务创建与删除交错 可能在仍有占用区域时形成分散空闲区。 删除路径是否释放对应位图单元。
任务生命周期不同 部分区域可能长期占用,其他区域反复申请和释放。 任务复用、删除和再次创建的完整流程。
高速任务与普通任务共用映射区 若确实共用同一分配区域,可能影响连续区间分布。 两类任务是否共享同一分配器与位图。
不要预先认定无法恢复: 如果任务删除时正确释放了位图,原空闲区域就可能被后续分配复用。 若系统没有内存压缩或搬移机制,分散的空闲区通常不会自动合并成一个更大的连续区; 但这不代表只能重启系统。最终行为取决于 HRTOS 的实际释放和重分配逻辑。
06

面向 8051 的内存规划策略

优先让资源需求可预估,再评估运行期间动态分配的必要性。

01 / CAPACITY 控制任务数量

依据任务上限与系统可用内存核算总需求。

02 / STACK 规划栈深度

为每个任务选择合理的栈需求,避免无依据地扩大分配。

03 / LIFETIME 集中创建长期任务

对生命周期固定的任务,可优先考虑启动阶段创建。

04 / VERIFY 检查分配与释放

验证位图标记、释放范围和再次分配的行为。

建议重点验证的项目

  • 申请失败时,位图和栈地址是否保持一致。
  • 任务删除后,对应分配单元是否被正确释放。
  • 连续空闲块的搜索起点、终止条件和边界是否正确。
  • 不同栈深度请求能否复用已释放区域。
  • 重复创建与删除任务后,内存映射是否出现残留占用。
工程目标: 不只是让任务创建成功,还要让分配边界、失败处理与释放行为都可以检查和验证。 对资源受限的实时系统而言,可预测的内存行为与单纯追求更高的利用率同样重要。
07

关键结论

从位图状态进一步理解连续分配的可用性。

结论 准确含义
位图记录分配状态 通过各分配单元的标记,描述当前占用与空闲情况。
连续搜索影响分配结果 请求是否成功不仅取决于总空闲量,还取决于连续区间长度。
碎片不等于空间全部丢失 分散空闲区域仍可能被较小的后续请求复用。
释放机制决定可复用性 需要检查任务删除时的位图更新和后续分配行为。
静态规划降低运行期不确定性 合理的任务数量、栈深度与生命周期规划,有助于减少意外分配失败。