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

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 3972|回復: 7
打印 上一主題 下一主題
收起左側

TI OSAL內存管理實現方式

[復制鏈接]
跳轉到指定樓層
樓主
ID:351097 發表于 2020-1-15 00:53 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
本帖最后由 沒有你 于 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高效可靠的內存管理。非常值得研究和借鑒!




評分

參與人數 1黑幣 +50 收起 理由
admin + 50 共享資料的黑幣獎勵!

查看全部評分

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏1 分享淘帖 頂 踩
回復

使用道具 舉報

沙發
ID:117433 發表于 2020-1-15 16:46 | 只看該作者
你寫這么多,為什么就不能讓大家都拿來就用體驗一下
回復

使用道具 舉報

板凳
ID:351097 發表于 2020-1-15 20:43 | 只看該作者
xizhe2005 發表于 2020-1-15 16:46
你寫這么多,為什么就不能讓大家都拿來就用體驗一下

兄弟,代碼都亮出了,不就可以體驗了嗎
回復

使用道具 舉報

地板
ID:117433 發表于 2020-1-17 09:31 | 只看該作者
給個現成的KEIL工程,讓我下載到我的單片機里,謝了,我不太懂原理,只是個用戶
回復

使用道具 舉報

5#
ID:351097 發表于 2020-1-17 13:14 來自手機 | 只看該作者
xizhe2005 發表于 2020-1-17 09:31
給個現成的KEIL工程,讓我下載到我的單片機里,謝了,我不太懂原理,只是個用戶

我電腦安裝keil打開沒響應,所以沒有搞keil工程。你直接把現成的代碼添加到你的keil工程里面,編譯不成問題的。
回復

使用道具 舉報

6#
ID:117433 發表于 2020-1-17 16:46 | 只看該作者
你都沒試過呀,那你怎么知道沒問題
回復

使用道具 舉報

7#
ID:351097 發表于 2020-1-17 19:09 來自手機 | 只看該作者
xizhe2005 發表于 2020-1-17 16:46
你都沒試過呀,那你怎么知道沒問題

我在IAR平臺調試的
回復

使用道具 舉報

8#
ID:351097 發表于 2020-1-17 19:24 | 只看該作者
xizhe2005 發表于 2020-1-17 16:46
你都沒試過呀,那你怎么知道沒問題

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

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

手機版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術交流QQ群281945664

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 中文一区| 免费成人在线网站 | 日韩欧美国产精品 | 日操操| 欧美精品久久久久 | 91精品国产一区二区三区 | 免费国产一区 | 精品久久久久久久久久久久久久 | 中文字幕高清 | 91亚洲视频在线 | 中文在线一区二区 | 欧美天堂在线 | 日韩久久久久 | 国产 日韩 欧美 在线 | com.国产| japanhdxxxx裸体 | 欧美激情在线精品一区二区三区 | 久久久国产精品视频 | 九九热久久免费视频 | 免费人成在线观看网站 | 91一区二区 | 91久久夜色精品国产网站 | 免费观看一级特黄欧美大片 | 亚洲免费观看视频 | 亚洲巨乳自拍在线视频 | 夜夜夜久久久 | 中国三级黄色录像 | 成人福利网站 | 日韩一区在线播放 | 亚洲精品专区 | 香蕉国产在线视频 | 日本免费在线 | 99热精品在线 | 亚洲精品国产成人 | 亚洲精品在线91 | 日韩在线免费看 | 久久99精品久久久久久 | 成人一区二区三区在线观看 | 欧美极品在线视频 | 国产精品自拍一区 | 青青久在线视频 |