當(dāng)前位置:首頁 > 嵌入式 > 嵌入式教程
[導(dǎo)讀]Keil C動態(tài)內(nèi)存管理機制分析及改進(jìn)

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

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

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

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

   

    #define AVAIL(__meM_avaiL_[O])
    全局?jǐn)?shù)組__meM_ avail_實際也是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)失敗時將返回0,成功則返回一1,參數(shù)pool指向應(yīng)用程序定義的堆空間,參數(shù)size為堆空間的字節(jié)數(shù)。如果應(yīng)用程序提供的堆空間太小(size的值太小),將失去實際意義,故函數(shù)將返回0表示失敗。當(dāng)size參數(shù)足夠大,則會初始化AVAIL(即_mem_avail__[O]),使其next域指向pool參數(shù)所指向的堆空間,len域為pool參數(shù)所指向的堆空間的總字節(jié)數(shù)size。其在KeilC 7.5A庫中init_mem.C的源代碼如下:
   

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

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

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

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

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

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

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

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

    設(shè)鏈表節(jié)點p指向所找到的空閑內(nèi)存塊,如果在p空閑塊分配size個字節(jié)后,剩余的字節(jié)數(shù)不多,則將p塊從AVAIL鏈表中刪除,然后返回一個指向p塊偏移sizeof(struct__mem)處的指針。如果在p空閑塊分配size個字節(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é)點是按其所指空閑內(nèi)存塊開始地址的大小按升序排列的。free函數(shù)的算法是在AVAIL鏈表中查一個節(jié)點p(其前驅(qū)為q),當(dāng)p節(jié)點所指空問內(nèi)存塊的地址大于參數(shù)memp所指內(nèi)存塊的起始地址時,則將memp塊插入到該節(jié)點之前,如沒有找到這樣的節(jié)點,則memp塊插到鏈尾。在插入memp塊時,還將檢查在memp塊的前后是否存在地址相鄰的空閑內(nèi)存塊,如果有,則將memp塊與相鄰塊合并。(free庫函數(shù)的部分源代碼見本刊網(wǎng)站——編者注)

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

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

    筆者特在Keil C7.5 A版中設(shè)計了一個示例(見本刊網(wǎng)站),用于引發(fā)該錯誤。要防止這種錯誤,只需將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)。

    編者注:本文為期刊縮略版,全文見網(wǎng)站www.mesnet.com.cn。

本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫毥谦F公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動 BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運行,同時企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險,如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機 衛(wèi)星通信

要點: 有效應(yīng)對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅持高質(zhì)量發(fā)展策略,塑強核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運營商 數(shù)字經(jīng)濟

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術(shù)學(xué)會聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(shù)(集團)股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉