久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

標題: TI OSAL內存管理實現方式 [打印本頁]

作者: 沒有你    時間: 2020-1-15 00:53
標題: TI OSAL內存管理實現方式
本帖最后由 沒有你 于 2020-1-16 00:25 編輯

    之前發的帖子介紹了OSAL在STC8A8K64S4A12單片機的移植方式和簡單使用,今天就簡單介紹一下OSAL的內存管理方式。在介紹之前,首先要了解單片機的棧和堆的區別。    一提到“棧”,很容易想到單片機的壓棧(PUSH)和出棧(POP)中用到的棧,這個棧是系統自動分配和釋放的,具體分配多少棧空間,在程序編譯后就已經確定不變了、而且無法更改。子函數內部的局部變量定義和使用,也是系統自動分配棧空間來保存變量,等函數執行完,系統就會將棧空間收回。另一個“堆”,就沒有“棧”那么熟悉了,因為初學者用單片機的時候,不會想到用堆的,也比較少接觸。“堆”就是用戶自己管理的內存空間,一般都是定義一個大數組全局變量,再基于這個數組的內存空間做管理。自己編寫類似malloc和free函來實現內存空間的申請和釋放,即需要用到空間,要自己申請,等空間使用周期結束了,還要自己釋放空間。
    既然 “棧”那么方便,由系統自動申請,自動釋放,肯定比自己去管理方便多了,那為什么還要“堆”呢?前面講過,“棧”空間的使用在程序編譯后就定死了,無法自己制定使用多大的棧,這樣就很不方便,尤其是在傳輸處理動態數據流的時候就很麻煩。那肯定有人想過,在函數里面定義一個很大的數組局部變量,那不就可以應對各種各樣的數據流了。如果在函數里面定義很大的數組,系統在跑的使用,其他函數也會用到棧空間哦,一旦棧空間使用過大導致內存溢出,那系統肯定奔潰了。“堆”就不一樣了,要用多少空間,完全可以動態申請,需要多少,就申請多少。由于是在自己定義全局數組變量的地址上管理內存操作,不怕內存溢出。
   棧和堆的共同點和區別:
   1、共同點:a:都是占用ram空間;
                     b:可使用最大ram空間在程序編譯后就確定不變了;
   2、區別:棧空間由系統自動申請和釋放,堆空間由用戶自己申請和釋放。
   接下來介紹一下OSAL的內存管理,OSAL的內存管理是堆管理,OSAL_Memory.c里面定義了一個很大的數組theHeap,內存管理把堆空間分成兩個部分,第一個部分是針對小塊內存的管理,第二部分是針對大塊內存的管理。這樣做的好處是容易申請到連續的大空間,因為小塊內存處理會使整個內存空間碎片化,那么會導致內存空間不連續,不連續的空間是對申請大空間是非常不利的。在申請堆空間時,會自動合并之前釋放的free塊,等到找到合適連續塊時,會自動裁剪多余的塊空間,以免造成空間的浪費。
  下面列舉出已去除其他無關代碼和修改部分名稱后的內存管理代碼,代碼處理有詳細注釋,還有測試過程。另外,代碼的測試是基于STC8A8K64S4A12單片機,ram空間為8K。
首先是相關宏和變量定義
//堆總空間
#define HEAP_SIZE   2048   
#define HEAPMEM_IN_USE             0x8000

//堆空間結束位置
#define HEAP_LASTBLK_IDX      ((HEAP_SIZE / HEAPMEM_HDRSZ) - 1)
//區分塊位置
#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))
//默認長塊大小
#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)

//默認小塊數量
#define HEAPMEM_SMALL_BLKCNT       8
// 調整申請內存大小宏操作(如申請17字節空間,則調整為18字節)
#define HEAPMEM_ROUND(X)       ((((X) + HEAPMEM_HDRSZ - 1) / HEAPMEM_HDRSZ) * HEAPMEM_HDRSZ)

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

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

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

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

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

下面是執行heap_init()之后的內存地址空間示例(初始地址是某次上電隨機出現的)
use
value
地址
對應數組
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的小塊空間,每次有小塊空間申請成功,這個剩余值就會減少。藍色標注的是小塊內存和大塊內存的格擋板,位置為all_heap[292],狀態標為1表示已使用,這樣在小塊內存在分配內存的時候就不會合并到大塊內存上面。all_heap[293]存放大塊空間首部,這個首部有大塊空間剩余量,1460表示可以使用1460個byte的大塊空間,每次有大塊空間申請成功,這個剩余值就會減少。堆末尾all_heap[1023]值被初始化為0,以便與零進行快速比較。從0x036d-0x0b6c這2048個byte空間就是本次堆的全部空間大小。

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

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

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

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

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

        coal = 1;//否則,標記coal為1,告訴下一塊,本快是free的
        prev = hdr; //保存當前內存地址
      }
    }
    //(uint8 *)hdr這個操作使本來2個字節,強制轉換成1個字節
    hdr = (heapMemHdr_t *)((uint8 *)hdr + hdr->hdr.len);//經典malloc實現方式,迭代下一塊

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

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

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

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

   下面寫一段代碼來測試heap_alloc函數
     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);
   下面是測試代碼運行后申請空間情況示例(地址: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
      標識塊
584
581
577
571
568

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

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

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

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





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

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

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

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

就算有點問題,應該也是數據類型定義那邊修改一下就可以了。

作者: men007    時間: 2025-4-26 11:45
下載學習,謝謝分享!




歡迎光臨 (http://m.zg4o1577.cn/bbs/) Powered by Discuz! X3.1
主站蜘蛛池模板: 欧美一级电影免费观看 | 成人黄色av网站 | 日韩免费av | 日韩成人影院在线观看 | 中文在线一区二区 | 日韩一区欧美一区 | 亚洲交性 | 国产成人在线一区二区 | 中文字幕亚洲精品 | 91九色porny首页最多播放 | 国产激情一区二区三区 | 国产精品永久免费 | 亚洲毛片在线观看 | 高清免费在线 | 亚洲国产欧美国产综合一区 | 九九天堂网 | 污视频免费在线观看 | 国产一区黄色 | 日韩成人免费中文字幕 | 美女视频黄的免费 | 欧美日韩电影一区二区 | 盗摄精品av一区二区三区 | 国产精品久久久久久久久久久久久 | 国产高清精品一区二区三区 | 国产综合视频 | 免费的一级视频 | 91九色在线观看 | 手机在线一区二区三区 | 精品国产一区二区国模嫣然 | 视频精品一区二区三区 | 亚洲国产成人av好男人在线观看 | 黄色a视频 | 亚洲视频免费一区 | 亚洲高清视频一区 | 日韩精品免费 | 国产视频一区二区在线观看 | 正在播放国产精品 | 国产精品午夜电影 | 久久99久久久久 | 亚洲成人一区 | 精品国产伦一区二区三区观看说明 |