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

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 4160|回復: 8
打印 上一主題 下一主題
收起左側(cè)

TI OSAL內(nèi)存管理實現(xiàn)方式

[復制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:351097 發(fā)表于 2020-1-15 00:53 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
本帖最后由 沒有你 于 2020-1-16 00:25 編輯

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

//默認小塊數(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;//標志位表示本快是否已經(jīng)被使用
} heapMemHdrHdr_t;

typedef union {
  //因此,當halDataAlign\u t小于UINT16時,編譯器強制結(jié)構(gòu)對齊最大的元素,而不會在目標上浪費空間。
  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)標志 0x01:踢出
說明,每個all_heap元素占用2個字節(jié),即16個bit,最高位bit代表使用狀態(tài),1表示非free,0表示free。剩下15個bit可以表示32768個byte堆空間,高達32K了,應付51單片機是完全沒有問題的。定義了一個全局數(shù)組變量all_heap作為堆總空間,大小為2048個byte。可以根據(jù)單片機資源自行修改堆總空間大小,宏HEAPMEM_SMALLBLK_HDRCNT 是小塊內(nèi)存和大塊內(nèi)存的分界數(shù)值。

下面是堆空間初始化函數(shù)
void heap_init(void)
{
  all_heap[HEAP_LASTBLK_IDX].val = 0;//在堆的末尾設(shè)置一個空塊,以便與零進行快速比較
  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)存地址空間示例(初始地址是某次上電隨機出現(xiàn)的)
use
value
地址
對應數(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的小塊空間,每次有小塊空間申請成功,這個剩余值就會減少。藍色標注的是小塊內(nèi)存和大塊內(nèi)存的格擋板,位置為all_heap[292],狀態(tài)標為1表示已使用,這樣在小塊內(nèi)存在分配內(nèi)存的時候就不會合并到大塊內(nèi)存上面。all_heap[293]存放大塊空間首部,這個首部有大塊空間剩余量,1460表示可以使用1460個byte的大塊空間,每次有大塊空間申請成功,這個剩余值就會減少。堆末尾all_heap[1023]值被初始化為0,以便與零進行快速比較。從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; //給需要申請的空間分配一個管理首部
//進入臨界區(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;//否則,標記coal為1,告訴下一塊,本快是free的
        prev = hdr; //保存當前內(nèi)存地址
      }
    }
    //(uint8 *)hdr這個操作使本來2個字節(jié),強制轉(zhuǎn)換成1個字節(jié)
    hdr = (heapMemHdr_t *)((uint8 *)hdr + hdr->hdr.len);//經(jīng)典malloc實現(xiàn)方式,迭代下一塊

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

  if ( hdr != NULL )//已經(jīng)找到合適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; //標記本塊已經(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);
   下面是測試代碼運行后申請空間情況示例(地址: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態(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;
  //進入中斷臨界
  hdr->hdr.inUse = FALSE; //標記使用狀態(tài)為:未使用

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

    比如執(zhí)行heap_free(test1),傳入地址為0x036f,執(zhí)行過程中會將0x036d的狀態(tài)標為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
      標識塊
584
581
577
571
568
    上面的示例是小內(nèi)存的管理過程,大內(nèi)存的管理也是一樣的過程,這里就不列舉了。
  在實際項目中,搭載TI OSAL的ble芯片或者zibee芯片運行都非常穩(wěn)定,這也要歸功于OSAL高效可靠的內(nèi)存管理。非常值得研究和借鑒!




評分

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

查看全部評分

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

使用道具 舉報

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

使用道具 舉報

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

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

使用道具 舉報

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

使用道具 舉報

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

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

使用道具 舉報

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

使用道具 舉報

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

我在IAR平臺調(diào)試的
回復

使用道具 舉報

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

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

使用道具 舉報

9#
ID:1063561 發(fā)表于 2025-4-26 11:45 | 只看該作者
下載學習,謝謝分享!
回復

使用道具 舉報

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

本版積分規(guī)則

小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機教程網(wǎng)

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 天天干天天操天天射 | 在线视频一区二区三区 | 福利片在线观看 | 中国一级特黄毛片大片 | 成人精品毛片国产亚洲av十九禁 | 久久成人精品视频 | 国产精品国色综合久久 | 日本久久黄色 | 色婷婷久久久久swag精品 | 亚洲精品在线免费观看视频 | 国产成人精品一区二区 | 国产一区二区在线播放视频 | 中文字幕精品一区久久久久 | 中文字幕人成乱码在线观看 | 日韩高清国产一区在线 | 国产视频在线观看一区二区三区 | 久久精品国产亚洲 | 日韩激情网 | 国产福利91精品 | 99视频入口| 欧美一区二区免费 | 国产精品毛片在线 | 久久只有精品 | 亚洲激情第一页 | 综合久久综合久久 | 国产一区二区三区四区五区加勒比 | 亚洲成人综合社区 | 国产精品国产亚洲精品看不卡15 | 婷婷综合 | 国产精品一二三区 | 久久久久亚洲精品 | www.男人天堂.com | 国产日韩欧美 | www.yw193.com| 中文字幕第十五页 | a视频在线 | 美国十次成人欧美色导视频 | 欧美一级淫片免费视频黄 | 国产精品自产拍在线观看蜜 | 欧美激情综合五月色丁香小说 | 久久精品在线免费视频 |