嵌入式系统的内存管理系统

嵌入式系统通常避免使用malloc/free以减少代码空间开销和不确定性。静态内存分配虽然避免动态内存问题,但降低了效率且易引发新的问题。为解决任务间通信,引入简化版动态内存分配——缓存管理系统,实现固定大小内存块的高效分配,同时介绍带内存泄露检查的实现方法。
摘要由CSDN通过智能技术生成

1. 嵌入式系统的内存分配

嵌入式程序开发中,与PC程序开发不同,很少使用完全动态内存分配(malloc()/free()),主要基于以下一些原因:

  • 为了支持动态内存分配必须实现一套完善的内存管理系统,包括空闲/占用内存的管理、内存分配策略、防止内存碎片化对策等等。这会使得系统过于庞大,占用过多的代码空间。在一些存储资源有限的嵌入式系统中,过大的代码空间开销是不可接受的。
  • malloc()/free()函数往往是非线程安全的,在使用之前,必须确认。如果不是线程安全的,则在调用侧必须进行保护。
  • 由于需要搜索合适大小的空闲内存块,因此每次malloc()的执行时间是不确定的。同样,在释放的时候,如果同时进行内存碎片合并回收,则free()的执行时间也是不确定的。因此在需要确保执行时间确定性的系统中,不能使用。
  • 嵌入式系统由于功能单一(相对于个人计算系统而言),对任意大小的动态内存分配的需求不强烈。
  • 跟PC系统中一样,动态内存分配还是很多问题的根源(内存泄露、内存越界、重复释放等),而且这样的问题不易调试和定位。

因此在嵌入式系统中,往往采用静态内存分配,也就是所有的变量,无论是全局变量,还是局部变量,都是在编译时指定大小。这虽然避免了上述动态内存分配的问题,但也有其缺陷,在使用中如果不注意,也会带来新的问题。

  • 降低了内存的使用效率。但这是避免动态内存分配的必要代价。
  • 多个任务访问同一个全局变量时,需要施加访问控制。根据所采用的操作系统的不同,访问控制可以通过互斥锁,也可以通过中断禁止、调度禁止等方法实现。因此,尽量避免让多个任务(包括中断服务程序)访问同一个全局变量,除非有必须这么做的理由。
  • 局部变量使用静态内存分配时,需要经常关注任务堆栈的大小,防止溢出,特别是局部变量所占空间较大时。因此,如果局部变量所占的空间较大,有时可以考虑采用全局变量来代替,以免任务栈过大。

如果只使用静态内存分配,在任务间、或者任务与中断服务程序之间进行数据交互时,就不太方便。此时就需要为不同的数据交互的任务组合分配不同的专用内存。如是一个由三个任务构成的系统,如果各个任务之间都有互相数据交互,则需要准备6个静态数据交互内存,而且其数量随着任务数的增加而呈指数增加。而且这种分配方式也很不灵活,可能为交互较少的任务分配了较多的内存,或者为交互较多的任务分配的内存不足等等。

图 1‑1 静态数据交互内存分配

嵌入式操作系统提供了消息(message)或者队列(queue)的手段来解决这个问题。在操作系统层面,为消息或者队列中的每个项都分配了固定大小的内存块。该内存块可以保存需要传输的数据,也可以保存需要交互数据的地址。如果采用消息或者队列的数据项进行直接数据传输时,在发送侧和接收侧都需要执行一次数据拷贝,影响执行效率;而且由于需要传输的数据大小一般依赖于实际应用,因此不可能在操作系统中规定一个数据项的内存大小。因此在实际应用中,消息的发送端将需要传输的数据放在缓存中,并将该内存地址放入消息和队列的数据项中进行发送;消息的接收端则从收到的数据项中取出对应的地址,从而取得所接收的数据。如图 1‑2所示。

这样的缓存系统可以看作是一个简化版的动态内存分配系统。它只支持固定大小的内存块的分配,从而大大简化了动态内存管理系统的负担。而在嵌入式系统中,固定大小的内存块可以完全满足需求。本章主要介绍一种固定大小内存分配的实现机制。

 1‑2 通过缓存区进行数据传递

2. 缓存管理系统的实现

1节所要求的缓存系统必须满足几个条件:

  • 位于全局变量区,各个任务都可以访问。
  • 需要支持申请和释放过程。
  • 为了缓存管理上的便利,每个缓存块按固定大小分配,或者按几个可以选择的大小分配。
  • 保证申请和释放的时间是确定的。

为此建立的一个缓存系统如下所示(为了简洁起见,省略了一部分注释):

mm.h

#ifndef __MM_H_

#define __MM_H_

#include <stdint.h>

void vInitMM(void);

uint8_t *pucAllocateBuffer(void);

void vFreeBuffer(uint8_t *pucBuf);

#endif /*__MM_H_*/

头文件中声明了三个函数,分别用于初始化缓存系统,分配缓存和释放缓存。除了初始化函数,其他两个与动态内存分配的malloc/free非常类似,使用方法上也没有什么区别。初始化函数是新增加的,必须在整个系统初始时调用。其实动态内存管理系统也有类似的初始化过程,不过是作为操作系统初始化的一部分而执行。

具体的实现代码如下所示:

mm.c

#include "mm.h"

//The size of each buffer block.

#define BLOCK_SIZE               (256)

//The number of buffer blocks.

#define BLOCK_NUM                (20)

//The head for each buffer block, it's only used by MM system.

typedef struct {

  uint8_t  ucIsUsed;  /* Indicate if the buffer is allocated (TRUE), or not (FALSE)*/

   /*

    * Index of the next empty buffer if the buffer is linked to the empty buffer pool. This

    * index is the one into pBlocks, If a buffer block is allocated, this value is meaningless.

    * For the last one of empty buffer link it is set to BLOCK_NUM.

    */

  uint8_t  ucNextIdx;

} mm_head_t;

//Strcture of a buffer block.

typedef struct {

  mm_head_t    hea

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值