2016-08-22 2 views
3

J'écris un logiciel embarqué pour STM32F7 et ma libc est newlib-2.4.0.20160527.malloc() dans newlib: gaspille-t-il de la mémoire après une grosse erreur?

J'ai mis en _sbrk() comme suit:

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; 
} 

Puis, quand je fais ce qui suit:

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

tout fonctionne bien (à savoir, malloc retourne deux fois non nul).

Mais quand je fais ce qui suit (à des fins de test):

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

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

     break; 
    } 
} 

chaque malloc() échoue, même malloc(0) (à savoir, __BKPT() est jamais atteint). Donc, il n'y a pas de mémoire allouée sur le tas en fait (je n'ai pas eu mem != 0 donc je ne peux même pas free() quelque chose) et il n'y a pas non plus de mémoire disponible.

Je me attendais malloc() à l'échec pour chaque sz > 0x40000 et réussir à chaque sz <= 0x40000 (en supposant free() fonctionne très bien après chaque malloc()).

Ai-je manqué quelque chose, ou est-ce un bug ou un comportement prévu dans newlib?

+0

Que dit le débogueur? Avez-vous franchi le code? Remarque: l'utilisation de l'allocation de mémoire dynamique à base de tas comme 'malloc' dans les systèmes embarqués est le plus souvent une mauvaise idée et rejetée par de nombreuses normes de codage pour de bonnes raisons. Comportement notablement déterministe et allocation garantie. Évaluer l'utilisation de pools ou d'autres mesures comme des variables statiques avant même de penser à 'malloc' etc! – Olaf

+0

Oh, et utilisez la macro 'NULL' avec des pointeurs. '0' comme constante de pointeur nul est valide, mais une mauvaise habitude des programmeurs C++. C++ 11 a introduit 'nullptr' pour de bonnes raisons. (Le vœu C11 les avait suivi) – Olaf

+0

J'ai parcouru mon code (j'ai vérifié les valeurs de 'mem';' __BKPT() 'était aussi un point d'arrêt). Pour entrer dans le code de newlib avec gdb je le recompile avec '-g3 -O0' maintenant. Je veux travailler 'snprintf' qui repose sur' malloc'. Devrais-je commencer à chercher des alternatives 'snprintf'? Je n'ai pas besoin de 'malloc' pour autre chose. –

Répondre

4

Le de newlib ne fonctionne pas correctement lors de l'allocation de la mémoire entière du tas en raison d'une mauvaise routine malloc_extend_top() dans newlib/libc/stdlib/mallocr.c:2137. Après un appel réussi à _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; 

il essaie de calculer la correction pour tenir l'alignement de la page:

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

La correction est toujours positive, car même si l'allocation correspond parfaitement, il tente d'affecter le prochaine page entière. Par exemple, si la taille de la page est 4096 et brk + sbrk_size = 4096*n, l'expression 4096 - ((brk + sbrk_size) & 4095) donnera 4096, donc la page vide suivante est requise, mais il n'y a pas d'espace pour cela.

La routine ne gère pas correctement cette situation et ne laisse que des données allouées (valeur brk), ce qui entraîne une allocation de tas entière permanente "irrécupérable". Un tel gâchis :-)

+2

Peut-être que vous devriez ouvrir un rapport de bug pour cela? –

+0

+1 parce que j'utilise beaucoup newlib malloc. Dans ce cas, 'front_misalign' sera toujours égal à zéro (car l'adresse du bloc et la taille de l'en-tête sont toutes les deux alignées). 'correction' sera en effet 4096, mais plus bas dans le code c'est' sbrk'ed, ce qui échouera, donc 'correction' sera remis à zéro et' sbrked_mem' (le global actuel) restera le même. Il me semble que le vrai problème est que lorsque la correction échoue, il définit 'new_brk = brk', donc plus bas vous obtiendrez un zéro' top_size' qui sera passé à 'set_head', ce qui se traduira par un bloc de tête de taille zéro échoue les allocations supplémentaires (ne peut pas être étendu). –