原创 uC/OS II中的内存管理模块

2010-7-6 10:33 1998 6 6 分类: MCU/ 嵌入式
内存管理模块主要由一个数据结构体和五个函数组成:
◇ 内存控制块数据结构OS_MEM
◇ 内存分区建立函数OSMemCreate()
◇ 内存块分配函数OSMemGet()
◇ 内存块释放函数OSMemPut()
◇ 内存分区状态查询函数OSMemQuery()
◇ 内存控制块链表初始化函数OSMemInit()
他们一同构成内存管理模块来对需要管理的内存块进行简单的管理――分配(动态分配)和释放(动态回收);
◇ 内存控制块数据结构OS_MEM
typedef struct {
void *OSMemAddr;
void *OSMemFreeList;
INT32U OSMemBlkSize;
INT32U OSMemNBlks;
INT32U OSMemNFree;
} OS_MEM;
系统中每个内存分区必须有一个属于自己的内存控制块,只有这样,内存管理模块中的五个函数才能对这个内存分区进行管理和操作;
操作系统首先在内存中声明了一个全局的内存控制块数组和指针:
static OS_MEM *OSMemFreeList;
static OS_MEM OSMemTbl[OS_MAX_MEM_PART];
然后在系统初始化的时候调用内存控制块链表初始化函数把这个全局的内存控制块数组OSMemTbl[ ]构建成一个单向链表,并把这个链表的头指针赋给OSMemFreeList,这样以后,每当用内存分区建立函数OSMemCreate()建立一个分区时,从这个链表中取出一个内存控制块来对这个内存分区进行管理;内存控制块数组的大小决定系统中内存分区的最大数目;
◇ 内存分区建立函数OSMemCreate()
要建立一个内存分区,必须具备三个条件:
1. 有供建立内存分区的内存空间:一般处理方法是以二维数组的形式来声明一个变量,这个变量在编译、链接的时候必定分配给一定的内存空间,这个内存空间只能通过内存块分配函数来获取使用;
2. 内存控制块数组中有闲置的内存控制块:一般的处理方法是先统计需要建立的内存分区的数目,然后依此来声明足够的内存控制块;
3. 声明一个内存控制块指针,便于以后对这个内存分区的访问;
内存分区建立后,这个二维数组就被构建成了一个单向链表,每个节点就是一个内存块,它由两部分组成:指向下一个内存块的指针(不能用来作为动态内存来使用)、能够作为动态内存来使用的存储空间;
◇ 内存块分配函数OSMemGet()
用户创建的任务要使用内存分区中的内存块,就必须通过调用内存分配函数来申请;一般的处理方法是:先在用户任务中声明一个void类型的指针和一个INTU8 型变量,然后调用OSMemGet()来从指定的内存分区中申请一个内存块,并把内存块的指针赋给预先声明的void类型指针;接着对INTU8 型变量进行判断,看申请内存块是否成功。如果成功,就可以使用这个内存块中可以用动态使用的存储空间。
◇ 内存块释放函数OSMemPut()
用户创建的任务不在使用申请来的内存块的时候,必须及时的调用OSMemPut()来把内存块释放到相应的内存分区中去。需要注意的是,这个内存块从那个内存分区中申请来的就必须释放到那个内存分区中去,否则会造成系统崩溃;这个用户在编写任务的时候注意就可以避免了;
OSMemGet()和OSMemPut()应该成对使用;
◇ 内存分区状态查询函数OSMemQuery()
调用内存分区状态查询函数可以获取一个内存分区的相关信息,它的实现方法就是把内存分区控制块中的信息拷贝到OS_MEM_DATA的数据结构体中供调用查看、使用;那么大家也许会问为什么我们不直接通过访问分区控制块来获取这个分区的使用信息呢?这就涉及到代码的临界区的问题。试想如果我们直接访问内存控制块中的域的话而没有关中断的话,那么如果时钟节拍到来的时候我们的访问肯定被打断(我们只获取了内存分区的部分信息,还有部分信息没来得及获取),这个时候通过调度准备就绪而且优先级高的任务得以运行,那么前面的任务就得等待;试想现在运行的任务如果对这个内存分区进行操作的话,那么上一个任务获取的内存分区的信息就不一致了,这个可能给我们不可预测的后果;为此在访问内存分区控制块前,我们得关中断,之后我们还得开中断,这个给我们的移值带来琐碎的麻烦!这也就是编写这个函数的原因!
下面就内存管理模块来举个例子:
#define BlockNum 100
#define BlockSize 32
OS_MEM *CommRAMA_Ptr;
OS_MEM *CommRAMB_Ptr;
INT8U CommRAMA[BlockNum][BlockSize];
INT8U CommRAMB[BlockNum][BlockSize*4];
… … … …
void main(void)

INT8U error;
… … … …
/*用内存分区建立函数OSMemCreate()把这两块RAM存储空间构建成内存 */
/*区,并把管理这两块内存分区的内存分区控制块的地址赋给两个OS_MEM指针*/
CommRAMA_Ptr=OSMemCreate(CommRAMA,BlockNum, BlockSize,error);
CommRAMB_Ptr=OSMemCreate(CommRAMB,BlockNum, BlockSize*2,error);
… … … …
OSStart();

void MyTask(void)

… … … …
INT8U Error;
OS_MEM_DATA MemInfo;
INT8U BlkA_Ptr,BlkB_Ptr;
… … … …
for(){
Error= OSMemQuery(CommRAMA_Ptr,&MemInfo); ------(1)
If(MemInfo.OSNFree≥2){ ------(2)
BlkA_Ptr=(INT8U *)OSMemGet(CommRAMA_Ptr,&Error); ------(3)
BlkB_Ptr=(INT8U *)OSMemGet(CommRAMA_Ptr,&Error); ------(4)
/*使用获得的内存块*/
… … … …
/*释放获得的内存块*/
OSMemPut(CommRAMA_Ptr,BlkA_Ptr);
OSMemPut(CommRAMA_Ptr,BlkB_Ptr);
}else{
Error= OSMemQuery(CommRAMB_Ptr,&MemInfo);
If(MemInfo.OSNFree≥1){
BlkA_Ptr=(INT8U *)OSMemGet(CommRAMB_Ptr,&Error);---(5)
/*使用获得的内存块*/
… … … …
/*释放获得的内存块*/
OSMemPut(CommRAMA_Ptr,BlkB_Ptr);
}else{
OSTimeDly(1); /*等待一个时钟节拍*/


… … … …


系统建立了两个内存分区:内存分区CommRAMA的内存块的大小为32个INT8U(可用的为31个INT8U),内存分区CommRAMB的内存块的大小为128个INT8U(可用的为127个INT8U);现在用户创建了一个名为MyTask的任务,它运行时需要一个大小为60个INT8U的内存空间,在这个系统有两个解决办法:从内存分区CommRAMA中申请两个内存块以及从内存分区CommRAMB中申请一个内存块;具体的实现方法是先获取内存分区CommRAMA的使用信息,看有没有足够的内存块(剩余块数不小于2)如果够的话,就不用从内存分区CommRAMB中申请了,否则就的从CommRAMB中申请,如果这里也没有的话,那么就等待一个时钟节拍!
使用内存管理模块需要做的工作还有:
1.打开配置文件OS_CFG.H,将开关量OS_MEM_EN设置为1:
#define OS_MEM_EN 0
2.打开配置文件OS_CFG.H,设置系统要建立的任务分区的数量:
#define OS_MAX_MEM_PART 2

文章评论0条评论)

登录后参与讨论
我要评论
0
6
关闭 站长推荐上一条 /2 下一条