Uso de malloc en PIC

10

¿Cómo puedo usar malloc()y free()funciones en un PIC? He comprobado el stdlib.hencabezado y no hay mención de ellos. Estoy usando MCC18.

¿Alguien ha tenido que usarlos?

Los necesito porque estoy transfiriendo una biblioteca de Windows XP al PIC. La guía de portabilidad dice a

adaptar las funciones específicas del sistema operativo a mis PIC

Pero no sé cómo "traducir" las funciones malloc()y free().

stef
fuente
44
Intente utilizar la asignación estática si es posible.
Nick T
1
¿Por qué? El problema realmente es que estoy escribiendo la capa inferior (la plataforma específica) de una biblioteca bastante grande y muchas funciones de las que no tengo idea para qué están usando esto ... y no tengo idea de cómo cambiar de dinámico a estático ..
stef
11
Parece que un microcontrolador PIC con <4KB RAM puede ser incorrecto para su aplicación. En una PC, mida el uso de memoria de la biblioteca de su PC antes de comenzar un puerto. Puede que esté mejor con algo más robusto como un ARM Cortex-M3. Regla general: si la base de código que está transfiriendo es demasiado grande para comprenderla, entonces no cabe en un PIC.
Toby Jaffey
Los controladores de Windows (y las aplicaciones en general) esencialmente están escritos con un paradigma de "RAM ilimitada", ya que si la RAM física se agota, la memoria virtual puede intercambiarse. Dependiendo de lo que esté haciendo la biblioteca, es muy posible que consuma más de 4kB disponible en su PIC18F87J11. Sospecho que no podrá medir la cantidad de memoria que usará el controlador.
Adam Lawrence
Otro problema potencial: un Win32 int tiene 32 bits, mientras que con el compilador MCC18, solo tiene 16 bits. Podría tener problemas de desbordamiento extraños si no tiene cuidado.
Adam Lawrence

Respuestas:

8

En muchas aplicaciones, uno necesitará asignar memoria, pero no necesitará liberar nada mientras mantiene algo que se asignó después. En dicho sistema, todo lo que uno necesita hacer es usar el enlazador para definir una matriz usando toda la RAM disponible, establecer un puntero al inicio de esa matriz y luego usar una función malloc fácil y agradable:

char * next_alloc;
nulo * malloc (tamaño int)
{
    char * this_alloc;
    this_alloc = next_alloc;
    if ((END_OF_ALLOC_SPACE - this_alloc) <tamaño)
      volver -1;
    next_alloc + = tamaño;
    devuelve this_alloc;
}
libre de vacío (vacío * ptr)
{
    si (ptr)
        next_alloc = (char *) ptr;
}

Agradable y fácil, y solo dos bytes de sobrecarga total para cualquier cantidad de asignaciones. Llamar a free () en un bloque desasignará ese bloque y todo lo que sigue.

Se pueden manejar patrones de asignación un poco más complicados mediante el uso de dos punteros: uno que asigna las cosas desde la parte inferior de la memoria hacia arriba y otra desde la parte superior de la memoria hacia abajo. También es posible usar un recolector de basura de compactación si los datos en el montón son homogéneos y uno sabe dónde están todas las referencias externas.

Super gato
fuente
Para estar completamente informado sobre las posibilidades, lea la respuesta en electronics.stackexchange.com/questions/7850/…
gavioto20
14

malloc()en microcontroladores generalmente se considera una "cosa mala". Pero, si lo necesita absolutamente, entonces querrá encontrar una versión de terceros.

Si tiene suerte, el código que está transfiriendo puede no depender de la reutilización de bloques de memoria. Si este es el caso, puede escribir un asignador simple que devuelva un puntero a un búfer de RAM, luego avance el puntero por el tamaño de bloque solicitado.

He utilizado con éxito este enfoque antes de portar bibliotecas de PC a microcontroladores.

A continuación, configuraría el asignador con my_malloc_init()y asignaría memoria con my_malloc(). my_free()está ahí para satisfacer la dependencia pero en realidad no hará nada. Eventualmente te quedarás sin espacio, por supuesto.

Para que esto funcione, necesitará medir el requisito de memoria del peor de los casos de su código (haga esto en una PC si es posible) y luego configúrelo en HEAP_SIZEconsecuencia. Antes de ingresar a la parte de su biblioteca que requiere memoria dinámica, llame my_malloc_init(). Antes de volver a usar, asegúrese de que nada apunte todavía 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
}

(nota: en el mundo real, es posible que deba considerar la alineación del puntero, es decir, redondear heap_ptren 2 o 4 bytes)

Otra opción es usar una estructura de asignación más simple que la que malloc()generalmente proporciona, como una Lista Libre , aunque esto puede no permitirle asignar bloques de tamaño variable.

Toby Jaffey
fuente
3
Me pregunto cuándo malloc se considera algo bueno en embebido.
Kellenjb
1
Todavía estoy de acuerdo en que no desea una asignación dinámica en los programas, como han dicho otros, pero esta es una excelente manera de hacerlo. Malloc de terceros diseñado para embebido es, con mucho, la mejor opción. Evitar la segmentación es imprescindible. @jobyTaffey Bien escrito.
Kortuk
1
@Kellenjb bueno, esa es una pregunta completamente nueva :-)
Toby Jaffey
1
Sugeriría que my_free debería establecer heap_ptr en el valor pasado, liberando efectivamente el elemento indicado y todo lo asignado después. Obviamente, uno debe asignar las cosas en una secuencia que permita dicho uso, pero dichos patrones no son infrecuentes. Otra variación útil es tener dos pares de funciones alloc / free, una de las cuales asigna de arriba hacia abajo y la otra de abajo hacia arriba.
supercat
13

Esta no es una respuesta a su pregunta, pero la asignación dinámica de memoria generalmente está mal vista en entornos RAM pequeños y en ausencia de un sistema operativo (por ejemplo, en el mundo de los microcontroladores) ... el espacio de almacenamiento dinámico que tiene disponible en un entorno integrado es típicamente medido en cientos de bytes ...

La implementación de malloc y free es esencialmente el mantenimiento de una lista vinculada de estructuras de "segmento libre", y como se puede imaginar, los metadatos asociados con los segmentos libres no son insustanciales en comparación con la cantidad de memoria típicamente disponible ... esa es la "sobrecarga" "de administrar una agrupación de memoria dinámica consume una cantidad significativa de los recursos disponibles.

vicatcu
fuente
Hay implementaciones donde la sobrecarga de metadatos es muy pequeña. Para un bloque asignado, solo necesita su tamaño. Para los bloques no utilizados, los punteros de la lista vinculada generalmente pueden caber de forma gratuita, incluso con tamaños de bloque mínimos bastante razonables.
Restablece a Mónica
El problema con los sistemas pequeños y de larga ejecución que usan microcontroladores no se trata generalmente de metadatos, sino de la fragmentación de la memoria. Lo que es peor, los pequeños cambios en su código pueden introducir fragmentación de memoria donde antes no había ninguno, por lo que puede hacer un cambio de aspecto inocente que de repente hace que su programa deje de funcionar "demasiado pronto".
Restablece a Mónica
11

No sé si la biblioteca estándar C18 admite mallocy free, pero la nota de aplicación de Microchip AN914 muestra cómo puede implementar la suya.

En cualquier caso, Thomas y otros pósters han sugerido que usar memoria dinámica en PIC con su espacio RAM muy pequeño está lleno de peligros. Puede quedarse rápidamente sin espacio contiguo debido a la falta de administradores de memoria virtual más avanzados que los sistemas operativos completos le brindan, lo que lleva a fallas en las asignaciones y fallas. Peor aún, puede no ser determinista, y probablemente será un dolor de depuración.

Si lo que está haciendo está realmente determinado dinámicamente en tiempo de ejecución (raro para la mayoría de las cosas incrustadas), y solo necesita asignar espacio en un par de ocasiones muy especiales , podría ver mallocy freeser aceptable.

Nick T
fuente
Quedarse sin espacio contiguo, también conocido como fragmentación de almacenamiento dinámico, es un problema totalmente independiente de cuán grande es su espacio de direcciones y si tiene memoria virtual. Es posible que desee intercambiar algo de RAM desperdiciada por una menor fragmentación de almacenamiento dinámico, pero eventualmente, en un sistema de ejecución prolongada, no tiene garantías de no quedarse sin espacio de almacenamiento dinámico. La única diferencia entre los sistemas pequeños y grandes aquí es la de cuánto tiempo tarda el disco en comenzar a agitarse (en sistemas con VM con paginación de disco), o el asignador para devolver NULL (en material incrustado).
Restablece a Mónica
@KubaOber: en general, es posible garantizar que un determinado tamaño de RAM podrá manejar cualquier secuencia de asignación y operaciones de liberación que nunca necesiten asignar más de una cierta cantidad (menor) de RAM simultáneamente. El problema con los sistemas embebidos es que garantizar el éxito incluso en el peor de los casos requerirá mucha más RAM de la necesaria sin fragmentación.
supercat
@ Supercat Tienes razón. Fui excesivamente dramático de hecho. Hay pruebas formales de esas garantías.
Restablece a Monica el
2

Bueno, ¿qué tan grande es tu PIC en términos de memoria?

malloc es una forma muy ineficiente de asignar memoria. El problema es que la memoria puede fragmentarse con frecuentes liberaciones y mallocs y con solo unos pocos kilobytes de memoria, las fallas de asignación son muy comunes. Es muy probable que si está utilizando un chip más pequeño o un PIC18 anterior, no hay soporte para malloc, ya que Microchip lo consideró muy difícil de implementar (o tal vez incluso imposible en algunos casos) o no lo utilizó lo suficiente como para que sea vale la pena. Sin mencionarlo, pero también es bastante lento, estás buscando 1 ciclo para usar un búfer estático ya disponible y de 100 a 1,000 ciclos para hacer un malloc.

Si desea asignar estáticamente, cree cosas como un búfer para sus funciones sprintf (si corresponde, alrededor de 128 bytes), un búfer para su tarjeta SD (si corresponde), y así sucesivamente, hasta que elimine la necesidad de malloc. Idealmente, solo lo usa donde lo necesita absolutamente y no puede salirse con la asignación estática, pero estas situaciones suelen ser raras y tal vez una señal de que debería estar buscando microcontroladores más grandes / potentes.

Y si está desarrollando / portando un "sistema operativo" en el PIC18 y si es compatible con microcontroladores, probablemente sea compatible con la asignación estática. Por ejemplo, SQLite3 admite la asignación estática: se le asigna una matriz de búfer grande y la usa cuando sea posible, aunque no sea para microcontroladores. Si no es así, ¿estás seguro de que está diseñado para un PIC18 pequeño?

Thomas O
fuente
Ya veo lo que quieres decir ... Estoy usando el PIC18F87J11, que tiene 128K de ram, ¿puede ser suficiente?
stef
Stefano, ese chip tiene 3,904 bytes de RAM. Tiene 128K de memoria flash de programa.
W5VO
@Stefao Salati - 3.8KB es pequeño.
Thomas O
Bien, lo siento ... ¿crees que puede ser suficiente de todos modos?
stef
@Stefano Salati, no hay necesidad de disculparse. Creo que realmente lo estarías presionando. Podría funcionar, pero le quitaría una parte del rendimiento y su memoria libre.
Thomas O
2

Si está considerando malloc()y free()para su software integrado, le sugiero que eche un vistazo a uC / OS-II y OSMemGet()y OSMemPut(). Si bien le malloc()permite asignar un bloque arbitrario de memoria, le OSMem*()brinda un bloque de tamaño fijo de un grupo preasignado. Este enfoque me parece un buen equilibrio entre la flexibilidad malloc()y la solidez de la asignación estática.

trondd
fuente
0

AFAIK, para hacer esto correctamente, realmente necesita estar mirando un dispositivo con algún tipo de unidad de administración de memoria (MMU). Si bien existen mecanismos de asignación dinámica para la serie PIC18, en realidad no van a ser tan sólidos, y hablando como alguien que ha trabajado en firmware que supera los límites de la serie PIC18, puedo decir que no va a obtener una aplicación considerable allí si gastas todos los gastos generales en un administrador de memoria.

Mejor solución: intente comprender qué está haciendo y por qué necesita una asignación dinámica. Vea si no puede volver a factorizarlo para que funcione con asignación estática. (Puede darse el caso de que esto simplemente no sea posible, si la biblioteca / aplicación está diseñada para hacer algo que se escala libremente, o no tiene límites de la cantidad de entrada que puede aceptar). Pero a veces, si realmente piensa sobre lo que está tratando de hacer, es posible que en su lugar sea posible (y posiblemente incluso bastante fácil) usar la asignación estática.

andersop
fuente
1
Eres incorrecto Una MMU le permite interactuar con la memoria externa (probablemente más de 4kB en el PIC). Hay muy poca diferencia en la asignación dinámica y estática con y sin una MMU. Una vez que comienzas a entrar en la memoria virtual, hay una diferencia, pero eso solo está relacionado tangencialmente con malloc.
Kevin Vermeer
1
Los primeros programadores de Macintosh usaban malloc () y free () (o sus equivalentes Pascal) con bastante frecuencia, a pesar del hecho de que las primeras computadoras Macintosh no tenían MMU. La idea de que "correctamente" usando malloc () requiere una MMU me parece incorrecta.
davidcary