13 votos

¿Qué ocurre cuando los microcontroladores se quedan sin RAM?

Puede ser sólo una coincidencia, pero me he dado cuenta de que los microcontroladores que he utilizado se reiniciaban cuando se quedaban sin RAM (Atmega 328 si es específico del hardware). ¿Es eso lo que hacen los microcontroladores cuando se quedan sin memoria? Si no es así, ¿qué ocurre entonces?

¿Por qué/cómo? Ciertamente, el puntero de la pila se incrementa a ciegas a un rango de memoria no asignado (o se hace rollover), pero qué ocurre entonces: ¿hay algún tipo de protección que lo hace reiniciar o es (entre otros efectos) el resultado de la sobreescritura de datos críticos (que supongo diferente del código que creo que se ejecuta directamente desde la flash)?

No estoy seguro de que esto debería estar aquí o en Stack Overflow, por favor, hágame saber si esto debe ser movido aunque estoy bastante seguro de que el hardware tiene un papel en eso.

Actualización

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 volcadura del SP -> depende de la asignación de memoria del uC, etc.)?

8 votos

Algunos micros se reinician si intentas acceder a direcciones no válidas. Es una característica valiosa implementada en el hardware. Otras veces puede terminar saltando a algún lugar arbitrario (digamos que has estropeado la dirección de retorno de un ISR), tal vez ejecutando datos en lugar de código si la arquitectura lo permite, y tal vez quedando atrapado en un bucle del que el perro guardián lo saca.

2 votos

Un procesador no puede quedarse sin RAM, no hay ninguna instrucción que haga que se quede sin RAM. Quedarse sin RAM es un concepto totalmente de software.

15voto

user4245 Puntos 324

En general, la pila y el montón chocan entre sí. En ese momento todo se complica.

Dependiendo de la MCU puede suceder (o sucederá) una de varias cosas.

  1. Las variables se corrompen
  2. La pila se corrompe
  3. El programa se corrompe

Cuando ocurre la primera, empiezas a tener comportamientos extraños, cosas que no hacen lo que deberían. Cuando ocurre la segunda, se desata todo el infierno. Si la dirección de retorno en la pila (si hay una) está corrupta, entonces a dónde regresará la llamada actual es una incógnita. En ese momento básicamente la MCU empezará a hacer cosas al azar. Cuando la 3 pasa de nuevo, quién sabe qué pasará. Esto sólo ocurre cuando se ejecuta código fuera de la RAM.

En general, cuando la pila se corrompe se acaba todo. Lo que ocurre depende de la MCU.

Puede ser que al intentar asignar la memoria en primer lugar falle para que no se produzca la corrupción. En este caso la MCU podría lanzar una excepción. Si no hay un manejador de excepciones instalado, entonces la mayoría de las veces el MCU simplemente se detendrá (un equivalente a while (1); . Si hay un manejador instalado, entonces podría reiniciar limpiamente.

Si la asignación de memoria sigue adelante, o si lo intenta, falla, y simplemente continúa sin memoria asignada, entonces estás en el terreno de "quién sabe". La MCU podría terminar reiniciándose a sí misma a través de la combinación correcta de eventos (interrupciones causadas que terminan reiniciando el chip, etc.), pero no hay garantía de que eso ocurra.

Sin embargo, lo que suele haber una alta probabilidad de que ocurra, si está activado, es que el temporizador de vigilancia interno (si existe) se agote y reinicie el chip. Cuando el programa se pierde por completo debido a este tipo de fallo, las instrucciones para restablecer el temporizador generalmente no se ejecutan, por lo que se agotará el tiempo y se reiniciará.

0 votos

Gracias por tu respuesta, es un excelente resumen de los efectos. Tal vez debería haber especificado, sin embargo, que me gustaría tener más detalles sobre el mecanismo real de esas corrupciones: ¿se asigna toda la RAM a la pila y al montón, de manera que el puntero de la pila se desplaza y sobrescribe las variables/direcciones anteriores? ¿O depende menos de la asignación de memoria de cada micro? Opcionalmente (probablemente sea un tema en sí mismo), me interesaría saber cómo se implementan esos manejadores de hardware.

1 votos

Depende sobre todo del compilador y de la biblioteca C estándar que se utilice. A veces también depende de la configuración del compilador (scripts de enlace, etc.).

0 votos

¿Podría ampliar la información, quizás con un par de ejemplos?

12voto

Brian Drummond Puntos 27798

Una visión alternativa: los microcontroladores no se quedan sin memoria.

Al menos, no cuando está bien programado. Programar un microcontrolador no es exactamente como la programación de propósito general, para hacerlo correctamente hay que ser consciente de sus limitaciones y programar en consecuencia. Hay herramientas que ayudan a conseguirlo. Búscalas y apréndelas - al menos cómo leer los scripts del enlazador y las advertencias.

Sin embargo, como dicen Majenko y otros, un microcontrolador mal programado puede quedarse sin memoria y hacer cualquier cosa, incluso un bucle infinito (lo que al menos da al temporizador de vigilancia la oportunidad de reiniciarlo). Has habilitado el watchdog timer, ¿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); "new" o "malloc" están prohibidos. Lo mismo ocurre con la recursividad, para poder analizar la profundidad máxima del anidamiento de subrutinas y demostrar que cabe en la pila disponible.

Por lo tanto, el almacenamiento máximo requerido puede ser calculado cuando el programa es compilado o enlazado, y comparado con el tamaño de la memoria (a menudo codificado en el linker script) 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, se llega a

  • reescribirlo, más pequeño, o
  • elige un procesador más grande (suelen estar disponibles con diferentes tamaños de memoria).

Un conjunto de reglas comunes para la programación de microcontroladores es MISRA-C adoptada por la industria del motor.

En mi opinión, la mejor práctica es utilizar el SPARK-2014 subconjunto de Ada. En realidad, Ada se dirige a pequeños controladores como AVR, MSP430 y ARM Cortex razonablemente bien, e intrínsecamente proporciona un mejor modelo para la programación de microcontroladores que C. Pero SPARK añade anotaciones al programa, en forma de comentarios, que describen lo que el programa está haciendo.

Ahora las herramientas de SPARK analizarán el programa, incluyendo esas anotaciones, y demostrarán propiedades sobre él (o informarán de posibles errores). No tienes que perder tiempo o espacio de código tratando con accesos erróneos a la memoria o desbordamientos de enteros porque se ha demostrado que nunca ocurren.

Aunque SPARK conlleva más trabajo inicial, la experiencia demuestra que puede conseguir un producto más rápido y más barato, ya que no se pierde tiempo persiguiendo reinicios misteriosos y otros comportamientos extraños.

Comparación de MISRA-C y SPARK

3 votos

+1 esto. Porting 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 confundidos con código roto tanto en su foro, como en el intercambio de pilas de arduino. Hay muy, muy pocas situaciones en las que tener malloc en un ATmega es beneficioso.

3 votos

+1 a la filosofía, -1 al realismo. Si las cosas se programaran bien, no habría necesidad de esta pregunta. La pregunta era qué pasa cuando los microcontroladores se quedan sin memoria. Cómo evitar que se queden sin memoria es otra cuestión. En otro orden de cosas, la recursividad es una poderosa herramienta, tanto para resolver problemas y se está quedando sin pila.

0 votos

@PkP : Tentativamente no estoy de acuerdo con el -1 por realismo. Si el codificador integrado puede pensar "cómo puedo prevenir..." en lugar de "qué pasa cuando..." entonces estamos progresando. La recursión, por ejemplo, a menudo puede ser linealizada, no hay muchas tareas en un controlador donde la verdadera recursión sea necesaria. Ahora podría simplemente aceptar la derrota y la programación peligrosa (y estoy de acuerdo en que a veces hay que hacerlo: debemos elegir las batallas con cuidado), pero no hagamos que sea la norma.

6voto

Dror Puntos 745

Me gusta mucho la respuesta de Majenko y yo mismo le he dado un +1. Pero quiero aclarar esto a un punto agudo:

Cualquier cosa puede ocurrir cuando un microcontrolador se queda sin memoria.

Realmente no se puede 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 cuando eso sucede, cualquier cosa puede pasar. Los valores de las variables, los derrames, los registros temporales, todo se corrompe, interrumpiendo el flujo del programa. Los if/then/elses pueden evaluarse incorrectamente. Las direcciones de retorno son confusas, haciendo que el programa salte a direcciones aleatorias. Cualquier código que haya escrito en el programa puede ejecutarse. (Considere código como : "si [condición] entonces {dispara_todos_los_misiles();}"). También un montón de instrucciones que no han escrito puede ejecutarse cuando el núcleo salta a una ubicación de memoria no conectada. Todas las apuestas están hechas.

2 votos

Gracias por el añadido, me ha gustado especialmente la línea fire_all_missiles().

1voto

Ilia Puntos 21

El AVR tiene un vector de reinicio en la dirección cero. Cuando sobreescribes la pila con basura aleatoria, eventualmente harás un bucle y sobreescribirás alguna dirección de retorno y apuntará a "ninguna parte"; entonces, cuando regreses de una subrutina a esa "ninguna parte", la ejecución hará un bucle hasta la dirección 0, donde suele estar un salto al manejador de reinicio.

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X