函数OSMemCreate()理解,二级指针,二维数组,强制转换

13 篇文章 0 订阅
4 篇文章 0 订阅

建立一个内存分区的步骤是先建立一个二维数组,二维数组的第一维是块数,第二维是块的大小,二维数组把一块连续的内存占了(虽然占了,但是并不能有详细的管理),然后把这个二维数组的地址给OSMemCreate()函数,进行一系列设置,方便系统对这块连续的内存进行管理。

函数作用:

把二维数组与内存控制块联系起来,行程内存分区;

函数工作流程:

OSMemCreate()对内存分区主要做了三个工作:
1,在系统初始化时,根据设置先初始化了一系列空白的内存控制块,并形成链表。在函数中首先在这个空白内存控制块中拿出一个控制块备用。
2,内存分区是一片连续的内存,目的是把这块内存分成一个个小块方便管理,所以函数先把每个块串联成一个链表。每个块前四个字节存放的都是下一个内存块的首地址。末尾指向NULL;
3,把内存与内存控制块联系起来。

详解:

固定流程:判断参数等

OS_MEM  *OSMemCreate (void   *addr,//内存分区的起始地址,也就是二维数组的起始地址。
                      INT32U  nblks,//块数,就是二维数组的第一维组数。
                      INT32U  blksize,//块大小,注意是字节为单位的。
                      INT8U  *perr)//错误标志。
{
    OS_MEM    *pmem;
    INT8U     *pblk;
    void     **plink;
    INT32U     loops;
    INT32U     i;
#if OS_CRITICAL_METHOD == 3u                          /* Allocate storage for CPU status register      */
    OS_CPU_SR  cpu_sr = 0u;
#endif



#ifdef OS_SAFETY_CRITICAL
    if (perr == (INT8U *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
    }
#endif

#ifdef OS_SAFETY_CRITICAL_IEC61508
    if (OSSafetyCriticalStartFlag == OS_TRUE) {
        OS_SAFETY_CRITICAL_EXCEPTION();
    }
#endif

#if OS_ARG_CHK_EN > 0u
    if (addr == (void *)0) {                          /* Must pass a valid address for the memory part.*/
        *perr = OS_ERR_MEM_INVALID_ADDR;
        return ((OS_MEM *)0);
    }
    if (((INT32U)addr & (sizeof(void *) - 1u)) != 0u){  /* Must be pointer size aligned                */
        *perr = OS_ERR_MEM_INVALID_ADDR;
        return ((OS_MEM *)0);
    }
    if (nblks < 2u) {                                 /* Must have at least 2 blocks per partition     */
        *perr = OS_ERR_MEM_INVALID_BLKS;
        return ((OS_MEM *)0);
    }
    if (blksize < sizeof(void *)) {                   /* Must contain space for at least a pointer     */
        *perr = OS_ERR_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             */
        *perr = OS_ERR_MEM_INVALID_PART;
        return ((OS_MEM *)0);//看看摘取下来的内存控制块是否可用,如果不可用返回错误信息。
    }

从已经创建的空闲内存控制块链表中取出一个备用。并且如果取出的是非空的,再把空余链表头指向下一个链表节点。

串联内存块成链表:

    plink = (void **)addr;                            /* Create linked list of free memory blocks      */
    pblk  = (INT8U *)addr;
    loops  = nblks - 1u;
    for (i = 0u; i < loops; i++) {
        pblk +=  blksize;                             /* Point to the FOLLOWING block                  */
       *plink = (void  *)pblk;                        /* Save pointer to NEXT block in CURRENT block   */
        plink = (void **)pblk;                        /* Position to  NEXT      block                  */
    }

这部分比较难理解,因为涉及到二级指针、二维数组与强制转换。
二级指针:A(即B的地址)是指向指针的指针,称为二级指针,用于存放二级指针的变量称为二级指针变量。根据B的不同情况,二级指针又分为指向指针变量的指针和指向数组的指针。
二维数组:二维数组本质就是一段连续的内存,但是二维数组的头是一个一级指针,指向内存首地址。与二级指针并不等价。所以传递参数时候addr是一个指向空的一级指针。
强制转换:强制转换只是转换当前变量类型,例如 : int p; p=0xaa00; *p=100; 则 (void )p=0xaa00;只是对p进行了强制转换并不是指指针所指的变量,要分清。

函数中plink=(void * *)addr;是把addr转换成指向任意类型的空指针的二级指针。因为plink定义的就是二级指针,所以要想直接赋值必须把addr也转换为二级指针。虽然直接赋值也是传递地址,但是传递地址的意义不一样,对于cortex内核来说是32位内核,地址是4个字节的,如果只传递地址,地址所指向的内存类型位置,编译器就不知道传递的是几个字节,有可能只是一个字节。但是转换为二级指针后,传递的是指向指针的指针,即是4个字节的内存。所以强制转换是有必要的。

进入for循环前,plink存放的是内存分区的首地址,地址内存放的是一个指针。pblk存放的是下一个块的首地址,地址内存放内容占用一个单位(一个字节)就可以,方便与blksize(字节单位)相加,所以addr转换成一级指针就可以。但是两个地址是相同的。
1,进入循环后,先把pblk指针位置后移blksize,即进入plink后一个块的首地址。
2,然后把pblk强制转换为指向任意内容的指针,再把地址赋给plink所指向的内存(注意是把地址放入PLINK地址所指向的内存)。此时plink指向前一个内存块,内存块前四个字节存放的下一个内存块的首地址。
3,然后把pblk转换为二级指针,再把pblk所指向的地址赋给plink,即plink指向原来内存块的后一个内存块地址,地址内存放的内容仍然是指针(内存内并没有被赋值,下一次循环的第二步才会被赋值)。这个步骤类似plink=(void **)addr;

链接内存控制块:

    *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        */
    *perr               = OS_ERR_NONE;
    return (pmem);
}

for循环出来后,plink所指向的地址就是内存分区最后一个块的首地址,因为是最后一个块了,把NULL赋给这个地址标志是最后一个块。
然后把内存分区首地址赋给内存控制块的OSMemAddr;
因为分区内还没有块被占用,所以空闲块的首地址与内存分区地址相同,再把首地址赋给OSMemFreeList。
分区内可用块大小与总块大小相同,所以把NBLKS赋给OSMemNFree和OSMemNBlks;
分区内每个块大小blksize赋给OSMemBlksize。
函数功能完成。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值