<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > UC/OS-II的內(nèi)存管理OSMemCreate()分析

          UC/OS-II的內(nèi)存管理OSMemCreate()分析

          作者: 時(shí)間:2016-12-01 來(lái)源:網(wǎng)絡(luò) 收藏
          我們?cè)趯憫?yīng)用程序的過(guò)程中通常都是采用一個(gè)malloc/free系列函數(shù)進(jìn)行內(nèi)存的管理,這樣分配的內(nèi)存空間是從應(yīng)用程序的??臻g分配處理,一般而言我們?cè)趯懗绦虻倪^(guò)程中要對(duì)內(nèi)存空間進(jìn)行適時(shí)的釋放,才不至于導(dǎo)致??臻g的不足,當(dāng)然這樣也會(huì)導(dǎo)致內(nèi)存垃圾的產(chǎn)生,因?yàn)椴煌笮〉膬?nèi)存分配因?yàn)閷?duì)齊等原因?qū)е潞芏嗟膬?nèi)存不能再使用,進(jìn)而使得系統(tǒng)的可用內(nèi)存越來(lái)越小,因此在實(shí)時(shí)操作系統(tǒng)中通常都需要?jiǎng)?chuàng)建自己的內(nèi)存管理操作。
          uc/os-II中的內(nèi)存管理主要是采用內(nèi)存分區(qū)控制塊實(shí)現(xiàn)的,具體的實(shí)現(xiàn)過(guò)程如下:

          本文引用地址:http://www.ex-cimer.com/article/201612/324517.htm

          /*
          關(guān)于內(nèi)存控制塊的結(jié)構(gòu)體,
          用來(lái)跟蹤每一個(gè)內(nèi)存分區(qū)
          每一個(gè)分區(qū)可以分成很多個(gè)小的內(nèi)存塊
          每一個(gè)內(nèi)存塊的大小都是相同的
          */
          #if (OS_MEM_EN > 0) && (OS_MAX_MEM_PART > 0)
          typedef struct { /* MEMORY CONTROL BLOCK */
          /*內(nèi)存的起始地址*/
          void *OSMemAddr; /* Pointer to beginning ofmemorypartition */
          /*
          鏈表指針,
          能夠快速的實(shí)現(xiàn)內(nèi)存的控制
          */
          void *OSMemFreeList; /* Pointer to list of free memory blocks */
          /*每一個(gè)內(nèi)存塊的大小*/
          INT32U OSMemBlkSize; /* Size (in bytes) of each block of memory */
          /*存在的內(nèi)存塊數(shù)量*/
          INT32U OSMemNBlks; /* Total number of blocks in this partition */
          /*空閑的內(nèi)存空間*/
          INT32U OSMemNFree; /* Number ofmemoryblocks remaining in this partition */
          } OS_MEM;

          基本的實(shí)現(xiàn)思想就是將內(nèi)存分區(qū)分解成很多的大小相同的內(nèi)存塊,然后OSMemFreeList將所有的內(nèi)存塊鏈接起來(lái),但是此處的鏈接與我們常用的鏈表存在一定的差別,這也是內(nèi)存管理中常用的技巧之一,即在當(dāng)前塊的起始地址處存放下一個(gè)內(nèi)存塊的地址,這樣就能比較快速的實(shí)現(xiàn)內(nèi)存的管理。在uc/os-II的內(nèi)存管理代碼的OS_MEM *OSMemCreate (void *addr, INT32U nblks, INT32U blksize, INT8U *err)函數(shù)中存在一些代碼難點(diǎn)。特別是強(qiáng)制類型轉(zhuǎn)換的使用,在uc/os-II中我們看見(jiàn)了大量的強(qiáng)制類型轉(zhuǎn)換問(wèn)題,下面就做一下簡(jiǎn)要的分析:

          OS_MEM *OSMemCreate (void *addr, INT32U nblks, INT32U blksize, INT8U *err)
          {
          #if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPUstatusregister */
          OS_CPU_SR cpu_sr;
          #endif
          OS_MEM *pmem;
          INT8U *pblk;
          void **plink;
          INT32U i;

          /*檢測(cè)參數(shù)的正確性*/
          #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();
          /*得到空閑的內(nèi)存控制塊*/
          pmem = OSMemFreeList; /* Get next free memory partition */
          /*更新內(nèi)存控制塊鏈表*/
          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);
          }
          /*
          將一個(gè)分區(qū)的各個(gè)內(nèi)存小塊鏈接起來(lái),
          形成一個(gè)鏈表
          */
          plink = (void **)addr; /* Create linked list of free memoryblocks */
          /*得到一個(gè)固定的*/
          pblk = (INT8U *)addr + blksize;
          for (i = 0; i < (nblks - 1); i++) {
          /*在當(dāng)前的地址處保存下一個(gè)內(nèi)存塊的地址*/
          *plink = (void *)pblk;
          /*將指針指向下一個(gè)內(nèi)存塊*/
          plink = (void **)pblk;
          /*得到第三個(gè)內(nèi)存塊的地址*/
          pblk = pblk + blksize;
          }
          /*
          將最后一個(gè)內(nèi)存塊的下一個(gè)地址設(shè)置為NULL;
          */
          *plink = (void *)0; /* Last memoryblockpoints to NULL */
          /*得到內(nèi)存分區(qū)的起始地址*/
          pmem->OSMemAddr = addr; /* Store start address ofmemorypartition */
          /*
          將內(nèi)存塊的鏈表頭指向當(dāng)前地址
          通過(guò)鏈表能夠快速的找到下一個(gè)內(nèi)存塊的地址
          只需要對(duì)鏈表取地址,
          然后將指針指向該地址就能實(shí)現(xiàn)快速的切換
          */
          pmem->OSMemFreeList = addr; /* Initialize pointer to pool of free blocks */
          /*空閑的內(nèi)存塊*/
          pmem->OSMemNFree = nblks; /* Store number of free blocks in MCB */
          /*內(nèi)存的總塊數(shù)*/
          pmem->OSMemNBlks = nblks;
          /*內(nèi)存塊的大小*/
          pmem->OSMemBlkSize = blksize; /* Store block size of eachmemoryblocks */
          *err = OS_NO_ERR;
          return (pmem);
          }

          上面的代碼大致的意思就是完成內(nèi)存塊的鏈接以及內(nèi)存分區(qū)控制單元的初始化操作,但是有幾句代碼存在一定的理解難度。

          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; /* Lastmemoryblockpoints to NULL */

          下面幾句代碼中存在大量的強(qiáng)制類型轉(zhuǎn)換,我們一句一句的分析,plink=(void**)addr的意思是將傳遞進(jìn)來(lái)的地址強(qiáng)制轉(zhuǎn)換,原因是因?yàn)閜link是一個(gè)存儲(chǔ)在函數(shù)棧中的變量,它指向了addr指向的地址,而該地址處將來(lái)存儲(chǔ)的也是一個(gè)地址,因此可以看做二維指針,而addr只是一維指針,因此需要強(qiáng)制類型轉(zhuǎn)換為二維指針。

          引用:在聲明的時(shí)候,plink是二維指針,在這里將addr強(qiáng)制的轉(zhuǎn)換成二維指針再賦值給plink的原因是:讓addr以前指向的內(nèi)容讓編譯器解釋成地址,也就是一個(gè)指針,如果不做這個(gè)強(qiáng)制轉(zhuǎn)換,以前addr指向的內(nèi)容就不是一個(gè)地址,也就是不是指針,在這個(gè)函數(shù)當(dāng)中,我們想把a(bǔ)ddr指向的二維數(shù)組,分割成大小相同的若干塊,就必須用指針把它們鏈接起來(lái),所以將addr強(qiáng)制轉(zhuǎn)換成二維指針,然后賦值給plink,然后讓plink去執(zhí)行連接的操作,也就是在以前addr指向的地方放上指針;plink本身是存放在棧上的,plink這個(gè)符號(hào)的值是指向addr的,*plink就是取plink指向地址單元的內(nèi)容,而 plink指向地址單元的內(nèi)容是一個(gè)地址,即是一個(gè)指針,plink指向地址單元的內(nèi)容也就是addr指向地址單元的內(nèi)容,但由于addr是一維指針,所以它指向的內(nèi)容不會(huì)被解釋成一個(gè)地址,而是一般的內(nèi)容?,F(xiàn)在*plink就是把a(bǔ)ddr所在存儲(chǔ)單元的內(nèi)容解釋成一個(gè)指針,并且將下一個(gè)block的首地址賦值給此存儲(chǔ)單元,理解了這點(diǎn)就可以理解下面的源代碼了,同時(shí)對(duì)C里面指針的概念又有了進(jìn)一步的認(rèn)識(shí)。http://blog.csdn.net/uestczhangchao/article/details/5589476

          pblk=(INT8U*)addr+blksize;這句代碼其中暗含了我們對(duì)指針加減操作的基本理解,因?yàn)樵趗c/OS-II中是按照字節(jié)作為內(nèi)存塊分布的,所以進(jìn)行了INT8U*的強(qiáng)制類型轉(zhuǎn)換,因?yàn)橹挥羞@樣才能保證addr+blksize的操作是增加多少個(gè)字節(jié)的數(shù)據(jù)。因?yàn)镃語(yǔ)言中指針的加減是與其指向的類型的內(nèi)存空間密切相關(guān)的,比如int * p = 0; p ++;此時(shí)的p = 4;而當(dāng)char *p = 0; p ++; 此時(shí)的p = 1;這就說(shuō)明了指針的加減必須注意數(shù)據(jù)的類型,而不能直接對(duì)void*類型的指針進(jìn)行加減操作。

          *plink=(void*)pblk;因?yàn)閜link是一個(gè)棧中變量,而對(duì)plink取進(jìn)行解引用,實(shí)際上就是得到addr的值,但是*plink是一個(gè)地址,還是一個(gè)指針,因此需要強(qiáng)制類型轉(zhuǎn)換。

          plink=(void**)pblk;上面的代碼已經(jīng)分析。

          基本的思想就是需要注意常數(shù)轉(zhuǎn)換為指針的方式方法:int* p = (int *)0x45342341;int **p = (int **)0x45342341;指針的引入主要就是為了解決內(nèi)存問(wèn)題,因此對(duì)內(nèi)存的管理直接體現(xiàn)了對(duì)指針的理解深度。



          評(píng)論


          相關(guān)推薦

          技術(shù)專區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();