- 该函数的功能是创建一块内存分区,并通过内存控制块指针来管理,具体功能为:堆内存addr地址处已经分配了一块连续内存分区,将该内存分区划分为blksize大小的不同内存块来管理,并配置内存块管理结构指针pmem。
- 函数源码如下:
- OS_MEM *OSMemCreate (void *addr, INT32U nblks, INT32U blksize, INT8U *err)
- {
- #if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
- OS_CPU_SR cpu_sr;
- #endif
- OS_MEM *pmem;
- INT8U *pblk;
- void **plink;
- INT32U i;
-
- #if OS_ARG_CHK_EN > 0
- if (addr == (void *)0) { /* Must pass a valid address for the memory part. */
- *err = OS_MEM_INVALID_ADDR;
- return ((OS_MEM *)0);
- }
- if (nblks < 2) { /* Must have at least 2 blocks per partition */
- *err = OS_MEM_INVALID_BLKS;
- return ((OS_MEM *)0);
- }
- if (blksize < sizeof(void *)) { /* Must contain space for at least a pointer */
- *err = OS_MEM_INVALID_SIZE;
- return ((OS_MEM *)0);
- }
- #endif
-
- OS_ENTER_CRITICAL();
- pmem = OSMemFreeList; /* Get next free memory partition */
- if (OSMemFreeList != (OS_MEM *)0) { /* See if pool of free partitions was empty */
- OSMemFreeList = (OS_MEM *)OSMemFreeList->OSMemFreeList;
- }
- OS_EXIT_CRITICAL();
- if (pmem == (OS_MEM *)0) { /* See if we have a memory partition */
- *err = OS_MEM_INVALID_PART;
- return ((OS_MEM *)0);
- }
- /*
- 将一个分区的各个内存小块链接起来,形成一个链表
- */
- plink = (void **)addr; /* Create linked list of free memory blocks */
- pblk = (INT8U *)addr + blksize;
- for (i = 0; i < (nblks - 1); i++) {
- *plink = (void *)pblk;
- plink = (void **)pblk;
- pblk = pblk + blksize;
- }
- *plink = (void *)0; /* Last memory block points to NULL */
-
- pmem->OSMemAddr = addr; /* Store start address of memory partition */
-
- pmem->OSMemFreeList = addr; /* Initialize pointer to pool of free blocks */
-
- pmem->OSMemNFree = nblks; /* Store number of free blocks in MCB */
-
- pmem->OSMemNBlks = nblks;
-
- pmem->OSMemBlkSize = blksize; /* Store block size of each memory blocks */
- *err = OS_NO_ERR;
- return (pmem);
- }
- 该函数的巧妙之处,也即难理解之处便在于:将addr起始的内存块以balcksize大小分块,并在每一块的起始地址处保存一个地址(指针变量),该地址(指针)指向下一内存块的起始地址,即以单链表的形式管理该内存分区的每一块内存块。
- 由于要在每一内存块起始地址保存一个地址,那么很显然,我们需要一个二级地址来保存指向下一内存块的地址。
- 下边以串联一个内存块为例讲解:
- void **plink = (void **)addr;解析:addr即为该内存分区首地址,将其强制转换为二级指针并赋值给plink,则plink也指向该分区首地址。这里最不好理解的是*plink是什么。为什么是这样呢?这里要好好思考一下将一级指针强制转换为二级指针的过程。以下图为例进行讲解:假设内存分区起始地址0x20000000,该内存中存储为0x12.....,此时,addr中存储的是0x12的地址,即首内存分区地址0x20000000,以此类推。
-
- 一旦执行void **plink = (void **)addr之后,从首地址开始的4字节内存中存储的数据被解释为地址(指针),但是这个地址是无意义的,我们正需要将该地址改写为下一内存块的地址,即通过这两句实现:pblk = (INT8U *)addr + blksize;*plink = (void *)pblk;(如下图所示)
- 以上便是一次链接内存块的过程。 在执行plink = (void **)pblk;之后,重复执行上一步,即可实现对整个内存分区所有内存块的串联。
- 最终串联完所有内存块后,*plink = (void *)0; 指针为空,表示下一内存块不存在,链表链接完成。
终于写完了,虽然花了点时间,但是自己完全搞懂了,希望能帮助所有在这里遇到困难的朋友!