Pregunta

He encontrado un problema en un programa C que se ejecuta en un microcontrolador AVR (ATmega328P). Creo que es debido a una colisión pila / pila, pero me gustaría ser capaz de confirmar esto.

¿Hay alguna manera de poder visualizar el uso SRAM por la pila y el montón?

Nota: el programa se compila con avr-gcc y utiliza avr-libc

.

Actualización: El problema real que estoy teniendo es que la implementación de malloc está fallando (NULL regresar). Todo mallocing sucede en el arranque y todo freeing sucede al final de la aplicación (que en la práctica nunca es desde la parte principal de la aplicación está en un bucle infinito). Así que estoy seguro de fragmentación no es el problema.

¿Fue útil?

Solución

Usted dice malloc está fallando y devuelva NULL:

La causa obvia que se debe buscar en un primer momento es que el montón es "completa" - es decir, la memoria con lo solicitado a malloc no puede ser distribuido, ya que no está disponible.

Existen dos escenarios a tener en cuenta:

R: Tienes un montón de 16 K, ya has malloced 10 K y tratar de malloc un 10K adicional. Su montón es simplemente demasiado pequeño.

b: Más comúnmente, tiene un 16 k Pila, que ha estado haciendo un montón de llamadas malloc / libre / REALLOC y su montón es inferior al 50% 'total': Se llama a malloc para 1K y falla - ¿Qué pasa? Respuesta - el espacio libre del montón está fragmentado - no hay un contigous 1 K de memoria libre que se puede devolver. gestores C Heap no pueden compactar el montón cuando esto sucede, por lo general, usted está en una mala manera. Hay técnicas para evitar la fragmentación, pero es difícil saber si esto es realmente el problema. Que había necesidad de agregar relleno de registro a malloc y libre para que pueda tener una idea de lo que se llevan a cabo las operaciones de memoria dinámica.

EDIT:

Usted dice que todos mallocs se realizan al arranque, por lo que la fragmentación no es el problema.

En este caso, debería ser fácil de reemplazar la asignación dinámica con estática.

viejo código de ejemplo:

char *buffer;

void init()
{
  buffer = malloc(BUFFSIZE);
}

nuevo código:

char buffer[BUFFSIZE];

Una vez que haya hecho esto en todas partes, su Linker debe advertir que si todo no puede caber en la memoria disponible. No se olvide de reducir el tamaño del montón -., Pero ten en cuenta que algunas funciones del sistema de tiempo de ejecución io aún puede utilizar la pila, por lo que puede no ser capaz de eliminar por completo

Otros consejos

Puede comprobar el uso de memoria RAM estática usando la utilidad avr-size, según lo descrito en
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=62968 , España http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=82536 , España http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=95638 , España y http://letsmakerobots.com/node/27115

avr-size -C -x Filename.elf

(documentación avr-size: http://ccrma.stanford.edu/planetccrma /man/man1/avr-size.1.html )

Sigue un ejemplo de cómo hacer esto en un entorno de desarrollo: En Code :: Blocks, Proyecto -> Opciones de construcción -> pre / post construir pasos -> Post-pasos de generación, incluyen:

avr-size -C $(TARGET_OUTPUT_FILE) o
avr-size -C --mcu=atmega328p $(TARGET_OUTPUT_FILE)

Ejemplo de salida al final de construcción:

AVR Memory Usage
----------------
Device: atmega16

Program:    7376 bytes (45.0% Full)
(.text + .data + .bootloader)

Data:         81 bytes (7.9% Full)
(.data + .bss + .noinit)

EEPROM:       63 bytes (12.3% Full)
(.eeprom) 

Los datos son el uso de SRAM, y es sólo la cantidad que el compilador conoce en tiempo de compilación. También necesita espacio para las cosas creadas en tiempo de ejecución (en particular apilar uso).

Para verificar el uso pila (RAM dinámica), de http://jeelabs.org/2011/05/22/atmega-memory-use/

Esta es una función de utilidad pequeña que determina la cantidad de RAM Actualmente no se usa:

int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

Y aquí es un boceto usando ese código:

void setup () {
    Serial.begin(57600);
    Serial.println("\n[memCheck]");
    Serial.println(freeRam());
}

La función FreeRAM () devuelve la forma existen muchos bytes entre el final de la pila y la última memoria asignada a la pila, por lo que es efectivamente la cantidad de la pila / pila puede crecer antes de que choquen.

Se puede comprobar el retorno de esta función en torno código sospecha que puede estar causando la colisión pila / pila.

El enfoque habitual sería para llenar la memoria con un patrón conocido y luego para comprobar qué áreas se sobrescriben.

Si está utilizando tanto pila y el montón, entonces puede ser un poco más complicado. Voy a explicar lo que he hecho cuando no se utiliza montón. Como regla general, todas las empresas que he trabajado (en el dominio de software embebido C) han evitado el uso montón de pequeños proyectos a embebidos evitar la incertidumbre de la disponibilidad de la pila de memoria. Utilizamos estáticamente por las variables en su lugar.

Un método consiste en llenar la mayor parte de la zona de apilamiento con un patrón conocido (por ejemplo, 0x55) en el arranque. Esto se hace generalmente por un pequeño trozo de código al principio de la ejecución del software, ya sea desde el principio de main (), o tal vez incluso antes de main () comienza, en el código de puesta en marcha. Tenga cuidado de no sobrescribir la pequeña cantidad de pila en uso en ese momento, por supuesto. Entonces, después de ejecutar el software durante un tiempo, inspeccionar el contenido de espacio de pila y ver donde el 0x55 sigue intacta. La forma de "inspeccionar" depende de su hardware de destino. Asumiendo que tiene un depurador conectado, entonces simplemente puede detener el micro correr y leer la memoria.

Si usted tiene un depurador que puede hacer un punto de interrupción memoria de acceso (un poco más elegante que el punto de interrupción de la ejecución de costumbre), entonces se puede establecer un punto de interrupción en un lugar, tales como el límite extremo de su espacio de pila pila particular. Eso puede ser muy útil, ya que también le muestra exactamente lo poco de código se ejecuta cuando se llega a ese grado de utilización de la pila. Pero se requiere el depurador para apoyar la función de memoria de acceso punto de interrupción y que a menudo no se encuentra en los depuradores de "gama baja".

Si también está utilizando montón, entonces puede ser un poco más complicado, ya que puede ser imposible predecir dónde pila y montón chocarán.

No utilice el montón de asignación / dinámico en objetivos embebidos. Especialmente con un procesador con recursos tan limitados. Más bien rediseñar su aplicación, ya que el problema vuelva a ocurrir como el programa crece.

Si se asume que está utilizando sólo una pila (por lo tanto no un RTOS o nada) y que la pila se encuentra al final de la memoria, creciendo hacia abajo, mientras que la pila está empezando después de la región BSS / DATA, creciendo. He visto las implementaciones de malloc que comprueban la realidad StackPointer y fallan en una colisión. Se podría tratar de hacer eso.

Si usted no es capaz de adaptar el código malloc, se puede optar por poner su pila al comienzo de la memoria (utilizando el archivo enlazador). En general, siempre es una buena idea para saber / definir el tamaño máximo de la pila. Si lo pones en el inicio, obtendrá un error en la lectura más allá del comienzo de la RAM. El montón será al final y, probablemente, no puede crecer más allá del final si es una implementación de dichas mejoras decente (devolverá NULL en su lugar). Lo bueno es que usted conoce tiene 2 casos de error independientes para 2 temas distintos.

Para averiguar el tamaño máximo de pila, se podría llenar su memoria con un patrón, ejecutar la aplicación y ver lo lejos que se fue, véase también la respuesta de Craig.

Si usted puede editar el código para su montón, usted podría almohadilla con un par de bytes adicionales (difíciles de esos recursos bajos) en cada bloque de memoria. Estos bytes pueden contener un patrón conocido diferente de la pila. Esto le puede dar una idea de si choca con la pila al ver aparecer dentro de la pila, o viceversa.

En Unix como sistemas operativos una función de biblioteca sbrk llamado () con un parámetro de 0 le permite acceder a la dirección superior de la pila de memoria asignada dinámicamente. El valor de retorno es un puntero void * y podría compararse con la dirección de una variable asignada pila arbitraria.

Utilizando el resultado de esta comparación se debe utilizar con cuidado. Dependiendo de la arquitectura de la CPU y el sistema, la pila puede ser creciente debajo de un máximo dirección arbitraria, mientras que el montón asignado se moverá hacia arriba desde bajo obligado memoria.

A veces, el sistema operativo tiene otros conceptos para la gestión de memoria (es decir, OS / 9) que coloca montón y se apilan en diferentes segmentos de memoria en la memoria libre. En estos sistemas operativos - especialmente para sistemas embebidos - es necesario definir los requisitos máximos de memoria de su  aplicaciones de antelación para que el sistema para asignar segmentos de memoria de tamaños coincidentes.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top