Utilisation de malloc dans PIC


10

Comment puis-je utiliser malloc()et free()fonctionner dans un PIC? J'ai vérifié l'en- stdlib.htête et il n'y en a aucune mention. J'utilise MCC18.

Quelqu'un a-t-il dû les utiliser?

J'en ai besoin car je porte une bibliothèque de Windows XP vers le PIC. Le guide de portage dit de

adapter les fonctions spécifiques du système d'exploitation à mes PIC

Mais je ne sais pas "traduire" les fonctions malloc()et free().


4
Essayez d'utiliser l'allocation statique si possible.
Nick T

1
Pourquoi? Le problème est que j'écris la couche inférieure (la plate-forme spécifique) d'une bibliothèque assez énorme et beaucoup de fonctions dont je n'ai aucune idée de la raison pour laquelle elles l'utilisent .. et je n'ai aucune idée de comment changer dynamique à statique ..
stef

11
Cela ressemble à un microcontrôleur PIC avec <4 Ko de RAM peut être incorrect pour votre application. Sur un PC, mesurez l'utilisation de la mémoire de votre bibliothèque PC avant de commencer un port. Vous pourriez être mieux avec quelque chose de plus costaud comme un ARM Cortex-M3. Règle générale: si la base de code que vous portez est trop grande pour être comprise, elle ne rentrera pas dans un PIC.
Toby Jaffey

Les pilotes Windows (et les applications en général) sont essentiellement écrits avec un paradigme de «RAM illimitée», car si la RAM physique est épuisée, la mémoire virtuelle peut être échangée. Selon ce que fait la bibliothèque, elle peut très bien consommer plus que les 4 Ko. disponible dans votre PIC18F87J11. Je soupçonne que vous ne pourrez pas évaluer la quantité de mémoire que le pilote va utiliser.
Adam Lawrence

Autre problème potentiel: un entier Win32 est de 32 bits, alors qu'avec le compilateur MCC18, il n'est que de 16 bits. Vous pourriez avoir des problèmes de débordement étranges si vous ne faites pas attention.
Adam Lawrence

Réponses:


8

Dans de nombreuses applications, il faudra allouer de la mémoire, mais pas besoin de libérer quoi que ce soit tout en conservant quelque chose qui a été alloué après. Sur un tel système, il suffit d'utiliser l'éditeur de liens pour définir un tableau en utilisant toute la RAM disponible, définir un pointeur sur le début de ce tableau, puis utiliser une fonction malloc facile et agréable:

char * next_alloc;
void * malloc (taille int)
{
    char * this_alloc;
    this_alloc = next_alloc;
    if ((END_OF_ALLOC_SPACE - this_alloc) <taille)
      return -1;
    next_alloc + = taille;
    return this_alloc;
}
sans vide (void * ptr)
{
    if (ptr)
        next_alloc = (char *) ptr;
}

Agréable et facile, et juste deux octets au total pour tout nombre d'allocations. Appeler free () sur un bloc désallouera ce bloc et tout ce qui le suivra.

Des modèles d'allocation légèrement plus compliqués peuvent être gérés à l'aide de deux pointeurs - l'un qui alloue des éléments du bas de la mémoire vers le haut, et l'autre allant du haut de la mémoire vers le bas. Il est également possible d'utiliser un récupérateur de place compacté si les données du tas sont homogènes et que l'on sait où se trouvent toutes les références externes.


Pour être complètement informé des possibilités, lisez la réponse sur electronics.stackexchange.com/questions/7850/…
gavioto20

14

malloc()dans les microcontrôleurs est généralement considéré comme une "mauvaise chose". Mais, si vous en avez absolument besoin, vous voudrez trouver une version tierce.

Si vous avez de la chance, le code que vous portez peut ne pas dépendre de la réutilisation de blocs de mémoire. Si tel est le cas, vous pouvez écrire un allocateur simple qui renvoie un pointeur dans un tampon RAM, puis avance le pointeur de la taille de bloc demandée.

J'ai déjà utilisé cette approche avec succès dans le portage de bibliothèques PC vers des microcontrôleurs.

Ci-dessous, vous devez configurer l'allocateur avec my_malloc_init()et allouer de la mémoire avec my_malloc(). my_free()est là pour satisfaire la dépendance mais ne fait rien. Finalement, vous manquerez d'espace, bien sûr.

Pour que cela fonctionne, vous devrez mesurer la mémoire la plus défavorable de votre code (faites-le sur un PC si possible), puis configurez-la en HEAP_SIZEconséquence. Avant d'entrer dans la partie de votre bibliothèque nécessitant une mémoire dynamique, appelez my_malloc_init(). Avant de réutiliser, assurez-vous que rien ne pointe toujours heap.

uint8_t heap[HEAP_SIZE];
uint8_t *heap_ptr;

void my_malloc_init(void)
{
    heap_ptr = heap;
}

void *my_malloc(size_t len)
{
    uint8_t *p = heap_ptr;
    heap_ptr += len;
    if (heap_ptr >= heap + HEAP_SIZE)
        return NULL;
    else
        return p;
}

void my_free(void)
{
    // do nothing
}

(Remarque: dans le monde réel, vous devrez peut-être envisager l'alignement du pointeur, c'est-à-dire arrondir à heap_ptr2 ou 4 octets)

Une autre option consiste à utiliser une structure d'allocation plus simple que celle malloc()fournie habituellement, comme une FreeList , bien que cela ne vous permette pas d'allouer des blocs de taille variable.


3
Je me demande quand malloc IS est considéré comme une bonne chose en embarqué.
Kellenjb

1
Je suis toujours d'accord pour dire que vous ne voulez pas d'allocation dynamique dans les programmes, comme d'autres l'ont dit, mais c'est une excellente façon de procéder. Malloc tiers conçu pour embarqué est de loin le meilleur choix. Éviter la segmentation est un must. @jobyTaffey Bien écrit.
Kortuk

1
@Kellenjb bien, c'est une toute nouvelle question :-)
Toby Jaffey

1
Je suggérerais que my_free définisse heap_ptr sur la valeur transmise, libérant efficacement l'élément indiqué et tout ce qui lui est alloué. Évidemment, il faut allouer les choses dans une séquence qui permet une telle utilisation, mais de tels modèles ne sont pas rares. Une autre variante utile consiste à avoir deux paires de fonctions alloc / free, dont l'une alloue de haut en bas et l'autre alloue de bas en haut.
supercat

13

Ce n'est guère une réponse à votre question, mais l'allocation dynamique de mémoire est généralement mal vue sur les petits environnements RAM et en l'absence d'un système d'exploitation (par exemple dans le monde des microcontrôleurs) ... l'espace de stockage dont vous disposez dans un environnement intégré est généralement mesuré en centaines d'octets ...

L'implémentation de malloc et de free est essentiellement la maintenance d'une liste chaînée de structures de "segment libre", et comme vous pouvez l'imaginer, les métadonnées associées aux segments libres ne sont pas négligeables par rapport à la quantité de mémoire généralement disponible ... c'est-à-dire la "surcharge" "La gestion d'un pool de mémoire dynamique consomme une quantité importante des ressources disponibles.


Il existe des implémentations où la surcharge de métadonnées est très petite. Pour un bloc alloué, vous n'avez besoin que de sa taille. Pour les blocs inutilisés, le (s) pointeur (s) de liste liée peut généralement tenir gratuitement, même avec des tailles de bloc minimales assez raisonnables.
Rétablir Monica le

Le problème avec les petits systèmes de longue durée qui utilisent des microcontrôleurs ne concerne généralement pas les métadonnées, mais la fragmentation de la mémoire. Pire encore, de petites modifications de votre code peuvent introduire une fragmentation de la mémoire là où il n'y en avait pas auparavant, de sorte que vous pouvez apporter une modification d'apparence innocente qui fait soudainement que votre programme cesse de fonctionner "bien trop tôt".
Rétablir Monica le

11

Je ne sais pas si la bibliothèque standard C18 prend en charge mallocet free, mais Microchip App Note AN914 montre comment vous pouvez implémenter la vôtre.

Dans tous les cas, Thomas et d'autres affiches ont suggéré que l'utilisation de la mémoire dynamique sur les PIC avec leur très petit espace RAM est lourde de dangers. Vous pouvez rapidement manquer d'espace contigu en raison du manque de gestionnaires de mémoire virtuelle plus avancés que les systèmes d'exploitation complets vous offrent, entraînant des allocations et des plantages défaillants. Pire encore, cela peut ne pas être déterministe et sera probablement difficile à déboguer.

Si ce que vous faites est vraiment déterminé dynamiquement au moment de l'exécution (rare pour la plupart des choses intégrées), et que vous n'avez besoin d'allouer de l'espace qu'à quelques occasions très spéciales , je pourrais voir mallocet freeêtre acceptable.


Le manque d'espace contigu, alias la fragmentation de tas, est un problème qui est totalement indépendant de la taille de votre espace d'adressage et de votre mémoire virtuelle. Vous voudrez peut-être échanger de la RAM gaspillée pour une fragmentation de segment de mémoire plus faible, mais finalement, sur un système à long terme, vous n'avez aucune garantie de ne pas manquer d'espace de segment de mémoire. La seule différence entre les petits et les gros systèmes est celle du temps nécessaire au disque pour commencer le thrashing (sur les systèmes avec une machine virtuelle paginée sur le disque), ou l'allocateur pour retourner NULL (sur les éléments intégrés).
Rétablir Monica le

@KubaOber: Il est généralement possible de garantir qu'une certaine taille de RAM sera capable de gérer n'importe quelle séquence d'opérations d'allocation et de libération qui n'ont jamais besoin de plus qu'une certaine (plus petite) quantité de RAM à allouer simultanément. Le problème avec les systèmes embarqués est que le succès garanti, même dans le pire des cas, nécessitera beaucoup plus de RAM que ce qui serait nécessaire sans fragmentation.
supercat

@supercat Vous avez raison. J'étais vraiment trop dramatique. Il existe des preuves formelles de ces garanties.
Rétablir Monica le

2

Eh bien, quelle est la taille de votre PIC en termes de mémoire?

malloc est une façon très inefficace d'allouer de la mémoire. Le problème avec cela est que la mémoire peut devenir fragmentée avec des libérations fréquentes et des mallocs et avec seulement quelques kilo-octets de mémoire, les échecs d'allocation sont trop fréquents. Il est très probable que si vous utilisez une puce plus petite ou un PIC18 antérieur, il n'y a pas de support pour malloc, car Microchip le considérait comme très difficile à mettre en œuvre (ou peut-être même impossible dans certains cas) ou pas assez utilisé pour qu'il soit ça vaut le coup. Sans parler de cela, mais c'est aussi assez lent, vous regardez 1 cycle pour utiliser un tampon statique déjà disponible et 100 à 1 000 cycles pour faire un malloc.

Si vous souhaitez allouer statiquement, créez des choses comme un tampon pour vos fonctions sprintf (le cas échéant, environ 128 octets), un tampon pour votre carte SD (le cas échéant), et ainsi de suite, jusqu'à ce que vous supprimiez le besoin de malloc. Idéalement, vous ne l'utilisez que là où vous en avez absolument besoin et ne pouvez pas vous passer de l'allocation statique, mais ces situations sont généralement rares et peuvent être un signe que vous devriez envisager des microcontrôleurs plus gros / plus puissants.

Et si vous développez / portez un "système d'exploitation" sur le PIC18 et s'il prend en charge les microcontrôleurs, il prend probablement en charge l'allocation statique. Par exemple, SQLite3 prend en charge l'allocation statique - vous lui allouez un grand tableau de tampons et il l'utilise dans la mesure du possible, même si ce n'est pas pour les microcontrôleurs. Si ce n'est pas le cas, êtes-vous sûr qu'il est conçu pour un petit PIC18?


Je vois ce que tu veux dire .. J'utilise le PIC18F87J11, qui a 128 Ko de RAM, cela peut-il être suffisant?
stef

Stefano, cette puce a 3 904 octets de RAM. Il a 128K de mémoire flash de programme.
W5VO

@Stefao Salati - 3,8 Ko est minuscule.
Thomas O

Bien désolé .. pensez-vous que cela peut être suffisant de toute façon?
stef

@Stefano Salati, pas besoin de s'excuser. Je pense que vous le pousseriez vraiment. Cela pourrait fonctionner, mais cela réduirait considérablement la performance et la mémoire disponible.
Thomas O

2

Si vous envisagez malloc()et free()pour votre logiciel intégré, je vous suggère de jeter un œil à uC / OS-II et OSMemGet()et OSMemPut(). Alors malloc()que vous allouez un bloc de mémoire arbitraire, OSMem*()donnez-vous un bloc de taille fixe à partir d'un pool pré-alloué. Je trouve cette approche un bon équilibre entre la flexibilité malloc()et la robustesse de l'allocation statique.


0

AFAIK, pour le faire correctement, vous devez vraiment regarder un appareil avec une sorte d'unité de gestion de la mémoire (MMU). Bien qu'il existe des mécanismes d'allocation dynamique pour la série PIC18, ils ne seront pas vraiment solides - et parlant comme quelqu'un qui a travaillé sur un firmware qui repousse les limites de la série PIC18, je peux dire que vous n'allez pas obtenir une application importante là-dedans si vous dépensez tous les frais généraux sur un gestionnaire de mémoire.

Meilleure solution: essayez de comprendre ce qu'elle fait et pourquoi elle a besoin d'une allocation dynamique. Voyez si vous ne pouvez pas le re-factoriser pour qu'il fonctionne avec l'allocation statique. (Il se peut que ce ne soit tout simplement pas possible - si la bibliothèque / application est conçue pour faire quelque chose qui évolue librement ou qui n'a pas de limites de la quantité d'entrée qu'elle peut accepter.) Mais parfois, si vous pensez vraiment sur ce que vous essayez de faire, vous pourriez trouver qu'il est possible (et peut-être même assez facile) d'utiliser l'allocation statique à la place.


1
Vous vous trompez. Une MMU vous permet d'interfacer avec la mémoire externe (probablement plus que les 4 Ko sur le PIC). Il y a très peu de différence dans l'allocation dynamique et statique avec et sans MMU. Une fois que vous commencez à entrer dans la mémoire virtuelle, il y a une différence, mais ce n'est que tangentiellement lié à malloc.
Kevin Vermeer

1
Les premiers programmeurs Macintosh utilisaient assez souvent malloc () et free () (ou leurs équivalents Pascal), malgré le fait que les premiers ordinateurs Macintosh n'avaient pas de MMU. L'idée que "correctement" l'utilisation de malloc () nécessite une MMU me semble incorrecte.
davidcary
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.