Puede ser solo una coincidencia, pero he notado que los microcontroladores que utilicé se reiniciaron cuando se quedaron sin RAM (Atmega 328 si es específico del hardware). ¿Es eso lo que hacen los microcontroladores cuando se quedan sin memoria? Si no, ¿qué pasa entonces?
¿Por qué / cómo? El puntero de la pila ciertamente se aumenta ciegamente a un rango de memoria no asignado (o se transfiere), pero lo que sucede entonces: ¿hay algún tipo de protección que lo haga reiniciar o es (entre otros efectos) el resultado de la sobreescritura de un elemento crítico? datos (que supongo que son diferentes del código que creo que se ejecuta directamente desde flash)?
No estoy seguro de si esto debería estar aquí o en Stack Overflow, avíseme si se debe mover, aunque estoy bastante seguro de que el hardware tiene un papel en eso.
Actualizar
Debo señalar que estoy particularmente interesado en el mecanismo real detrás de la corrupción de la memoria (¿es el resultado del vuelco del SP -> depende de la asignación de memoria de la UC, etc.)?
fuente
Respuestas:
En general, la pila y el montón chocan entre sí. En ese punto, todo se vuelve desordenado.
Dependiendo de la MCU, una de varias cosas puede suceder (o sucederá).
Cuando sucede 1, comienzas a tener un comportamiento extraño: las cosas no hacen lo que deberían. Cuando sucede 2, se desata todo tipo de infierno. Si la dirección de retorno en la pila (si hay una) está dañada, entonces nadie sabe dónde volverá la llamada actual. En ese momento, básicamente, la MCU comenzará a hacer cosas al azar. Cuando 3 vuelve a suceder, quién sabe qué pasaría. Esto solo sucede cuando está ejecutando código fuera de RAM.
En general, cuando la pila se corrompe, todo termina. Lo que sucede depende de la MCU.
Puede ser que al intentar asignar la memoria en primer lugar falle, por lo que la corrupción no ocurre. En este caso, la MCU podría generar una excepción. Si no hay un controlador de excepción instalado, la mayoría de las veces la MCU simplemente se detendrá (un equivalente de
while (1);
. Si hay un controlador instalado, entonces podría reiniciarse limpiamente.Si la asignación de memoria continúa, o si lo intenta, falla y simplemente continúa sin memoria asignada, entonces estás en los reinos de "¿quién sabe?". La MCU puede terminar reiniciando a través de la combinación correcta de eventos (las interrupciones causaron que terminen reiniciando el chip, etc.), pero no hay garantía de que eso suceda.
Sin embargo, lo que generalmente puede haber una alta probabilidad de que suceda, si está habilitado, es el temporizador interno de vigilancia (si existe) que excede el tiempo de espera y reinicia el chip. Cuando el programa deja de funcionar por completo durante este tipo de bloqueo, las instrucciones para restablecer el temporizador generalmente no se ejecutarán, por lo que se agotará el tiempo y se restablecerá.
fuente
Una vista alternativa: los microcontroladores no se quedan sin memoria.
Al menos, no cuando se programa correctamente. Programar un microcontrolador no es exactamente como la programación de propósito general, para hacerlo correctamente debe tener en cuenta sus limitaciones y programar en consecuencia. Hay herramientas para ayudar a garantizar esto. Búscalos y aprende - al menos cómo leer guiones y advertencias de enlazadores.
Sin embargo, como dicen Majenko y otros, un microcontrolador mal programado puede quedarse sin memoria, y luego hacer cualquier cosa, incluido el bucle infinito (que al menos le da al temporizador de vigilancia la oportunidad de restablecerlo. Habilitó el temporizador de vigilancia, ¿no? )
Las reglas de programación comunes para los microcontroladores evitan esto: por ejemplo, toda la memoria está asignada en la pila o está estática (globalmente) asignada; "nuevo" o "malloc" están prohibidos. También lo es la recursión, de modo que la profundidad máxima de anidamiento de subrutinas se pueda analizar y demostrar que se ajusta a la pila disponible.
Por lo tanto, el almacenamiento máximo requerido se puede calcular cuando el programa se compila o se vincula, y se compara con el tamaño de la memoria (a menudo codificada en el script del vinculador) para el procesador específico al que se dirige.
Entonces el microcontrolador puede no quedarse sin memoria, pero su programa sí. Y en ese caso, llegas a
Un conjunto común de reglas para la programación de microcontroladores es MISRA-C , adoptado por la industria del motor.
En mi opinión, la mejor práctica es utilizar el subconjunto SPARK-2014 de Ada. Ada en realidad se dirige a pequeños controladores como AVR, MSP430 y ARM Cortex razonablemente bien, e inherentemente proporciona un mejor modelo para la programación de microcontroladores que C. Pero SPARK agrega anotaciones al programa, en forma de comentarios, que describen lo que el programa está haciendo.
Ahora las herramientas SPARK analizarán el programa, incluidas esas anotaciones, y probarán propiedades al respecto (o informarán posibles errores). No tiene que perder tiempo ni espacio de código lidiando con accesos de memoria erróneos o desbordamientos de enteros porque se ha demostrado que nunca suceden.
Aunque hay más trabajo inicial relacionado con SPARK, la experiencia demuestra que puede llegar a un producto más rápido y más barato porque no pasa tiempo persiguiendo reinicios misteriosos y otros comportamientos extraños.
Una comparación de MISRA-C y SPARK
fuente
malloc()
(y su compañero de C ++new
) al AVR es una de las peores cosas que la gente arduino podría haber hecho, y ha llevado a muchos, muchos programadores muy confundidos con código roto tanto en su foro como en el intercambio de pila arduino. Hay muy, muy pocas situaciones en las que tenermalloc
un ATmega es beneficioso.Realmente me gusta la respuesta de Majenko y la hice +1. Pero quiero aclarar esto con precisión:
Cualquier cosa puede suceder cuando un microcontrolador se queda sin memoria.
Realmente no puedes confiar en nada cuando sucede. Cuando la máquina se queda sin memoria de pila, la pila probablemente se corrompe. Y mientras eso sucede, cualquier cosa puede suceder. Los valores variables, los derrames, los registros temporales, todos se corrompen, interrumpiendo los flujos del programa. Si / then / elses puede evaluar incorrectamente. Las direcciones de retorno son confusas, lo que hace que el programa salte a direcciones aleatorias. Cualquier código que haya escrito en el programa puede ejecutarse. (Considere código como: "if [condición] entonces {fire_all_missiles ();}"). También se pueden ejecutar un montón de instrucciones que no ha escrito cuando el núcleo salta a una ubicación de memoria no conectada. Todas las apuestas están cerradas.
fuente
AVR ha restablecido el vector en la dirección cero. Cuando sobrescribe la pila con basura aleatoria, eventualmente recorrerá y sobrescribirá alguna dirección de retorno y apuntará a "en ninguna parte"; luego, cuando regrese de una subrutina a ese lugar, la ejecución se repetirá en la dirección 0, donde generalmente se encuentra un salto para restablecer el controlador.
fuente