Pode ser apenas uma coincidência, mas notei que os microcontroladores que usei foram reiniciados quando a RAM ficou sem memória (o Atmega 328, se específico do hardware). É isso que os microcontroladores fazem quando ficam sem memória? Se não, o que acontece então?
Porque como? O ponteiro da pilha certamente é aumentado às cegas para um intervalo de memória não alocado (ou rolado), mas o que acontece então: existe algum tipo de proteção que o reinicia ou é (entre outros efeitos) o resultado da substituição de informações críticas dados (que eu assumo diferente do código que eu acho que é executado diretamente do flash)?
Não tenho certeza se isso deve estar aqui ou no Stack Overflow, por favor, deixe-me saber se isso deve ser movido, embora eu tenha certeza de que o hardware tem um papel nisso.
Atualizar
Devo salientar que estou particularmente interessado no mecanismo real por trás da corrupção de memória (é o resultado da rolagem do SP -> isso depende do mapeamento de memória do uC etc.)?
fonte
Respostas:
Em geral, a pilha e o heap se chocam. Nesse ponto, tudo fica confuso.
Dependendo do MCU, uma de várias coisas pode (ou vai) acontecer.
Quando isso acontece, você começa a ter um comportamento estranho - as coisas não estão fazendo o que deveriam. Quando 2 acontece, todo tipo de inferno se abre. Se o endereço de retorno na pilha (se houver um) estiver corrompido, o local em que a chamada atual retornará é de todo mundo. Naquele momento, basicamente, o MCU começará a fazer coisas aleatórias. Quando o 3 acontecer novamente, quem sabe o que aconteceria. Isso só acontece quando você está executando um código fora da RAM.
Em geral, quando a pilha é corrompida, tudo acaba. Apenas o que acontece depende do MCU.
Pode ser que a tentativa de alocar a memória falhe para que a corrupção não aconteça. Nesse caso, o MCU pode gerar uma exceção. Se não houver um manipulador de exceção instalado, na maioria das vezes o MCU será interrompido (o equivalente a
while (1);
. Se houver um manipulador instalado, ele poderá ser reiniciado corretamente.Se a alocação de memória continuar, ou se tentar, falhar e continuar sem memória alocada, você estará no reino de "quem sabe?". O MCU pode acabar se reinicializando através da combinação correta de eventos (interrupções causadas que acabam redefinindo o chip etc.), mas não há garantia de que isso aconteça.
O que geralmente pode haver uma alta probabilidade de acontecer, no entanto, se estiver ativado, é o temporizador interno do watchdog (se houver) exceder o tempo limite e reiniciar o chip. Quando o programa passar completamente por AWOL nesse tipo de falha, as instruções para redefinir o timer geralmente não serão executadas, portanto o tempo limite será redefinido.
fonte
Uma visão alternativa: os microcontroladores não ficam sem memória.
Pelo menos, não quando programado corretamente. Programar um microcontrolador não é exatamente como a programação de uso geral; para fazê-lo corretamente, você deve estar ciente de suas restrições e programar adequadamente. Existem ferramentas para ajudar a garantir isso. Pesquise-os e aprenda-os - pelo menos como ler scripts e avisos do vinculador.
No entanto, como Majenko e outros dizem, um microcontrolador mal programado pode ficar sem memória e, em seguida, fazer qualquer coisa, incluindo loop infinito (que pelo menos dá ao cronômetro do watchdog uma chance de redefini-lo. Você ativou o timer do watchdog, não foi? )
Regras de programação comuns para microcontroladores evitam isso: por exemplo, toda a memória é alocada na pilha ou estaticamente (globalmente) alocada; "new" ou "malloc" são proibidos. O mesmo ocorre com a recursão, para que a profundidade máxima do aninhamento de sub-rotinas possa ser analisada e mostrada para caber na pilha disponível.
Portanto, o armazenamento máximo necessário pode ser calculado quando o programa é compilado ou vinculado e comparado com o tamanho da memória (geralmente codificada no script do vinculador) para o processador específico que você está direcionando.
Em seguida, o microcontrolador pode não ficar sem memória, mas o seu programa pode. E nesse caso, você começa a
Um conjunto comum de regras para a programação de microcontroladores é o MISRA-C , adotado pela indústria automobilística.
A melhor prática, na minha opinião, é usar o subconjunto SPARK-2014 do Ada. Na verdade, o Ada tem como alvo pequenos controladores como AVR, MSP430 e ARM Cortex razoavelmente bem e, inerentemente, fornece um modelo melhor para a programação de microcontroladores do que o C. Mas o SPARK adiciona anotações ao programa, na forma de comentários, que descrevem o que o programa está fazendo.
Agora, as ferramentas SPARK analisarão o programa, incluindo essas anotações, e comprovarão suas propriedades (ou reportarão possíveis erros). Você não precisa perder tempo ou espaço de código lidando com acessos de memória incorretos ou estouros de números inteiros, porque comprovadamente nunca aconteceram.
Embora exista mais trabalho inicial envolvido com o SPARK, a experiência mostra que ele pode chegar a um produto mais rápido e mais barato, porque você não gasta tempo perseguindo reinicializações misteriosas e outros comportamentos estranhos.
Uma comparação de MISRA-C e SPARK
fonte
malloc()
(e é companheiro de C ++new
) para o AVR é uma das piores coisas que o pessoal do arduino poderia ter feito, e levou a muitos, muitos programadores muito confusos com código quebrado tanto no fórum quanto na troca de pilhas do arduino. Existem muito, muito poucas situações em que termalloc
um ATmega é benéfico.Eu realmente gosto da resposta de Majenko e marquei isso com +1. Mas quero esclarecer isso em um ponto agudo:
Tudo pode acontecer quando um microcontrolador fica sem memória.
Você realmente não pode confiar em nada quando isso acontece. Quando a máquina fica sem memória da pilha, a pilha provavelmente fica corrompida. E quando isso acontece, tudo pode acontecer. Valores variáveis, derramamentos, registros temporários, todos ficam corrompidos, interrompendo os fluxos do programa. Se / then / elses pode avaliar incorretamente. Os endereços de retorno são ilegíveis, fazendo o programa pular para endereços aleatórios. Qualquer código que você escreveu no programa pode ser executado. (Considere um código como: "if [condition] then {fire_all_missiles ();}"). Além disso, várias instruções que você não escreveu podem executar quando o núcleo salta para um local de memória não conectado. Todas as apostas estão encerradas.
fonte
O AVR redefiniu o vetor no endereço zero. Quando você sobrescreve a pilha com lixo aleatório, você eventualmente volta e sobrescreve algum endereço de retorno e ele aponta para "lugar nenhum"; quando você retornar de uma sub-rotina para esse lugar nenhum, a execução retornará para o endereço 0, onde normalmente ocorre um salto para redefinir o manipulador.
fonte