UCOS-II内存管理

内存管理:
  我们知道,在 ANSI C 中可以用 malloc()和 free()两个函数动态地分配内存和释放内存。但是,在嵌入式实时操作系统中,多次这样做会把原来很大的一块连续内存区域,逐渐地分割成许多非常小而且彼此又不相邻的内存区域,也就是内存碎片。由于这些碎片的大量存在,使得程序到后来连非常小的内存也分配不到,我们讲到过用 malloc()函数来分配堆栈时,曾经讨论过内存碎片的问题。另外,由于内存管理算法的原因,malloc()和free()函数执行时间是不确定的。
  在µC/OS-II 中,操作系统把连续的大块内存按分区来管理。每个分区中包含有整数个大小相同的内存块。利用这种机制,µC/OS-II 对 malloc()和 free()函数进行了改进,使得它们可以分配和释放固定大小的内存块。这样一来,malloc()和 free()函数的执行时间也是固定的了。


内存控制块:
  为了便于内存的管理,在µC/OS-II 中使用内存控制块(memory control blocks)的数据结构来跟踪每一个内存分区,系统中的每个内存分区都有它自己的内存控制块。
typedef struct 
{
  //指向内存分区起始地址的指针 建立一个内存分区,OSMemCreate()时被初始化,在此之后就不能更改了。
  void *OSMemAddr;
  //指向下一个空闲内存控制块或者下一个空闲的内存块的指针
  void *OSMemFreeList;
  //内存分区中内存块的大小
  INT32U OSMemBlkSize;
  //内存分区中总的内存块数量
  INT32U OSMemNBlks;
  //内存分区中当前可以使用的空闲内存块的数量
  INT32U OSMemNFree;
} OS_MEM;


  在uC/OS-II中OS_MAX_MEM_PART(见文件 OS_CFG.H)定义了最大的内存分区数,该常数值至少应为 2。 每个内存分区必须含有至少两个内存块,每个内存块至少为一个指针的大小,因为同一分区中的所有空闲内存块是由指针串联起来的。


建立一个内存分区, OS_MEM *OSMemCreate ()
OS_MEM *OSMemCreate (void *addr, INT32U nblks, INT32U blksize, INT8U *err)
{
  OS_MEM *pmem;
  INT8U *pblk;
  void **plink;
  INT32U i;
  if (nblks < 2)//在ucos中内存分区中内存块的大小最小为2
  {
  *err = OS_MEM_INVALID_BLKS;
  return ((OS_MEM *)0);
  }
  if (blksize < sizeof(void *)) //每个内存块的大小至少为一个指针大小
  {
  *err = OS_MEM_INVALID_SIZE;
  return ((OS_MEM *)0);
  }
  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 = (void **)pblk;//plink指向下一个内存块
  pblk = pblk + blksize;//pblk指向下一个内存块
  }
  *plink = (void *)0;//最后一个内存块的第一个元素指向NULL
  OS_ENTER_CRITICAL();
  pmem->OSMemAddr = addr;
  pmem->OSMemFreeList = addr;
  pmem->OSMemNFree = nblks;
  pmem->OSMemNBlks = nblks;
  pmem->OSMemBlkSize = blksize;
  OS_EXIT_CRITICAL();
  *err = OS_NO_ERR;
  return (pmem);
}

分配一个内存块,OSMemGet()
void *OSMemGet (OS_MEM *pmem, INT8U *err)
{
  void *pblk;
  OS_ENTER_CRITICAL();
  if (pmem->OSMemNFree > 0) 
  {
  pblk = pmem->OSMemFreeList;
  pmem->OSMemFreeList = *(void **)pblk;
  pmem->OSMemNFree--;
  OS_EXIT_CRITICAL();
  *err = OS_NO_ERR;
  return (pblk);
  } 
  else 
  {
  OS_EXIT_CRITICAL();
  *err = OS_MEM_NO_FREE_BLKS;
  return ((void *)0);
  }
}
释放一个内存块,OSMemPut()
  注意不要把和“当前分区中内存块大小不同”的内存块释放到当前分区。否则会引起灾难性后果。
INT8U OSMemPut (OS_MEM *pmem, void *pblk)
{
  OS_ENTER_CRITICAL();
  if (pmem->OSMemNFree >= pmem->OSMemNBlks)
  {
  OS_EXIT_CRITICAL();
  return (OS_MEM_FULL);
  }
  *(void **)pblk = pmem->OSMemFreeList;
  pmem->OSMemFreeList = pblk;
  pmem->OSMemNFree++;
  OS_EXIT_CRITICAL();
  return (OS_NO_ERR);
}


查询一个内存分区的状态,OSMemQuery()
  在µC/OS-II 中,可以使用 OSMemQuery()函数来查询一个特定内存分区的有关消息。通过该函数可以知道特定内存分区中内存块的大小、可用内存块数和正在使用的内存块数等信息。所有这些信息都放在一个叫 OS_MEM_DATA 的数据结构中。


OS_MEM_DATA数据结构:
typedef struct 
{
  void *OSAddr; /* 指向内存分区首地址的指针 */
  void *OSFreeList; /* 指向空闲内存块链表首地址的指针 */
  INT32U OSBlkSize; /* 每个内存块所含的字节数 */
  INT32U OSNBlks; /* 内存分区总的内存块数 */
  INT32U OSNFree; /* 空闲内存块总数 */
  INT32U OSNUsed; /* 正在使用的内存块总数 */
} OS_MEM_DATA;

你可能感兴趣的:(UCOS,内存,嵌入式,内存管理,ucos-ii)