欧美极品高清xxxxhd,国产日产欧美最新,无码AV国产东京热AV无码,国产精品人与动性XXX,国产传媒亚洲综合一区二区,四库影院永久国产精品,毛片免费免费高清视频,福利所导航夜趣136

標(biāo)題: TI OSAL內(nèi)存管理實現(xiàn)方式 [打印本頁]

作者: 沒有你    時間: 2020-1-15 00:53
標(biāo)題: TI OSAL內(nèi)存管理實現(xiàn)方式
本帖最后由 沒有你 于 2020-1-16 00:25 編輯

    之前發(fā)的帖子介紹了OSAL在STC8A8K64S4A12單片機(jī)的移植方式和簡單使用,今天就簡單介紹一下OSAL的內(nèi)存管理方式。在介紹之前,首先要了解單片機(jī)的棧和堆的區(qū)別。    一提到“!保苋菀紫氲絾纹瑱C(jī)的壓棧(PUSH)和出棧(POP)中用到的棧,這個棧是系統(tǒng)自動分配和釋放的,具體分配多少?臻g,在程序編譯后就已經(jīng)確定不變了、而且無法更改。子函數(shù)內(nèi)部的局部變量定義和使用,也是系統(tǒng)自動分配?臻g來保存變量,等函數(shù)執(zhí)行完,系統(tǒng)就會將棧空間收回。另一個“堆”,就沒有“!蹦敲词煜ち耍驗槌鯇W(xué)者用單片機(jī)的時候,不會想到用堆的,也比較少接觸!岸选本褪怯脩糇约汗芾淼膬(nèi)存空間,一般都是定義一個大數(shù)組全局變量,再基于這個數(shù)組的內(nèi)存空間做管理。自己編寫類似malloc和free函來實現(xiàn)內(nèi)存空間的申請和釋放,即需要用到空間,要自己申請,等空間使用周期結(jié)束了,還要自己釋放空間。
    既然 “!蹦敲捶奖,由系統(tǒng)自動申請,自動釋放,肯定比自己去管理方便多了,那為什么還要“堆”呢?前面講過,“!笨臻g的使用在程序編譯后就定死了,無法自己制定使用多大的棧,這樣就很不方便,尤其是在傳輸處理動態(tài)數(shù)據(jù)流的時候就很麻煩。那肯定有人想過,在函數(shù)里面定義一個很大的數(shù)組局部變量,那不就可以應(yīng)對各種各樣的數(shù)據(jù)流了。如果在函數(shù)里面定義很大的數(shù)組,系統(tǒng)在跑的使用,其他函數(shù)也會用到?臻g哦,一旦?臻g使用過大導(dǎo)致內(nèi)存溢出,那系統(tǒng)肯定奔潰了!岸选本筒灰粯恿,要用多少空間,完全可以動態(tài)申請,需要多少,就申請多少。由于是在自己定義全局?jǐn)?shù)組變量的地址上管理內(nèi)存操作,不怕內(nèi)存溢出。
   棧和堆的共同點和區(qū)別:
   1、共同點:a:都是占用ram空間;
                     b:可使用最大ram空間在程序編譯后就確定不變了;
   2、區(qū)別:?臻g由系統(tǒng)自動申請和釋放,堆空間由用戶自己申請和釋放。
   接下來介紹一下OSAL的內(nèi)存管理,OSAL的內(nèi)存管理是堆管理,OSAL_Memory.c里面定義了一個很大的數(shù)組theHeap,內(nèi)存管理把堆空間分成兩個部分,第一個部分是針對小塊內(nèi)存的管理,第二部分是針對大塊內(nèi)存的管理。這樣做的好處是容易申請到連續(xù)的大空間,因為小塊內(nèi)存處理會使整個內(nèi)存空間碎片化,那么會導(dǎo)致內(nèi)存空間不連續(xù),不連續(xù)的空間是對申請大空間是非常不利的。在申請堆空間時,會自動合并之前釋放的free塊,等到找到合適連續(xù)塊時,會自動裁剪多余的塊空間,以免造成空間的浪費(fèi)。
  下面列舉出已去除其他無關(guān)代碼和修改部分名稱后的內(nèi)存管理代碼,代碼處理有詳細(xì)注釋,還有測試過程。另外,代碼的測試是基于STC8A8K64S4A12單片機(jī),ram空間為8K。
首先是相關(guān)宏和變量定義
//堆總空間
#define HEAP_SIZE   2048   
#define HEAPMEM_IN_USE             0x8000

//堆空間結(jié)束位置
#define HEAP_LASTBLK_IDX      ((HEAP_SIZE / HEAPMEM_HDRSZ) - 1)
//區(qū)分塊位置
#define HEAPMEM_SMALLBLK_HDRCNT   (HEAPMEM_SMALLBLK_BUCKET / HEAPMEM_HDRSZ)
//大塊起始位置
#define HEAPMEM_BIGBLK_IDX        (HEAPMEM_SMALLBLK_HDRCNT + 1)

//首部大小
#define HEAPMEM_HDRSZ              sizeof(heapMemHdr_t)  
//最小塊大小
#define HEAPMEM_MIN_BLKSZ         (HEAPMEM_ROUND((HEAPMEM_HDRSZ * 2)))
//小塊大小
#define HEAPMEM_SMALL_BLKSZ       (HEAPMEM_ROUND(16))
//默認(rèn)長塊大小
#define HEAPMEM_LL_BLKSZ          (HEAPMEM_ROUND(417) + (19 * HEAPMEM_HDRSZ))

//小塊總空間
#define HEAPMEM_SMALLBLK_BUCKET  ((HEAPMEM_SMALL_BLKSZ * HEAPMEM_SMALL_BLKCNT) + HEAPMEM_LL_BLKSZ)
//大塊總空間
#define HEAPMEM_BIGBLK_SZ         (HEAP_SIZE - HEAPMEM_SMALLBLK_BUCKET - HEAPMEM_HDRSZ*2)

//默認(rèn)小塊數(shù)量
#define HEAPMEM_SMALL_BLKCNT       8
// 調(diào)整申請內(nèi)存大小宏操作(如申請17字節(jié)空間,則調(diào)整為18字節(jié))
#define HEAPMEM_ROUND(X)       ((((X) + HEAPMEM_HDRSZ - 1) / HEAPMEM_HDRSZ) * HEAPMEM_HDRSZ)

typedef struct {
  unsigned len : 15;//本快的長度最大為2^16-1個字節(jié),且申請空間的最小粒度為2個字節(jié)
  unsigned inUse : 1;//標(biāo)志位表示本快是否已經(jīng)被使用
} heapMemHdrHdr_t;

typedef union {
  //因此,當(dāng)halDataAlign\u t小于UINT16時,編譯器強(qiáng)制結(jié)構(gòu)對齊最大的元素,而不會在目標(biāo)上浪費(fèi)空間。
  uint8 alignDummy;
  uint16 val;//存儲上一塊長度,in use信息
  heapMemHdrHdr_t hdr;//快頭指針
} heapMemHdr_t;

static __no_init heapMemHdr_t all_heap[HEAP_SIZE];//定義堆空間數(shù)組
static __no_init heapMemHdr_t *ff1;  //第一個空塊

static uint8 heapMemStat = 0x01;            // 離散狀態(tài)標(biāo)志 0x01:踢出
說明,每個all_heap元素占用2個字節(jié),即16個bit,最高位bit代表使用狀態(tài),1表示非free,0表示free。剩下15個bit可以表示32768個byte堆空間,高達(dá)32K了,應(yīng)付51單片機(jī)是完全沒有問題的。定義了一個全局?jǐn)?shù)組變量all_heap作為堆總空間,大小為2048個byte。可以根據(jù)單片機(jī)資源自行修改堆總空間大小,宏HEAPMEM_SMALLBLK_HDRCNT 是小塊內(nèi)存和大塊內(nèi)存的分界數(shù)值。

下面是堆空間初始化函數(shù)
void heap_init(void)
{
  all_heap[HEAP_LASTBLK_IDX].val = 0;//在堆的末尾設(shè)置一個空塊,以便與零進(jìn)行快速比較
  ff1 = all_heap;//設(shè)置管理小塊空間的首部
  ff1->val = HEAPMEM_SMALLBLK_BUCKET;
   //設(shè)置劃分小塊空間與大塊空間的首部
  all_heap[HEAPMEM_SMALLBLK_HDRCNT].val = (HEAPMEM_HDRSZ | HEAPMEM_IN_USE);
  // 設(shè)置管理大塊空間首部
  all_heap[HEAPMEM_BIGBLK_IDX].val = HEAPMEM_BIGBLK_SZ;  // Set 'len' & clear 'inUse' field.
}

下面是執(zhí)行heap_init()之后的內(nèi)存地址空間示例(初始地址是某次上電隨機(jī)出現(xiàn)的)
use
value
地址
對應(yīng)數(shù)組
0
584
0x036d - 0x036e
all_heap[0]
0
-
-
-
0
-
-
-
1
2
0x05b5 - 0x05b6
all_heap[292]
0
1460
0x05b7 - 0x05b8
all_heap[293]
0
-
-
-
0
-
-
-
0
0
0x0b6b - 0x0b6c
all_heap[1023]
    說明,第一個all_heap[0]存放這個小塊空間首部,這個首部有小塊空間剩余量,584表示可以使用584個byte的小塊空間,每次有小塊空間申請成功,這個剩余值就會減少。藍(lán)色標(biāo)注的是小塊內(nèi)存和大塊內(nèi)存的格擋板,位置為all_heap[292],狀態(tài)標(biāo)為1表示已使用,這樣在小塊內(nèi)存在分配內(nèi)存的時候就不會合并到大塊內(nèi)存上面。all_heap[293]存放大塊空間首部,這個首部有大塊空間剩余量,1460表示可以使用1460個byte的大塊空間,每次有大塊空間申請成功,這個剩余值就會減少。堆末尾all_heap[1023]值被初始化為0,以便與零進(jìn)行快速比較。從0x036d-0x0b6c這2048個byte空間就是本次堆的全部空間大小。

下面是堆空間申請函數(shù)
void *heap_alloc(uint16 size)
{
  heapMemHdr_t *prev = NULL;
  heapMemHdr_t *hdr;
  uint8 intState;
  uint8 coal = 0;
  size += HEAPMEM_HDRSZ; //給需要申請的空間分配一個管理首部
//進(jìn)入臨界區(qū)

  //調(diào)整size大小,是空間對齊(與處理器和編譯器相關(guān))
  if ( sizeof( uint8 ) == 2 )//假設(shè)uint8占用2個字節(jié)
  {
    size += (size & 0x01);//假設(shè)為196個,則size為196;假設(shè)為197個,則size要198才滿足
  }
  else if ( sizeof( uint8 ) != 1 )
  {
    const uint8 mod = size % sizeof( uint8 );

    if ( mod != 0 )
    {
      size += (sizeof( uint8 ) - mod);
    }
  }
  //判斷小塊內(nèi)存空間是否足夠分配,否則向大塊內(nèi)存空間申請
  if ((heapMemStat == 0) || (size <= HEAPMEM_SMALL_BLKSZ))
  {
    hdr = ff1;//小塊內(nèi)存,從ff1開始查找

  }
  else
  {
    hdr = (all_heap + HEAPMEM_BIGBLK_IDX);//從大塊開始查找
  }
  //開始迭代的尋找適合的內(nèi)存空間
  do
  {
    if ( hdr->hdr.inUse )//遇到非free塊
    {
      coal = 0;//告訴下一塊,本塊非free
    }
    else //遇到free塊
    {
      if ( coal != 0 )//上一塊是free塊
      {
        prev->hdr.len += hdr->hdr.len;//兩個free塊合并相鄰內(nèi)存空間

        if ( prev->hdr.len >= size )  //合并后的大小滿足size
        {
          hdr = prev;  //得到塊的地址
          break;
        }
      }
      else //上一塊是非free塊
      {
        if ( hdr->hdr.len >= size )//一個快的大小就可以滿足情況,分配,跳出循環(huán)返回
        {
          break;
        }

        coal = 1;//否則,標(biāo)記coal為1,告訴下一塊,本快是free的
        prev = hdr; //保存當(dāng)前內(nèi)存地址
      }
    }
    //(uint8 *)hdr這個操作使本來2個字節(jié),強(qiáng)制轉(zhuǎn)換成1個字節(jié)
    hdr = (heapMemHdr_t *)((uint8 *)hdr + hdr->hdr.len);//經(jīng)典malloc實現(xiàn)方式,迭代下一塊

    if ( hdr->val == 0 )//已經(jīng)到達(dá)堆底部(初始化時,已經(jīng)讓堆底為零,方便識別)
    {
      hdr = NULL;//空指針,表示找不到合適size塊
      break;
    }
  }while(1);

  if ( hdr != NULL )//已經(jīng)找到合適size塊
  {
    uint16 tmp = hdr->hdr.len - size;//表示塊的大小大于請求的大小時,為了不浪費(fèi)空間,還要把塊切開
    //確定是否滿足拆分閾值
    if ( tmp >= HEAPMEM_MIN_BLKSZ )//剩下的大小可以單獨成為一個free塊
    {
      heapMemHdr_t *next = (heapMemHdr_t *)((uint8 *)hdr + size);
      next->val = tmp;                     // 告訴后一個塊自己的信息
      hdr->val = (size | HEAPMEM_IN_USE);  // value代表前一塊的大小和使用情況,這樣相當(dāng)于雙向鏈表
    }
    else
    {
      hdr->hdr.inUse = TRUE; //標(biāo)記本塊已經(jīng)被使用
    }

    if ((heapMemStat != 0) && (ff1 == hdr))
    {
      ff1 = (heapMemHdr_t *)((uint8 *)hdr + hdr->hdr.len);
    }
    hdr++;
  }
  //退出臨界區(qū)
  return (void *)hdr;
}

其中,size是申請空間的多少,每次申請空間為1個byte。比如要申請10個元素uint16類型的數(shù)組,代碼可以寫:
   uint16 *test;
   test = (uint16*)heap_alloc(20);

   下面寫一段代碼來測試heap_alloc函數(shù)
     heap_init(); //初始化堆
     uint8 *test1;
     uint16 *test2;
     uint32 *test3;
     uint8 *test4;
     test1 = (uint8 *)heap_alloc(1);
     test2 = (uint16 *)heap_alloc(2);  
     test3 = (uint32 *)heap_alloc(4);
     test4 = (uint8 *)heap_alloc(1);
   下面是測試代碼運(yùn)行后申請空間情況示例(地址:0x036d-0x037e)
0x03-前綴
6d
6e
6f
70
71
72
73
74
75
76
77
78
79
7a
7b
7c
7d
7e
      申請塊
test1
test2
test3
Test4
      標(biāo)識塊
584
581
577
571
568

   說明,灰色標(biāo)識前后塊信息(非free態(tài)),里面的數(shù)值是剩余byte空間,本次測試執(zhí)行后,總共用去16個byte(地址0x036d-0x037e)空間,其中8個byte用于剩余頭部信息存儲,8個bytes才是有效空間。每個申請內(nèi)存塊都帶有前后信息塊,借鑒了雙向鏈表的數(shù)據(jù)結(jié)構(gòu)。568沒有顏色的表示free態(tài)。

    下面是堆空間釋放函數(shù)
void heap_free(void *ptr)
{
  //如果heapMemHdr_t為2個字節(jié),則下面指針減去1,物理地址會改變2
  heapMemHdr_t *hdr = (heapMemHdr_t *)ptr - 1;//獲取該內(nèi)存空間首部
  uint8 intState;
  //進(jìn)入中斷臨界
  hdr->hdr.inUse = FALSE; //標(biāo)記使用狀態(tài)為:未使用

  if (ff1 > hdr)
  {
    ff1 = hdr;//調(diào)整ff1位置
  }
  //退出中斷臨界
}

    比如執(zhí)行heap_free(test1),傳入地址為0x036f,執(zhí)行過程中會將0x036d的狀態(tài)標(biāo)為free態(tài),也就是釋放掉堆占用了,584那塊會由灰色變成無色。那么,下次申請空間的時候,可以申請使用這塊內(nèi)存(地址:0x036d-0x037e)
   0x03-前綴
6d
6e
6f
70
71
72
73
74
75
76
77
78
79
7a
7b
7c
7d
7e
      申請塊
test2
test3
Test4
      標(biāo)識塊
584
581
577
571
568
    上面的示例是小內(nèi)存的管理過程,大內(nèi)存的管理也是一樣的過程,這里就不列舉了。
  在實際項目中,搭載TI OSAL的ble芯片或者zibee芯片運(yùn)行都非常穩(wěn)定,這也要歸功于OSAL高效可靠的內(nèi)存管理。非常值得研究和借鑒!





作者: xizhe2005    時間: 2020-1-15 16:46
你寫這么多,為什么就不能讓大家都拿來就用體驗一下
作者: 沒有你    時間: 2020-1-15 20:43
xizhe2005 發(fā)表于 2020-1-15 16:46
你寫這么多,為什么就不能讓大家都拿來就用體驗一下

兄弟,代碼都亮出了,不就可以體驗了嗎
作者: xizhe2005    時間: 2020-1-17 09:31
給個現(xiàn)成的KEIL工程,讓我下載到我的單片機(jī)里,謝了,我不太懂原理,只是個用戶
作者: 沒有你    時間: 2020-1-17 13:14
xizhe2005 發(fā)表于 2020-1-17 09:31
給個現(xiàn)成的KEIL工程,讓我下載到我的單片機(jī)里,謝了,我不太懂原理,只是個用戶

我電腦安裝keil打開沒響應(yīng),所以沒有搞keil工程。你直接把現(xiàn)成的代碼添加到你的keil工程里面,編譯不成問題的。
作者: xizhe2005    時間: 2020-1-17 16:46
你都沒試過呀,那你怎么知道沒問題
作者: 沒有你    時間: 2020-1-17 19:09
xizhe2005 發(fā)表于 2020-1-17 16:46
你都沒試過呀,那你怎么知道沒問題

我在IAR平臺調(diào)試的
作者: 沒有你    時間: 2020-1-17 19:24
xizhe2005 發(fā)表于 2020-1-17 16:46
你都沒試過呀,那你怎么知道沒問題

就算有點問題,應(yīng)該也是數(shù)據(jù)類型定義那邊修改一下就可以了。

作者: men007    時間: 2025-4-26 11:45
下載學(xué)習(xí),謝謝分享!




歡迎光臨 (http://www.raoushi.com/bbs/) Powered by Discuz! X3.1