0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Keil C的應(yīng)用動(dòng)態(tài)存儲(chǔ)管理的原理和實(shí)現(xiàn)方法分析

牽手一起夢 ? 來源:單片機(jī)與嵌入式系統(tǒng)應(yīng)用 ? 作者:丁明亮,熊真春 ? 2020-10-04 14:00 ? 次閱讀

Keil C是常用的嵌入式系統(tǒng)編程工具,它通過init_mempool、mallloe、free等函數(shù),提供了動(dòng)態(tài)存儲(chǔ)管理等功能。本文通過對init_mempool、mallloe和free這3個(gè)KeilC庫函數(shù)源代碼的分析,揭示其實(shí)現(xiàn)的原理和方法,并對其中的不足作了改進(jìn),以使Keil C編程人員更好地應(yīng)用動(dòng)態(tài)存儲(chǔ)管理。

1 相關(guān)數(shù)據(jù)結(jié)構(gòu)、變量及說明

在Keil C安裝目錄下的\c5l\lib目錄下,有實(shí)現(xiàn)init_mempool、mallloe和free這3個(gè)函數(shù)的C源文件init_mere.c、malloc.e和free.c。下面針對keil C7.5A版,將其中與動(dòng)態(tài)存儲(chǔ)管理相關(guān)的數(shù)據(jù)結(jié)構(gòu)介紹如下:

Keil C的應(yīng)用動(dòng)態(tài)存儲(chǔ)管理的原理和實(shí)現(xiàn)方法分析

該結(jié)構(gòu)的next指向堆中的下一空閑內(nèi)存塊,len表示該空閑塊除去該塊首部的struct__mem__結(jié)構(gòu)所占的字節(jié)數(shù)后,該塊實(shí)際可用的字節(jié)數(shù)。由于next是一個(gè)指向XDATA區(qū)的指針,故在Keil C中應(yīng)用程序所定義的堆空間應(yīng)在XDATA段中定義。

在Keil C中,堆中的所有空閑內(nèi)存塊是用一個(gè)單鏈表來管理的,struct_mere_即為該鏈表結(jié)點(diǎn)的結(jié)構(gòu),后面定義的宏AVAIL為該鏈表的首結(jié)點(diǎn),為敘述方便,以下將該鏈表稱為AVAIL鏈表。

#define AVAIL(__meM_avaiL_[O])

全局?jǐn)?shù)組__meM_ avail_實(shí)際也是struct__mem__類型,__mem_avail__[O]的next指向堆中首塊空閑塊。如果堆中已無空閑內(nèi)存塊,則__mem_avail__[0]的next為NULL(0值)。為使程序代碼簡潔,定義了宏AVAIL來代替__mem_avail__[O]。

2 init_mempool函數(shù)剖析

函數(shù)int_mempool(void_MALLOC_MEM_*pool,unsigned int size)失敗時(shí)將返回0,成功則返回一1,參數(shù)pool指向應(yīng)用程序定義的堆空間,參數(shù)size為堆空間的字節(jié)數(shù)。如果應(yīng)用程序提供的堆空間太?。╯ize的值太小),將失去實(shí)際意義,故函數(shù)將返回0表示失敗。當(dāng)size參數(shù)足夠大,則會(huì)初始化AVAIL(即_mem_avail__[O]),使其next域指向pool參數(shù)所指向的堆空間,len域?yàn)閜ool參數(shù)所指向的堆空間的總字節(jié)數(shù)size。其在KeilC 7.5A庫中init_mem.C的源代碼如下:

在成功執(zhí)行init_mempool函數(shù)后,將得到如圖1所示的一個(gè)數(shù)據(jù)結(jié)構(gòu)。另外,鏈?zhǔn)捉Y(jié)點(diǎn)AVAIL的len域記錄了整個(gè)堆的字節(jié)數(shù)。鏈?zhǔn)譇VAIL結(jié)點(diǎn)的next域指向的是首塊空閑塊,當(dāng)經(jīng)過多次的malloe函數(shù)而堆中投有空閑內(nèi)存塊時(shí),AVAIL結(jié)點(diǎn)的next域?qū)镹ULL值。

很明顯,從上面的if(pool==NULL){pool=1;size--;)這部分源代碼來看,如果應(yīng)用程序中pool參數(shù)為空指針(pool為0)時(shí),顯然不能直接將AVAIL,的next域的值賦為空指針的(即賦為O)。將pool的值改為1,再將size的值減l,這樣,init_mempool函數(shù)會(huì)在XDATA區(qū)中,從地址l開始,取size一1個(gè)字節(jié)作為堆來使用。如果源程序有定義在XDATA區(qū)的變量,則這些變量所占的存儲(chǔ)單元也可能會(huì)被當(dāng)成堆空間的一部分,這無疑是有潛在風(fēng)險(xiǎn)的。

部分程序員在調(diào)用init_mempool函數(shù)時(shí),習(xí)慣將pool參數(shù)設(shè)為一個(gè)形如0xAAAA數(shù)字表示的絕對地址,如果不加特別防范,也是不妥的,因?yàn)镵eil C可能會(huì)在此方式指定的堆空間中分配臨時(shí)變量。好的習(xí)慣是定義一個(gè)字節(jié)數(shù)組作為堆空間,再將數(shù)組名作為pool參數(shù)調(diào)用init_mempool函數(shù)。

在Keil C的聯(lián)機(jī)文檔中,指明了init_mempool在應(yīng)用程序中只能被調(diào)用一次,那么,如果多次調(diào)用該函數(shù)又會(huì)有什么后果呢?從該函數(shù)的源代碼來分析,多次調(diào)用init_mempoo1函數(shù),會(huì)導(dǎo)致重新初始化首結(jié)點(diǎn)AVAIL的next域和len域的值,將使AVAIL鏈表中的原有管理信息丟失,從而導(dǎo)致一些很難診斷的問題。

對此問題,可采用如下保護(hù)措施。當(dāng)發(fā)現(xiàn)AVAIL鏈表中已有管理信息時(shí),則返回失敗標(biāo)志,函數(shù)直接返回。具體的方法是檢查AVAIL結(jié)點(diǎn)的len域,由于其被初始化為零,如果發(fā)現(xiàn)其值非零,則表明init_mempool函數(shù)已被成功調(diào)用過,此時(shí)函數(shù)直接返回。

3 malloc函數(shù)分析

malloc函數(shù)的原形是void *malloc(unsigned intsize),size參數(shù)為需動(dòng)態(tài)申請的內(nèi)存塊的字節(jié)數(shù)。

malloc函數(shù)的算法是查找AVAIL鏈表中各結(jié)點(diǎn)next指針?biāo)赶虻目臻e內(nèi)存塊。如果某塊的空閑字節(jié)數(shù)≥size參數(shù),則停止查找,并從該塊進(jìn)行內(nèi)存分配,返回一個(gè)指向所分配內(nèi)存塊的指針給應(yīng)用程序。如果沒有找到符合要求的空閑內(nèi)存塊,則返回空指針給應(yīng)用程序。

需要注意的是,AVAIL鏈表中除首結(jié)點(diǎn)AVAIL外,其余各節(jié)點(diǎn)位于堆中各空閑內(nèi)存塊開始處的一個(gè)struct__mem__結(jié)構(gòu)中,其len域?yàn)樵摽臻e塊總字節(jié)數(shù)減去sizeof(stiuct__mem)后的值,即該塊實(shí)際空閑的字節(jié)數(shù);next域指向堆中下一空閑內(nèi)存塊。

設(shè)鏈表節(jié)點(diǎn)p指向所找到的空閑內(nèi)存塊,如果在p空閑塊分配size個(gè)字節(jié)后,剩余的字節(jié)數(shù)不多,則將p塊從AVAIL鏈表中刪除,然后返回一個(gè)指向p塊偏移sizeof(struct__mem)處的指針。如果在p空閑塊分配size個(gè)字節(jié)后,該塊仍剩余較多的字節(jié)數(shù),則需對該塊進(jìn)行分割,將多出的這一部分保留在AVAIL鏈表中。(以下部分有省略,全文請見本刊網(wǎng)站——編者注)

4 free函數(shù)分析及改進(jìn)

free函數(shù)的原形是void free(void xdata *memp),參數(shù)memp指向所要釋放的內(nèi)存塊。

在AVAIL鏈表中,各結(jié)點(diǎn)是按其所指空閑內(nèi)存塊開始地址的大小按升序排列的。free函數(shù)的算法是在AVAIL鏈表中查一個(gè)節(jié)點(diǎn)p(其前驅(qū)為q),當(dāng)p節(jié)點(diǎn)所指空問內(nèi)存塊的地址大于參數(shù)memp所指內(nèi)存塊的起始地址時(shí),則將memp塊插入到該節(jié)點(diǎn)之前,如沒有找到這樣的節(jié)點(diǎn),則memp塊插到鏈尾。在插入memp塊時(shí),還將檢查在memp塊的前后是否存在地址相鄰的空閑內(nèi)存塊,如果有,則將memp塊與相鄰塊合并。

值得探討的是最后一段將memp塊與前一塊(q塊)合并的這部分代碼。如果在執(zhí)行此部分代碼之前,q指向首結(jié)點(diǎn)AVAIL,而此時(shí)欲將q塊與memp塊合并,顯然是不合理的。實(shí)際上,此時(shí)應(yīng)當(dāng)將q的next指針的值設(shè)為memp塊的開始地址p0。由于KeilC7.5A中,free庫函數(shù)的源程序中沒有考慮這種特殊情況,因此可能會(huì)引發(fā)嚴(yán)重后果。

由源代碼分析可知,q指向首結(jié)點(diǎn)AVAIL,而此時(shí)如果滿足。memp塊與q塊合并的判定條件,執(zhí)行q>1en+=p0一>Len+HL,EN和q一>next=pO一>next后,不但不能回收內(nèi)存,反而導(dǎo)致memp塊丟失;同時(shí),AVAIL的len域的值也不正確。如果此時(shí)pO一>next又為NULL,則會(huì)導(dǎo)致整個(gè)堆內(nèi)存的丟失。

筆者特在Keil C7.5 A版中設(shè)計(jì)了一個(gè)示例(見本刊網(wǎng)站),用于引發(fā)該錯(cuò)誤。要防止這種錯(cuò)誤,只需將if((((char_MALLOC_MEM_*)q)+q一>len+HLEN)==pO)判定語句改為if((q!=&AVAIL)&&(((char_MALLOC_MEM_*)q)+q一>len+HLEN)==p0)即可。有興趣的可通過電子郵件與筆者聯(lián)系(cqdoml@sina.com)。

責(zé)任編輯:gt

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 嵌入式
    +關(guān)注

    關(guān)注

    5052

    文章

    18913

    瀏覽量

    300829
  • 編程
    +關(guān)注

    關(guān)注

    88

    文章

    3544

    瀏覽量

    93479
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4260

    瀏覽量

    62233
收藏 人收藏

    評論

    相關(guān)推薦

    Keil C動(dòng)態(tài)內(nèi)存管理機(jī)制分析及改進(jìn),不看肯定后悔

    Keil C動(dòng)態(tài)內(nèi)存管理機(jī)制分析及改進(jìn),不看肯定后悔
    發(fā)表于 04-25 08:48

    有什么方法可以實(shí)現(xiàn)邏輯分析儀的實(shí)時(shí)存儲(chǔ)嗎?

    有什么方法可以實(shí)現(xiàn)邏輯分析儀的實(shí)時(shí)存儲(chǔ)嗎?
    發(fā)表于 05-06 07:39

    怎樣去實(shí)現(xiàn)嵌入式裸機(jī)內(nèi)存動(dòng)態(tài)管理

    嵌入式裸機(jī)內(nèi)存動(dòng)態(tài)管理實(shí)現(xiàn)與講解(一)C 的標(biāo)準(zhǔn)庫自帶了malloc和free,為啥還要自己實(shí)現(xiàn)?標(biāo)準(zhǔn)庫的函數(shù)占用空間較大,采用本文的
    發(fā)表于 12-17 07:02

    基于對象存儲(chǔ)中的元數(shù)據(jù)組織管理方法

    提出了一種動(dòng)態(tài)分區(qū)元數(shù)據(jù)組織管理方法它混合了動(dòng)態(tài)和靜態(tài)的方法在MDS 機(jī)群中分布元數(shù)據(jù)并使用散列的技術(shù)索引元數(shù)據(jù)利用共享存儲(chǔ)來存放元數(shù)據(jù)整個(gè)
    發(fā)表于 06-28 17:03 ?53次下載
    基于對象<b class='flag-5'>存儲(chǔ)</b>中的元數(shù)據(jù)組織<b class='flag-5'>管理方法</b>

    XXTEA加密算法的KEIL C實(shí)現(xiàn)

    本內(nèi)容提供了XXTEA加密算法的KEIL C實(shí)現(xiàn),詳細(xì)列出了程序共大家學(xué)習(xí)
    發(fā)表于 08-25 17:57 ?3288次閱讀

    keil C51 uVision2安裝方法

    keil C51 uVision2安裝方法。
    發(fā)表于 11-03 10:27 ?7次下載

    利用Keil C51實(shí)現(xiàn)單片機(jī)與PC機(jī)串口通信任務(wù)2

    【LabVIEW從入門到精通】5.2.4.3 利用Keil C51實(shí)現(xiàn)單片機(jī)與PC機(jī)串口通信任務(wù)2(方法2)
    發(fā)表于 01-08 15:47 ?0次下載

    Keil-C51與MDK-ARM并存方法

    Keil-C51與MDK-ARM并存方法
    發(fā)表于 03-14 17:45 ?21次下載

    基于C/S醫(yī)療設(shè)備全過程動(dòng)態(tài)管理信息系統(tǒng)

    設(shè)計(jì)一套醫(yī)療設(shè)備的全過程動(dòng)態(tài)管理信息系統(tǒng)。方法分析系統(tǒng)的整體框架,將系統(tǒng)分成購置管理、資產(chǎn)管理
    發(fā)表于 12-05 10:57 ?0次下載
    基于<b class='flag-5'>C</b>/S醫(yī)療設(shè)備全過程<b class='flag-5'>動(dòng)態(tài)</b><b class='flag-5'>管理</b>信息系統(tǒng)

    基于對象跟蹤的動(dòng)態(tài)分析方法

    Web程序的安全威脅主要是由外部輸入未驗(yàn)證引發(fā)的安全漏洞,如數(shù)據(jù)庫注入漏洞和跨站腳本漏洞,動(dòng)態(tài)污點(diǎn)分析可有效定位此類漏洞。提出一種基于對象跟蹤的動(dòng)態(tài)分析
    發(fā)表于 01-05 16:21 ?0次下載
    基于對象跟蹤的<b class='flag-5'>動(dòng)態(tài)</b><b class='flag-5'>分析</b><b class='flag-5'>方法</b>

    Keil C51單片機(jī)變量的使用方法詳細(xì)介紹

    8051內(nèi)核單片機(jī)是一種通用單片機(jī),在國內(nèi)占有較大的市場份額。在將C語言用于51內(nèi)核單片機(jī)的研究方面,Keil公司做得最為成功。由于51內(nèi)核單片機(jī)的存儲(chǔ)結(jié)構(gòu)的特殊性,Keil
    發(fā)表于 11-24 10:20 ?4586次閱讀
    <b class='flag-5'>Keil</b> <b class='flag-5'>C</b>51單片機(jī)變量的使用<b class='flag-5'>方法</b>詳細(xì)介紹

    keil C51應(yīng)用程序和安裝方法

    keil C51應(yīng)用程序和安裝方法
    發(fā)表于 11-27 08:00 ?11次下載

    怎么樣才能使用Keil C51開發(fā)大型嵌入式程序

     結(jié)合在8051系列單片機(jī)平臺(tái)上的實(shí)際開發(fā)應(yīng)用的經(jīng)驗(yàn),介紹用 Keil C在8051單片機(jī)上進(jìn)行大型嵌入式程序開發(fā)的技術(shù)。主要闡述了大型嵌入式開發(fā)中在存儲(chǔ)器的管理、
    發(fā)表于 07-01 08:00 ?12次下載
    怎么樣才能使用<b class='flag-5'>Keil</b> <b class='flag-5'>C</b>51開發(fā)大型嵌入式程序

    Keil5中C51和MDK共存的方法(以Keil5為例)

    Keil5中C51和MDK共存的方法(以Keil5為例)
    發(fā)表于 12-03 20:21 ?75次下載
    <b class='flag-5'>Keil</b>5中<b class='flag-5'>C</b>51和MDK共存的<b class='flag-5'>方法</b>(以<b class='flag-5'>Keil</b>5為例)

    Keil MDK與Keil C51共存的方法(成功)

    Keil5+Keil4共存的方法。在網(wǎng)上搜的資料MDK與KeilC51安裝順序都搞反了,但是用此安裝方法可以成功解決了MDK與KeilC51的共存問題。所有功能完美運(yùn)行。注意事項(xiàng)因?yàn)镸DK功能比KeilC51多,所以要先安裝Ke
    發(fā)表于 12-03 20:36 ?46次下載
    <b class='flag-5'>Keil</b> MDK與<b class='flag-5'>Keil</b> <b class='flag-5'>C</b>51共存的<b class='flag-5'>方法</b>(成功)