函數OSMemCreate()理解,二級指針,二維數組,強制轉換

建立一個內存分區的步驟是先建立一個二維數組,二維數組的第一維是塊數,第二維是塊的大小,二維數組把一塊連續的內存佔了(雖然佔了,但是並不能有詳細的管理),然後把這個二維數組的地址給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。
函數功能完成。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章