Puede que solo sea una coincidencia, pero noté que los microcontroladores que usé 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é sucede entonces?
¿Por qué/Cómo? El puntero de la pila ciertamente aumenta ciegamente a un rango de memoria no asignado (o se transfiere), pero qué sucede entonces: ¿hay algún tipo de protección que haga que se reinicie o es (entre otros efectos) el resultado de la sobrescritura de crítico? data (que supongo que es diferente del código que creo que se ejecuta directamente desde flash)?
No estoy seguro de que esto deba estar aquí o en Stack Overflow, avíseme si debe moverse, aunque estoy bastante seguro de que el hardware tiene un papel en eso.
Debo señalar que estoy particularmente interesado en el mecanismo real detrás de la corrupción de la memoria (¿es el resultado de la transferencia del SP -> depende eso del mapeo de memoria de uC, etc.)?
En general, la pila y el montón chocan entre sí. En ese momento todo se complica.
Dependiendo de la MCU, una de varias cosas puede (o sucederá).
Cuando sucede 1, comienza 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, nadie sabe a dónde regresará la llamada actual. En ese momento, básicamente, la MCU comenzará a hacer cosas al azar. Cuando 3 vuelva a suceder, quién sabe qué pasaría. Esto solo sucede cuando está ejecutando código fuera de la RAM.
En general, cuando la pila se corrompe, se acabó. Justo lo que sucede depende de la MCU.
Puede ser que intentar asignar la memoria en primer lugar falle para que no ocurra la corrupción. En este caso, la MCU podría generar una excepción. Si no hay un controlador de excepciones 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á en el reino de "¿quién sabe?". La MCU podría terminar reiniciándose a través de la combinación correcta de eventos (las interrupciones causaron que terminaron 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 que el temporizador de vigilancia interno (si existe) agote el tiempo y reinicie el chip. Cuando el programa se ausenta por completo a través de este tipo de bloqueo, las instrucciones para restablecer el temporizador generalmente no se ejecutarán, por lo que expirará y se restablecerá.
Una vista alternativa: los microcontroladores no se quedan sin memoria.
Al menos, no cuando se programa correctamente. La programación de un microcontrolador no es exactamente como la programación de propósito general, para hacerlo correctamente debe ser consciente de sus limitaciones y programar en consecuencia. Hay herramientas para ayudar a asegurar esto. Búscalos y apréndelos, al menos cómo leer las secuencias de comandos y las advertencias del enlazador.
Sin embargo, como dicen Majenko y otros, un microcontrolador mal programado puede quedarse sin memoria y luego hacer cualquier cosa, incluido un bucle infinito (lo 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 se asigna en la pila o se asigna estáticamente (globalmente); "nuevo" o "malloc" están prohibidos. También lo es la recursividad, de modo que la profundidad máxima de anidamiento de subrutinas pueda analizarse y mostrarse para que encaje en la pila disponible.
Por lo tanto, el almacenamiento máximo requerido se puede calcular cuando el programa se compila o 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 es posible que el microcontrolador no se quede 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 usar el subconjunto SPARK-2014 de Ada. Ada realmente apunta a controladores pequeños 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 está haciendo el programa.
Ahora las herramientas de SPARK analizarán el programa, incluidas esas anotaciones, y probarán sus propiedades (o informarán sobre 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 involucrado con SPARK, la experiencia demuestra que puede llegar a un producto más rápido y más barato porque no pierde el tiempo persiguiendo reinicios misteriosos y otros comportamientos extraños.
malloc()
(y su compañero C ++ new
) al AVR es una de las peores cosas que la gente de 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 de arduino. Hay muy, muy pocas situaciones en las que tener malloc
un ATmega sea beneficioso.Me gusta mucho la respuesta de Majenko y yo mismo hice +1. Pero quiero aclarar esto en un punto agudo:
Cualquier cosa puede pasar 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, lo más probable es que la pila se corrompa. Y mientras eso sucede, cualquier cosa puede suceder. Los valores de las variables, los derrames, los registros temporales, todos se corrompen, interrumpiendo los flujos del programa. If/then/elses pueden evaluar incorrectamente. Las direcciones de retorno están distorsionadas, lo que hace que el programa salte a direcciones aleatorias. Cualquier código que haya escrito en el programa puede ejecutarse. (Considere un 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.
AVR ha reiniciado el vector en la dirección cero. Cuando sobrescribe la pila con basura aleatoria, eventualmente dará vueltas y sobrescribirá alguna dirección de retorno y apuntará a "ninguna parte"; luego, cuando regresa de una subrutina a esa nada, la ejecución se desplazará a la dirección 0 donde generalmente se encuentra un controlador de salto para restablecer.
Spehro Pefhany
usuario253751