2016-08-22 144 views
3

我正在爲STM32F7編寫一個嵌入式軟件,我的libc是newlib-2.4.0.20160527。newlib中的malloc():一次大的故障分配後會浪費內存嗎?

我已經實現_sbrk()如下:

extern intptr_t g_bss_end; /* value after the last byte in .bss */ 
extern intptr_t g_msp_lim; /* stack buffer starts at this address */ 

intptr_t _sbrk(ptrdiff_t heap_incr) 
{ 
    static intptr_t heap_end = 0; 

    intptr_t prev_heap_end; 
    intptr_t new_heap_end; 

    if(heap_end == 0) { 
     heap_end = (intptr_t)&g_bss_end; 
    } 

    prev_heap_end = heap_end; 
    new_heap_end = prev_heap_end + heap_incr; 

    if(new_heap_end >= g_msp_lim) { 
     errno = ENOMEM; 

     return -1; 
    } 

    heap_end = new_heap_end; 

    return prev_heap_end; 
} 

然後,當我做到以下幾點:

/* total capacity of my heap is 0x40000 */ 
void * mem = malloc(0x40000); 
free(mem); mem = 0; 
mem = malloc(0x40000); 

一切正常(即malloc的返回非零的兩倍)。

但是,當我這樣做(用於測試目的):

for(int32_t sz = 0x50000; sz >= 0; sz--) { 
    void * mem = malloc(sz); 

    if(mem != 0) { 
     __BKPT(); 
     free(mem); 

     break; 
    } 
} 

malloc()失敗,甚至malloc(0)(即__BKPT()從未達到)。所以,實際上並沒有在堆上分配內存(我沒有得到任何mem != 0,所以我什至不能free()東西),也沒有可用的內存。

我預計malloc()失敗,每sz > 0x40000併爲每sz <= 0x40000(假設每個free()malloc()正常工作)取得成功。

我錯過了什麼,或者這是bug或在newlib中的預期行爲?

+0

調試器說什麼?你是否已經完成了代碼?注意:在嵌入式系統中使用基於堆的動態內存分配像'malloc'通常是一個壞主意,許多編碼標準出於正當理由不允許使用這種分配。值得注意的是確定性行爲和保證分配。在甚至考慮'malloc'等之前,先評估池或其他度量方法(如靜態變量)的使用! – Olaf

+0

哦,並用指針的'NULL'宏。 '0'作爲空指針常量是有效的,但是C++程序員的壞習慣。出於好的理由,C++ 11引入了'nullptr'。 (希望C11跟着他們) – Olaf

+0

我通過我的代碼(我檢查了'mem'的值;'__BKPT()'也是一個斷點)。爲了使用gdb進入newlib的代碼,我現在用'-g3 -O0'重新編譯它。我想要工作'snprintf',它依賴'malloc'。我應該開始搜索'snprintf'選項嗎?我不需要'malloc'來做其他事情。 –

回答

4

由於在newlib/libc/stdlib/mallocr.c:2137中的例行程序出錯,分配整個堆內存時,newlib的malloc()無法正常工作。

/* Guarantee alignment of first new chunk made from this space */ 
front_misalign = (POINTER_UINT)chunk2mem(brk) & MALLOC_ALIGN_MASK; 
if (front_misalign > 0) 
{ 
    correction = (MALLOC_ALIGNMENT) - front_misalign; 
    brk += correction; 
} 
else 
    correction = 0; 

/* Guarantee the next brk will be at a page boundary */ 
correction += pagesz - ((POINTER_UINT)(brk + sbrk_size) & (pagesz - 1)); 

校正始終是正的,因爲即使分配完全符合它嘗試分配:成功調用_sbrk()

brk = (char*)(MORECORE (sbrk_size)); /* MORECORE = _sbrk */ 

    /* Fail if sbrk failed or if a foreign sbrk call killed our space */ 
    if (brk == (char*)(MORECORE_FAILURE) || 
     (brk < old_end && old_top != initial_top)) 
    return; 

它試圖計算修正以適應頁面對齊後下一整頁。例如,如果頁面大小爲4096brk + sbrk_size = 4096*n,則表達式4096 - ((brk + sbrk_size) & 4095)將給出4096,因此下一個空白頁是必需的,但是沒有空間。

例行程序不正確地處理這種情況,並留下剛剛分配的數據(brk值),導致永久的「不可用」整個堆分配。這樣的浪費:-)

+2

也許你應該爲此打開一個錯誤報告? –

+0

+1因爲我使用newlib malloc很多。在這種情況下,'front_misalign'將始終爲零(因爲塊地址和標頭大小都是對齊的)。 'correction'確實是4096,但是在代碼中進一步下降,這將會失敗,所以'correction'將被重置爲零,'sbrked_mem'(實際的全局)將保持不變。在我看來,真正的問題是,當校正失敗時,它會設置'new_brk = brk',所以進一步下來,你會得到一個零'top_size',它被傳遞給'set_head',從而產生一個零大小的頭部塊未能進一步分配(無法擴展)。 –