To może być tylko zbieg okoliczności, ale zauważyłem, że mikrokontrolery, których użyłem, uruchomiły się ponownie, gdy zabrakło pamięci RAM (Atmega 328, jeśli jest specyficzna dla sprzętu). Czy to właśnie robią mikrokontrolery, gdy zabraknie im pamięci? Jeśli nie, co się wtedy stanie?
Dlaczego / jak Wskaźnik stosu jest z pewnością ślepo zwiększony do nieprzydzielonego zakresu pamięci (lub przywrócony), ale co się wtedy dzieje: czy istnieje jakiś rodzaj ochrony, który powoduje, że restartuje się, czy jest to (między innymi efektem) wynik zastąpienia krytycznego dane (które, jak zakładam, różnią się od kodu, który moim zdaniem jest uruchamiany bezpośrednio z pamięci flash)?
Nie jestem pewien, czy powinno to być tutaj, czy w przypadku przepełnienia stosu, daj mi znać, czy powinno to zostać przeniesione, chociaż jestem pewien, że sprzęt ma w tym rolę.
Aktualizacja
Powinienem zwrócić uwagę, że jestem szczególnie zainteresowany faktycznym mechanizmem powodującym uszkodzenie pamięci (czy jest to wynikiem przewracania się SP -> czy to zależy od mapowania pamięci uC itp.)?
źródło
Odpowiedzi:
Ogólnie stos i sterty zderzają się ze sobą. W tym momencie robi się bałagan.
W zależności od MCU może się zdarzyć jedna z kilku rzeczy.
Kiedy 1 zdarza się, zaczynasz zachowywać się dziwnie - rzeczy nie robią tego, co powinny. Kiedy zdarza się 2, piekło się rozprasza. Jeśli adres zwrotny na stosie (jeśli taki istnieje) jest uszkodzony, to miejsce, do którego wróci bieżące połączenie, jest zgadywaniem. W tym czasie zasadniczo MCU zacznie robić losowe rzeczy. Kiedy 3 powtórzy się, kto wie, co by się stało. Dzieje się tak tylko wtedy, gdy kod jest wykonywany poza pamięcią RAM.
Zasadniczo, gdy stos ulega uszkodzeniu, wszystko się kończy. To, co się dzieje, zależy od MCU.
Może się zdarzyć, że próba alokacji pamięci nie powiedzie się, więc nie nastąpi uszkodzenie. W takim przypadku MCU może zgłosić wyjątek. Jeśli nie ma zainstalowanego modułu obsługi wyjątków, to najczęściej MCU po prostu zatrzymuje się (odpowiednik
while (1);
. Jeśli zainstalowany jest moduł obsługi, to może zostać ponownie uruchomiony ponownie.Jeśli alokacja pamięci będzie postępować, lub jeśli spróbuje, nie powiedzie się, a po prostu będzie kontynuowana bez przydzielonej pamięci, to przejdziesz do sfery „kto wie?”. MCU może się zrestartować przez odpowiednią kombinację zdarzeń (przerwania spowodowały zresetowanie układu itp.), Ale nie ma gwarancji, że tak się stanie.
To, co zwykle może mieć duże prawdopodobieństwo, to jednak, jeśli jest włączone, wewnętrzny licznik czasu watchdoga (jeśli taki istnieje) odlicza limit czasu i restartuje układ. Kiedy program przechodzi całkowicie w stan AWOL przez tego rodzaju awarię, instrukcje resetowania timera na ogół nie zostaną uruchomione, więc upłynie limit czasu i nastąpi reset.
źródło
Alternatywny widok: w mikrokontrolerach nie brakuje pamięci.
Przynajmniej nie po prawidłowym zaprogramowaniu. Programowanie mikrokontrolera nie jest dokładnie tak samo jak programowanie ogólnego przeznaczenia, aby zrobić to poprawnie, musisz być świadomy jego ograniczeń i odpowiednio programować. Istnieją narzędzia, które pomogą to zapewnić. Wyszukaj je i naucz się - przynajmniej jak czytać skrypty linkera i ostrzeżenia.
Jednak, jak mówią Majenko i inni, źle zaprogramowanemu mikrokontrolerowi może zabraknąć pamięci, a następnie zrobić cokolwiek, w tym nieskończoną pętlę (co przynajmniej daje zegarowi nadzorującemu szansę na jego zresetowanie. Włączyłeś zegar nadzoru, prawda? )
Wspólne reguły programowania mikrokontrolerów unikają tego: na przykład cała pamięć jest albo przydzielana na stosie, albo statycznie (globalnie) przydzielana; „nowe” lub „malloc” są zabronione. Podobnie jest z rekurencją, aby maksymalna głębokość zagnieżdżenia podprogramu mogła zostać przeanalizowana i pokazana, aby pasowała do dostępnego stosu.
Zatem maksymalną wymaganą pamięć można obliczyć, gdy program jest kompilowany lub połączony, i porównać z wielkością pamięci (często zakodowaną w skrypcie linkera) dla konkretnego procesora, na który celujesz.
Wtedy mikrokontroler może nie zabraknąć pamięci, ale twój program może. I w takim przypadku dojdziesz do
Jednym wspólnym zestawem zasad programowania mikrokontrolerów jest MISRA-C , przyjęty przez przemysł motoryzacyjny.
Moim zdaniem najlepszą praktyką jest korzystanie z podzestawu Ady SPARK-2014 . Ada faktycznie atakuje stosunkowo małe kontrolery, takie jak AVR, MSP430 i ARM Cortex, i z natury zapewnia lepszy model programowania mikrokontrolera niż C. Jednak SPARK dodaje do programu adnotacje w formie komentarzy, które opisują jego działanie.
Teraz narzędzia SPARK przeanalizują program, w tym te adnotacje, i udowodnią jego właściwości (lub zgłoszą potencjalne błędy). Nie musisz marnować czasu ani miejsca na kod, zajmując się błędnym dostępem do pamięci lub przepełnieniem liczb całkowitych, ponieważ udowodniono, że nigdy się nie zdarzają.
Chociaż SPARK wymaga więcej pracy z góry, doświadczenie pokazuje, że może dostać się do produktu szybciej i taniej, ponieważ nie spędzasz czasu na tajemniczych restartach i innych dziwnych zachowaniach.
Porównanie MISRA-C i SPARK
źródło
malloc()
(i towarzyszącego mu C ++new
) do AVR jest jedną z najgorszych rzeczy, które ludzie arduino mogliby zrobić, i doprowadziło do wielu, wielu bardzo zdezorientowanych programistów ze złamanym kodem zarówno na forum, jak i wymianie stosu arduino. Jest bardzo, bardzo niewiele sytuacji, w których posiadaniemalloc
ATmega jest korzystne.Naprawdę podoba mi się odpowiedź Majenko i dałam jej +1. Ale chcę to wyjaśnić do pewnego stopnia:
Wszystko może się zdarzyć, gdy w mikrokontrolerze zabraknie pamięci.
Naprawdę nie można na niczym polegać, kiedy to się dzieje. Kiedy w urządzeniu zabraknie pamięci stosu, stos najprawdopodobniej zostanie uszkodzony. I tak się dzieje, wszystko może się zdarzyć. Zmienne wartości, wycieki, rejestry temp. Zostają uszkodzone, co zakłóca przepływ programu. Jeśli / to / elses może niepoprawnie ocenić. Adresy zwrotne są zniekształcone, co powoduje, że program przeskakuje na adresy losowe. Każdy kod napisany w programie może zostać wykonany. (Rozważ kod typu: „jeśli [warunek], to {fire_all_missiles ();}”). Również cała gama instrukcji, których nie napisałeś, może zostać wykonana, gdy rdzeń przeskoczy do niepołączonej lokalizacji pamięci. Wszystkie zakłady są wyłączone.
źródło
AVR zresetował wektor pod adresem zero. Kiedy nadpiszesz stos losowymi śmieciami, w końcu zapętlisz się i nadpisujesz adres zwrotny i wskaże on „nigdzie”; wtedy, gdy wrócisz z podprogramu do tego miejsca, wykonanie zapętli się wokół adresu 0, gdzie zwykle jest skok do resetowania procedury obsługi.
źródło