2016-08-22 2 views
3

STM32F7 용 임베디드 소프트웨어를 작성 중이며 libc는 newlib-2.4.0.20160527입니다. 다음과 같이 내가 _sbrk()을 구현 한newlib의 malloc() : 하나의 큰 실패 할당 후에 메모리를 낭비합니까?

: 나는 다음 작업을 수행 할 때

다음
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을 두 번 비제로 반환). 내가 (테스트 용) 다음 작업을 수행 할 때

그러나 : 모든 malloc() 실패

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

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

     break; 
    } 
} 

, 심지어 malloc(0) (즉, __BKPT()입니다 도달하지 못했다). 그래서 실제로 힙에 할당 된 메모리가 없습니다 (나는 mem != 0을 얻지 못했고 따라서 free() 뭔가도 없을 수 있습니다) 또한 사용 가능한 메모리도 없습니다.

나는 모든 sz > 0x40000 실패와 sz <= 0x40000이 ( free()를 가정하는 것은 각 malloc() 후 잘 작동) 모든 성공하여야 malloc()을 기대했다.

나는 뭔가를 놓친 적이 있습니까, 아니면 newlib에서 버그입니까?

+0

디버거는 무엇을 말합니까? 코드를 단계별로 실행 했습니까? 참고 : 임베디드 시스템에서'malloc '과 같은 힙 기반 동적 메모리 할당을 사용하는 것은 나쁜 아이디어이며 좋은 이유 때문에 많은 코딩 표준에 의해 허용되지 않습니다. 특히 결정 론적 행동과 보장 된 배정. 정적 변수와 같은 풀 또는 다른 측정 값의 사용을 평가하기 전에 'malloc'등에 대해서 생각해보십시오! – Olaf

+0

아, 그리고 포인터와 함께'NULL' 매크로를 사용하십시오. null 포인터 상수로 '0'은 유효하지만 C++ 프로그래머에게는 나쁜 습관입니다. C++ 11은 좋은 이유로 nullptr을 도입했습니다. (소원 C11이 따라 갔다.) – Olaf

+0

나는 (mem의 값을 검사하고, __BKPT()도 중단 점이었다) 코드를 밟았다. gdb로 newlib의 코드를 가져 오려면 지금 그것을'-g3 -O0'으로 다시 컴파일하고 있습니다. 'malloc '에 의존하는'snprintf'작업을하고 싶습니다. 'snprintf '대안 검색을 시작해야합니까? 나는 다른 것을 위해 malloc을 필요로하지 않는다. –

답변

4

malloc_extend_top() 루틴으로 인해 전체 힙 메모리를 할당 할 때 newlib의 malloc()이 올바르게 작동하지 않습니다. newlib/libc/stdlib/mallocr.c:2137. 할당이 완벽하게 맞는 경우에도 때문에

/* 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; 

에 성공적으로 호출 후 다음 전체 페이지. 예를 들어, 페이지 크기가 4096이고 brk + sbrk_size = 4096*n 인 경우, 표현식 4096 - ((brk + sbrk_size) & 4095)4096으로 표시되므로 다음 빈 페이지가 필요하지만 공백이 없습니다.

루틴은이 상황을 제대로 처리하지 못하고 할당 된 데이터 (brk 값)를 남기므로 영구적 인 "unfreeable"전체 힙 할당이 발생합니다. 그런 낭비 :-)

+2

아마도 이것에 대한 버그 보고서를 열어야합니까? –

+0

+1 왜냐하면 newlib malloc을 많이 사용하기 때문입니다. 이 경우'front_misalign'은 항상 청크 주소와 헤더 크기가 모두 정렬되기 때문에 0이됩니다. 'correct'는 실제로 4096이 될 것이지만 코드에서 더 아래로 내려 가면'sbrk'ed되고 실패하면'correction'은 0으로 재설정되고'sbrked_mem' (실제 전역)은 그대로 유지됩니다. 실제 문제는 교정이 실패했을 때'new_brk = brk'을 설정했기 때문에'set_head'에 전달되는 'top_size'가 0이되어 결과적으로 크기가 0 인 헤드 블록이 생성됩니다. 추가 할당이 실패합니다 (확장 할 수 없음). –