西安电子科技大学嵌入式实时操作系统课件 第9章_第1页
西安电子科技大学嵌入式实时操作系统课件 第9章_第2页
西安电子科技大学嵌入式实时操作系统课件 第9章_第3页
西安电子科技大学嵌入式实时操作系统课件 第9章_第4页
西安电子科技大学嵌入式实时操作系统课件 第9章_第5页
已阅读5页,还剩55页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第9章内存管理9.1概述

9.2建立内存分区——OSMemCreate()9.3分配内存块——OSMemGet()

9.4释放内存块——OSMemPut()

9.5查询内存分区的状态——OSMemQuery()习题

9.1概述

9.1.1基本原理

在内存管理方面,ANSIC本身就提供了malloc()和free()两个函数用于动态地分配内存和释放内存。但是,µC/OS-Ⅱ为什么不直接利用这两个函数,而要另外构建内存管理方法呢,其主要原因在于以下两个方面:

(1)当应用程序反复调用malloc()和free()函数进行内存的分配与释放时,可能会将原来一块很大且连续的内存区域逐渐分割成许多细小而彼此不相邻的内存区域,产生通常说的内存碎片。当内存碎片大量存在时,最后应用程序可能连一块很小的内存也无法分配到。(2)由于内存管理算法的原因,malloc()和free()函数的执行时间是不确定的,因此不适合作实时操作系统函数应用。

µC/OS-Ⅱ操作系统的内存管理方法是在解决了malloc()和free()两函数缺陷的基础上构建起来的。其原理如图9.1所示,将内存分区分块,也就是把连续的每个大块内存分区,图9.1内存分区每个分区又分成整数个大小相同的内存块。µC/OS-Ⅱ利用这种新机制,对malloc()和free()函数进行了改进,并构建了新的内存管理函数,使得它们可以分配和释放固定大小的内存块。这样一来,malloc()和free()两函数的执行时间不确定的问题就首先得到了解决。接下来就是要解决内存碎片的问题。如图9.1所示,在有多个分区分块的内存系统中,分配内存时,应用程序可以从不同的内存分区中得到大小不同的内存块。当需要释放时,特定的内存块再重新放回它以前所属的内存分区。通过这样的内存管理算法,内存碎片问题就得到了解决。9.1.2内存管理函数

如表9.1所示,µC/OS-Ⅱ提供了四种内存管理函数,函数所属文件是OS_MEM.C。表9.1内存管理函数一览表9.1.3内存管理函数的配置常量

在使用内存管理函数之前,必须将OS_CFG .H文件中相应的配置常量设置为0或1,以确定是编译还是裁剪该函数,其配置常量如表9.2所示。表9.2内存管理函数的配置常量一览表9.1.4内存控制块

内存控制块(MemoryControlBlocks,MCB)是用于实现内存管理、跟踪每一个内存分区的数据结构,如程序清单9.1所示,每个内存分区都有它自己的内存控制块。 程序清单9.1内存控制块的数据结构

typedefstruct{

void*OSMemAddr; /*指向内存分区起始地址的指针。它在建立内存分区时被初始化,

此后不能更改(见9.2节) */

void*OSMemFreeList;/*指向下一个空闲内存控制块或下一个空闲内存块的指针,具体含义

要根据该内存分区是否已经建立来决定(见9.2节) */INT32UOSMemBlkSize;/*内存分区中内存块的大小,是用户在建立该内存分区时指定的*/

INT32UOSMemNBlks; /*内存分区中总的内存块数量,是用户在建立该内存分区时指定的*/

INT32UOSMemNFree; /*内存分区中当前可以使用的空闲内存块数量 */

}OS_MEM;如果要使用µC/OS-Ⅱ中的内存管理,首先需要将OS_CFG .H文件中的开关量OS_MEM_EN设置为1;然后还要设置OS_MAX_MEM_PART常量,其值至少是2,它决定了系统中的最大分区数。这样,在启动时µC/OS-Ⅱ就会通过OSInit()调用OSMemInit()来实现对内存管理器的初始化。该初始化主要建立一个如图9.2所示的空闲内存控制块链表,其中OSMemFreeList指针的作用是将空闲内存控制块链接成空闲内存控制块链表。图9.2空闲内存控制块链表9.2建立内存分区——OSMemCreate()

9.2.1函数原型

函数原型如下:

OS_MEM*OSMemCreate(void*addr,INT32Unblks,INT32Ublksize,INT8U*err)

OSMemCreate()函数用于建立并初始化一块内存区。要使用内存管理函数,必先调用OSMemCreate()函数建立内存分区。一个内存区包含确定数量和大小的内存块,应用程序可以分配这些内存块,并在用完后释放回内存区。

OSMemCreate()函数有如下四个参数:(1) addr:建立的内存区的起始地址。内存区可以使用静态数组或在初始化时使用malloc()函数建立。

(2) nblks:内存块的数量。每一个内存区最少需要定义两个内存块。

(3) blksize:每个内存块的大小,最少应该能够容纳一个指针。

(4) err:指向错误代码的变量的指针。OSMemCreate()函数返回的错误码可能为下述几种之一:

① OS_NO_ERR:内存分区建立成功。

② OS_MEM_INVALID_ADD:地址指针为空,非法。③ OS_MEM_INVALID_PART:没有空闲的内存区。

④ OS_MEM_INVALID_BLKS:没有为每一个内存区建立至少两个内存块。

⑤ OS_MEM_INVALID_SIZE:内存块太小,不能容纳一个指针变量。

OSMemCreate()函数返回指向内存控制块的指针。如果没有空闲内存区,则OSMemCreate()函数返回空指针。

函数的调用者是任务或者启动代码,开关量是OS_MEM_EN。9.2.2源代码

OSMemCreate()函数的源代码如程序清单9.2所示。该函数的主要工作过程如下:

(1)条件检查,确保各种前提条件的满足。

(2)从空闲内存控制块链表中取得一个内存控制块。

(3)若该空闲内存控制块可用,则将该内存分区内的所有内存块用指针链接成一个单向链表。因为在这个链表中,插入和删除元素都是从顶端开始的,所以无需使用双向链表。

(4)在内存分区的控制块中填写与该内存分区有关的内容。

(5)最后返回该内存控制块指针,以后的操作都通过该指针来实现。 程序清单9.2OSMemCreate()函数的源代码

OS_MEM*OSMemCreate(void*addr,INT32Unblks,INT32Ublksize,INT8U*err)

{

#ifOS_CRITICAL_METHOD= =3

OS_CPU_SRcpu_sr;

#endif

OS_MEM *pmem;

INT8U *pblk;

void **plink;INT32U i;

#ifOS_ARG_CHK_EN>0

if(addr = =(void*)0){ /*确保定义的内存分区起始地址是有效的 */

*err =OS_MEM_INVALID_ADDR;

return((OS_MEM*)0);

}

if(nblks<2){ /*确保每个内存分区至少有两个内存块 */*err=OS_MEM_INVALID_BLKS;

return((OS_MEM*)0);

}

if(blksize<sizeof(void*)){ /*确保每个内存块至少能容纳下一条指针,因为空闲内存控制块是由指针链接在一起的 */

*err=OS_MEM_INVALID_SIZE;

return((OS_MEM*)0);

}#endif

OS_ENTER_CRITICAL();

pmem=OSMemFreeList; /*从空闲内存控制块链表中取得一个空闲内存控制块 */

if(OSMemFreeList !=(OS_MEM*)0){ /*确保取得的空闲内存控制块是可用的 */

OSMemFreeList =(OS_MEM*)OSMemFreeList->OSMemFreeList; /*调整指针 */

}

OS_EXIT_CRITICAL();if(pmem==(OS_MEM*)0){ /*检查内存控制块是否可用 */

*err=OS_MEM_INVALID_PART;

return((OS_MEM*)0);

}

/*满足上述条件后,将所要建立的内存分区内的所有内存块链接成一个单向链表 */

plink=(void**)addr; /*建立单向链表 */

pblk=(INT8U*)addr+blksize;

for(i=0;i<(nblks-1);i++){*plink =(void*)pblk;

plink =*plink;

pblk =pblk+blksize;

}

/*在该内存分区控制块中填写与内存分区有关的信息 */

*plink =(void*)0;/*最后一条指针指向NULL */

/*在该内存分区控制块中填写与内存分区有关的信息 */pmem->OSMemAddr =addr; /*存储内存分区的起始地址 */

pmem->OSMemFreeList =addr; /*初始化空闲内存块指针 */

pmem->OSMemNFree =nblks; /*存储内存块的数量 */

pmem->OSMemNBlks =nblks;

pmem->OSMemBlkSize =blksize; /*存储每个内存块的尺寸 */

*err =OS_NO_ERR;

return(pmem);/*返回内存控制块指针,以后对该内存分区的操作都通过这个指针来实现*/

}

OSMemCretae()函数运行完毕后,内存控制块所指向的内存分区与分区中的内存块之间的关系如图9.3所示。图9.3OSMemCretae()函数建立的内存分区程序一旦运行,经过多次分配与释放内存块后,同一分区内的内存块的链接顺序会有很大变化,但这并不影响使用,也不增加时间开销。

9.2.3范例

建立一个含有50个内存块、每个内存块为16B的内存分区,具体代码如下:OS_MEM*MemBuf; /*定义一个内存控制块指针 */

INT8Ubuffer[50][16]; /*定义一个内存分区数组 */

voidmain(void){

INT8Uerr;

OSInit();

.

MemBuf=OSMemCreate(buffer,50,16,&err);

.

OSStart();

} 9.3分配内存块——OSMemGet()

9.3.1函数原型

函数原型如下:

void*OSMemGet(OS_MEM*pmem,INT8U*err)

OSMemGet()函数用于从已经建立的内存分区中申请一个内存块。该函数的调用者可以是任务或者中断,开关量是OS_MEM_EN。该函数有如下两个参数:(1) pmem:指向内存控制块的指针。其值可以在建立内存分区时得到。

(2) err:指向包含错误码的变量的指针。返回的错误码可能为下述几种之一:

① OS_NO_ERR:成功得到一个内存块。

② OS_MEM_NO_FREE_BLKS:内存区中已经没有空闲内存块。

③ OS_MEM_INVALID_PMEM:pmem是空指针。

函数返回指向内存区块的指针,如果没有空间分配给内存块,函数返回空指针。9.3.2注意事项

调用OSMemGet()函数时应注意如下事项:

(1)调用该函数申请内存块时,用户必须知道所建立的内存块的大小,使用时不能超过容量。例如,如果一个内存分区内的每个内存块为64B,那么应用程序最多只能使用该内存块中的64B。

(2)用户程序必须在使用完内存块后及时释放,并重新放回它原先属于的分区中去。

(3)函数可以多次调用。

(4)如果暂时没有内存块可用,函数不会等待,而是立即返回NULL指针,所以可在中断中调用。9.3.3源代码

OSMemGet()函数的源代码如程序清单9.3所示。该函数的主要工作过程如下:

(1)确保运行条件的满足。

(2)检查分区是否有空闲的内存块,若有,则取得它,并作如下操作:因为已经从空闲内存块中取走了,所以要将它从空闲内存块链表中删除,并调整空闲内存块的指针,空闲内存块的数量也要相应减1;若没有空闲的内存块,则返回错误代码。 程序清单9.3OSMemGet()函数的源代码

void*OSMemGet(OS_MEM*pmem,INT8U*err)

{

#ifOS_CRITICAL_METHOD= =3

OS_CPU_SRcpu_sr;

#endif

void*pblk;

#ifOS_ARG_CHK_EN>0

if(pmem= =(OS_MEM*)0){ /*确保指针指向的内存控制块是有效的 */*err=OS_MEM_INVALID_PMEM;

return((OS_MEM*)0);

}

#endif

OS_ENTER_CRITICAL();

if(pmem->OSMemNFree>0){ /*检查分区中是否有空闲的内存块 */

pblk=pmem->OSMemFreeList; /*如果有,则将第一个内存块从空闲内存块链表中删除,因为此时要使用这个内存控制块*/pmem->OSMemFreeList=*(void**)pblk; /*调整空闲内存块链表指针 */

pmem->OSMemNFree--; /*空闲内存块数量减1 */

OS_EXIT_CRITICAL();

*err=OS_NO_ERR; /*申请成功 */

return(pblk); /*将分配到的内存块指针返回给应用程序 */}

OS_EXIT_CRITICAL();

*err=OS_MEM_NO_FREE_BLKS; /*如果没有空闲内存块,返回错误代码 */

return((void*)0); /*返回空指针 */

}

9.3.4范例

OSMemGet()函数的使用范例如下:OS_MEM*MemBuf; /*定义一个内存控制块指针 */

voidTask(void*pdata){

INT8U*msg;pdata=pdata;

for(;;){

msg=OSMemGet(MemBuf,&err);

if(msg!=(INT8U*)0){

./*内存块已经分配 */

}

}

} 9.4释放内存块——OSMemPut()

9.4.1函数原型

函数原型如下:

INT8UOSMemPut(OS_MEM*pmem,void*pblk)

OSMemPut()函数用于释放一个内存块。开关量是OS_MEM_EN,调用者是任务或者中断。该函数有如下两个参数:

(1) pmem:指向内存控制块的指针。其值可以在调用OSMemCreate()函数建立内存分区的时候得到。

(2) pblk:指向将要被释放的内存块的指针。9.4.2返回值

OSMemPut()函数的返回值为下述内容之一:

(1) OS_NO_ERR:内存块成功释放。

(2) OS_MEM_FULL:内存分区已满,不能再接收释放的内存块。这种情况说明用户程序出现了错误,释放的内存块多于用OSMemGet()函数得到的内存块。

(3) OS_MEM_INVALID_PMEM:pmem是空指针。

(4) OS_MEM_INVALID_PBLK:pblk是空指针。9.4.3注意事项

调用OSMemPut()函数时应注意如下事项:

(1)如果一个内存块已经不再使用,必须及时释放它,以备其它应用程序使用。

(2)释放内存块时,必须放回到原先申请的内存分区中,不能错放,否则可能导致系统崩溃。例如,从每个内存块是32B的内存分区中申请了一个内存块,用完后就不能把它返回给每个内存块是64B的内存分区。因为,应用程序以后申请64B分区中的内存块时,可能会只得到32B的可用空间,而得不到64B的内存块。9.4.4源代码

OSMemPut()函数的源代码如程序清单9.4所示。该函数的主要工作过程如下:首先检查内存分区是否已满,如果已满,则说明系统在分配和释放内存时出现了错误;如果未满,则将所要释放的内存块插入到该分区的空闲内存块链表中。最后,将分区中空闲内存块总数加1。 程序清单9.4OSMemPut()函数的源代码

INT8UOSMemPut(OS_MEM*pmem,void*pblk)

{

#ifOS_CRITICAL_METHOD= =3

OS_CPU_SRcpu_sr;

#endif

#ifOS_ARG_CHK_EN>0

if(pmem= =(OS_MEM*)0){ /*确保指针指向的内存控制块是有效的*/return(OS_MEM_INVALID_PMEM);

}

if(pblk= =(void*)0){ /*确保释放的内存块是有效的 */

return(OS_MEM_INVALID_PBLK);

}

#endif

OS_ENTER_CRITICAL();

if(pmem->OSMemNFree>=pmem->OSMemNBlks){ /*检查内存分区是否已满 */OS_EXIT_CRITICAL(); /*若满,则说明分配或者释放内存时出现了错误 */

return(OS_MEM_FULL);

}

/*内存分区未满 */

*(void**)pblk=pmem->OSMemFreeList;/*将需要释放的内存块返回给空闲内存控制块链表*/pmem->OSMemFreeList=pblk; /*调整指针,将所释放的内存块放在链表的最前面 */

pmem->OSMemNFree++; /*将分区中的空闲内存块总数加1 */

OS_EXIT_CRITICAL();

return(OS_NO_ERR); /*通知调用者释放成功 */

}

9.4.5范例

OSMemPut()函数的使用范例如下:OS_MEM*MemBuf; /*定义一个内存控制块指针 */

INT8U*MemMsg; /*定义一个内存块指针 */

voidTask(void*pdata){

INT8Uerr;

pdata=pdata;

for(;;){

err=OSMemPut(MemBuf,(void*)MemMsg);if(err= =OS_NO_ERR){

. /*处理代码 */

}

.

}

}9.5查询内存分区的状态——OSMemQuery()

9.5.1函数原型

函数原型如下:

INT8UOSMemQuery(OS_MEM*pmem,OS_MEM_DATA*pdata)

OSMemQuery()函数可以查询特定内存分区中的有关信息。该函数使用了一个新的OS_MEM_DATA的数据结构来复制OS_MEM结构中的信息,并比OS_MEM多一个成员。OS_MEM_DATA的数据结构如程序清单9.5所示。函数的调用者可以是任务或者中断,开关量是OS_MEM_EN和OS_MEM_QUERY_EN。该函数有如下两个参数:(1) pmem:指向内存控制块的指针。其值可以在调用OSMemCreate()函数时返回得到。

(2) pdata:指向OS_MEM_DATA数据结构的指针。它比OS_MEM多一个成员。 程序清单9.5OS_MEM_DATA的数据结构

typedefstruct{

void OSAddr; /*指向内存分区起始地址的指针 */

void OSFreeList; /*指向空闲内存块列表起始地址的指针 */

INT32U OSBlkSize; /*每个内存块的大小 */ INT32U OSNBlks; /*内存分区的内存块总数 */

INT32U OSNFree; /*空闲的内存块数量 */

INT32U OSNUsed; /*正在使用的内存块数量 */

}OS_MEM_DATA;9.5.2返回值

OSMemQuery()函数的返回值为下述内容之一:

(1) OS_NO_ERR:调用成功。

(2) OS_MEM_INVALID_PMEM:pmem是空指针。

(3) OS_MEM_INVALID_PDATA:pdata是空指针。

9.5.3源代码

OSMemQuery()函数的源代码如程序清单9.6所示,它将指定内存分区的信息复制到OS_MEM_DATA定义的变量中。 程序清单9.6OSMemQuery()函数的源代码

#ifOS_MEM_QUERY_EN>0

INT8UOSMemQuery(OS_MEM*pmem,OS_MEM_DATA*ppdata)

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论