Kompilowanie aplikacji do użytku w wysoce radioaktywnych środowiskach

1456

Kompilujemy wbudowaną aplikację C / C ++, która jest wdrażana w ekranowanym urządzeniu w środowisku bombardowanym promieniowaniem jonizującym . Używamy GCC i kompilacji krzyżowej dla ARM. Po wdrożeniu nasza aplikacja generuje niektóre błędne dane i ulega awarii częściej niż byśmy tego chcieli. Sprzęt jest przeznaczony dla tego środowiska, a nasza aplikacja działa na tej platformie od kilku lat.

Czy są jakieś zmiany, które możemy wprowadzić w naszym kodzie, lub ulepszenia czasu kompilacji, które można wprowadzić w celu zidentyfikowania / skorygowania błędów miękkich i uszkodzenia pamięci spowodowanych zakłóceniami pojedynczych zdarzeń ? Czy inni programiści odnieśli sukces w zmniejszaniu szkodliwych skutków błędów miękkich w długotrwałej aplikacji?

wieża
źródło
186
Czy zmieniają się wartości w pamięci, czy zmieniają się wartości w procesorze? Jeśli sprzęt jest przeznaczony dla środowiska, oprogramowanie powinno działać tak, jakby działało w środowisku nieradioaktywnym.
Thomas Matthews,
3
Jeśli to możliwe, należy skonfigurować system rejestrowania, który przechowuje zdarzenia w nieulotnej pamięci odpornej na promieniowanie. Przechowuj wystarczającą ilość informacji, aby móc prześledzić zdarzenie i łatwo znaleźć główną przyczynę.
Thomas Matthews
2
@Thomas Matthews Cała pamięć ma wskaźnik błędów FIT, a producenci sprzętu składają wiele obietnic. Większość problemów jest prawdopodobnie spowodowanych przez modyfikację pamięci RAM w środowisku wykonawczym przez SEU.
wieża
9
Jest to kombinacja rozwiązania sprzętowo-programowego, ale wiem, że Texas Instruments (i prawdopodobnie inne) produkuje wbudowane układy scalone do aplikacji o krytycznym znaczeniu dla bezpieczeństwa, które składają się z dwóch zduplikowanych rdzeni, pracujących w trybie blokowania, o pół cyklu poza fazą. Istnieją specjalne przerwania i operacje resetowania, które są podejmowane, gdy sprzęt wykryje coś innego między rdzeniami, abyś mógł wyzerować się po błędach. Wierzę, że TI określa je jako procesory bezpieczeństwa „Hercules”.
mbrig 10.10.17
5
Nadmiarowe wytrzymałe silniki, niektóre koła zębate, wały i grzechotki! Wymieniaj co roku lub częściej, jeśli wymagają tego dawki. Nie, moje pierwsze pytanie dotyczące tego rodzaju problemów zawsze brzmiało: czy naprawdę potrzebujesz tak dużo oprogramowania? Bądź tak analogowy, jak to tylko możliwe.
jwdonahue

Odpowiedzi:

814

Pracując przez około 4-5 lat przy opracowywaniu oprogramowania / oprogramowania układowego i testowaniu środowiska zminiaturyzowanych satelitów *, chciałbym podzielić się tutaj swoimi doświadczeniami.

* ( zminiaturyzowane satelity są znacznie bardziej podatne na zakłócenia pojedynczych zdarzeń niż większe satelity ze względu na stosunkowo małe, ograniczone rozmiary komponentów elektronicznych )

Być bardzo zwięzły i bezpośredni: nie ma mechanizmu, aby odzyskać od wykrywalnego, błędnej sytuacji przez oprogramowanie / firmware się bez przynajmniej jednej kopii od minimalnej wersji roboczej oprogramowania / firmware gdzieś do odzyskiwania celu - i ze sprzętem wsparcie odzyskiwanie (funkcjonalne).

Ta sytuacja jest zwykle obsługiwana zarówno na poziomie sprzętowym, jak i programowym. Tutaj, zgodnie z twoją prośbą, podzielę się tym, co możemy zrobić na poziomie oprogramowania.

  1. ... ... celem odzysku . Zapewnij możliwość aktualizacji / ponownej kompilacji / aktualizacji oprogramowania / oprogramowania układowego w prawdziwym środowisku. Jest to niemal niezbędna funkcja każdego oprogramowania / oprogramowania układowego w silnie zjonizowanym środowisku. Bez tego, to mogło mieć redundantny oprogramowania / sprzętu tyle, ile chcesz, ale w pewnym momencie, wszystkie są zamiar wysadzić. Więc przygotuj tę funkcję!

  2. ... minimalna wersja robocza ... Posiadaj responsywne, wiele kopii, minimalną wersję oprogramowania / oprogramowania w kodzie. To jest jak tryb awaryjny w systemie Windows. Zamiast mieć tylko jedną, w pełni funkcjonalną wersję oprogramowania, masz wiele kopii minimalnej wersji oprogramowania / oprogramowania układowego. Minimalna kopia ma zwykle znacznie mniejszy rozmiar niż kopia pełna i prawie zawsze ma tylko następujące dwie lub trzy funkcje:

    1. zdolny do słuchania poleceń z systemu zewnętrznego,
    2. zdolny do aktualizacji aktualnego oprogramowania / oprogramowania układowego,
    3. zdolny do monitorowania podstawowych danych porządkowych operacji.
  3. ... kopiuj ... gdzieś ... Masz gdzieś zbędne oprogramowanie / oprogramowanie.

    1. Możesz, z redundantnym sprzętem lub bez niego, próbować mieć redundantne oprogramowanie / oprogramowanie w ARM uC. Zwykle odbywa się to poprzez posiadanie dwóch lub więcej identycznych programów / oprogramowania układowego pod oddzielnymi adresami, które wysyłają do siebie bicie serca - ale tylko jedno będzie aktywne jednocześnie. Jeśli wiadomo, że jedno lub więcej oprogramowania / oprogramowania wewnętrznego nie odpowiada, przełącz się na inne oprogramowanie / oprogramowanie wewnętrzne. Korzyścią wynikającą z zastosowania tego podejścia jest możliwość zastąpienia funkcjonalnego natychmiast po wystąpieniu błędu - bez kontaktu z jakimkolwiek zewnętrznym systemem / podmiotem odpowiedzialnym za wykrycie i naprawę błędu (w przypadku satelity jest to zwykle Centrum Kontroli Misji ( MCK)).

      Ściśle mówiąc, bez zbędnego sprzętu wadą takiego działania jest to, że nie można wyeliminować wszystkich pojedynczych punktów awarii. Przynajmniej będziesz miał jeden punkt awarii, którym jest sam przełącznik (lub często początek kodu). Niemniej jednak, w przypadku urządzenia ograniczonego wielkością w wysoce zjonizowanym środowisku (takim jak satelity pico / femto), warto rozważyć zmniejszenie pojedynczego punktu awarii do jednego punktu bez dodatkowego sprzętu. Kiedyś fragment kodu do przełączania byłby z pewnością znacznie mniejszy niż kod dla całego programu - znacznie zmniejszając ryzyko dostania się do niego pojedynczego zdarzenia.

    2. Ale jeśli tego nie robisz, powinieneś mieć co najmniej jedną kopię w swoim systemie zewnętrznym, która może wejść w kontakt z urządzeniem i zaktualizować oprogramowanie / oprogramowanie (w przypadku satelity jest to ponownie centrum kontroli misji).

    3. Możesz również mieć kopię w swojej stałej pamięci w urządzeniu, którą można uruchomić w celu przywrócenia oprogramowania / oprogramowania wewnętrznego uruchomionego systemu
  4. ... wykrywalna błędna sytuacja .. Błąd musi być wykrywalny , zwykle przez sprzętowy obwód korekcji / wykrywania błędu lub mały fragment kodu do korekcji / wykrywania błędu. Najlepiej jest umieścić taki kod mały, wielokrotny i niezależny od głównego oprogramowania / oprogramowania układowego. Jego głównym zadaniem jest tylko sprawdzanie / poprawianie. Jeśli obwód sprzętowy / oprogramowanie układowe jest niezawodny(na przykład, że jest bardziej hartowany promieniowaniem niż reszta - lub ma wiele obwodów / logiki), możesz rozważyć dokonanie z nim korekcji błędów. Ale jeśli nie jest, lepiej zrobić to jako wykrywanie błędów. Korekta może być wykonana przez zewnętrzny system / urządzenie. Do korekcji błędów można rozważyć użycie podstawowego algorytmu korekcji błędów, takiego jak Hamming / Golay23, ponieważ można je łatwiej zaimplementować zarówno w obwodzie / oprogramowaniu. Ale ostatecznie zależy to od zdolności twojego zespołu. Do wykrywania błędów zwykle stosuje się CRC.

  5. ... sprzęt wspierający odzyskiwanie Teraz dochodzi do najtrudniejszego aspektu tego problemu. Ostatecznie odzyskiwanie wymaga, aby sprzęt odpowiedzialny za odzyskiwanie był przynajmniej funkcjonalny. Jeśli sprzęt jest trwale uszkodzony (zwykle dzieje się po tym, jak jego całkowita dawka jonizująca osiągnie pewien poziom), wówczas (niestety) nie ma sposobu, aby oprogramowanie mogło pomóc w odzyskaniu. Dlatego sprzęt jest słusznie najważniejszy w przypadku urządzeń narażonych na wysoki poziom promieniowania (takich jak satelita).

Oprócz sugestii powyżej przewidywania błędu oprogramowania układowego z powodu zakłócenia pojedynczego zdarzenia, chciałbym również zasugerować, abyś miał:

  1. Algorytm wykrywania błędów i / lub korekcji błędów w protokole komunikacyjnym między podsystemami. To kolejna niemal konieczność, aby uniknąć niekompletnych / złych sygnałów odbieranych z innego systemu

  2. Filtruj odczyt ADC. Czy nie używać ADC odczyt bezpośrednio. Filtruj według mediany, średniej lub innych filtrów - nigdy nie ufaj pojedynczej wartości odczytu. Próbuj więcej, nie mniej - rozsądnie.

Ian
źródło
401

NASA ma artykuł na temat oprogramowania odpornego na promieniowanie . Opisuje trzy główne zadania:

  1. Regularne monitorowanie pamięci pod kątem błędów, a następnie ich usuwanie,
  2. solidne mechanizmy odzyskiwania po błędach oraz
  3. możliwość rekonfiguracji, jeśli coś już nie działa.

Należy pamiętać, że szybkość skanowania pamięci powinna być wystarczająco częsta, aby rzadko występowały błędy wielobitowe, ponieważ większość pamięci ECC może odzyskać po błędach jednobitowych, a nie błędach wielobitowych.

Solidne odzyskiwanie po błędzie obejmuje transfer kontroli przepływu (zwykle ponowne uruchomienie procesu w punkcie poprzedzającym błąd), zwolnienie zasobów i przywracanie danych.

Ich głównym zaleceniem w zakresie przywracania danych jest unikanie ich konieczności, ponieważ dane pośrednie należy traktować jako tymczasowe, aby ponowne uruchomienie przed błędem również przywróciło dane do wiarygodnego stanu. To brzmi podobnie do pojęcia „transakcji” w bazach danych.

Omawiają techniki szczególnie odpowiednie dla języków obiektowych, takich jak C ++. Na przykład

  1. Oparte na oprogramowaniu ECC dla ciągłych obiektów pamięci
  2. Programowanie według umowy : weryfikacja warunków wstępnych i dodatkowych, a następnie sprawdzenie obiektu w celu sprawdzenia, czy nadal jest w poprawnym stanie.

Tak się składa, że ​​NASA używa C ++ do dużych projektów, takich jak Mars Rover .

Abstrakcja i enkapsulacja klasy C ++ umożliwiły szybki rozwój i testowanie wśród wielu projektów i programistów.

Unikali pewnych funkcji C ++, które mogłyby powodować problemy:

  1. Wyjątki
  2. Szablony
  3. Iostream (bez konsoli)
  4. Wielokrotne dziedziczenie
  5. Przeciążenie operatora (inne niż newi delete)
  6. Alokacja dynamiczna (zastosowano dedykowaną pulę pamięci i rozmieszczenie, newaby uniknąć możliwości uszkodzenia sterty systemowej).
rsjaffe
źródło
28
To właściwie brzmi jak coś, w czym czysty język byłby dobry. Ponieważ wartości nigdy się nie zmieniają, jeśli zostaną uszkodzone, możesz po prostu wrócić do oryginalnej definicji (która powinna być) i nie zrobisz tego dwa razy przypadkowo (z powodu braku efektów ubocznych).
PyRulez 25.04.16
20
RAII jest złym pomysłem, ponieważ nie można polegać na tym, że działa poprawnie lub w ogóle. Może losowo uszkodzić twoje dane itp. Naprawdę chcesz niezmienności, jaką możesz uzyskać, a na dodatek mechanizmy korekcji błędów. O wiele łatwiej jest po prostu wyrzucić zepsute rzeczy, niż spróbować je naprawić (jak dokładnie wiesz wystarczająco dużo, aby wrócić do właściwego starego stanu?). Prawdopodobnie chcesz do tego użyć raczej głupiego języka - optymalizacje mogą zaszkodzić bardziej niż pomagają.
Luaan,
67
@PyRulez: Czyste języki to abstrakcja, sprzęt nie jest czysty. Kompilatory są całkiem dobre w ukrywaniu różnicy. Jeśli twój program ma wartość, której logicznie nie powinien już używać po kroku X, kompilator może zastąpić go wartością obliczoną w kroku X + 1. Ale to oznacza, że ​​nie możesz wrócić. Mówiąc bardziej formalnie, możliwe stany programu w czystym języku tworzą wykres acykliczny, co oznacza, że ​​dwa stany są równoważne i można je łączyć, gdy stany osiągalne z obu są równoważne. To połączenie niweczy różnicę ścieżek prowadzących do tych stanów.
MSalters
2
@Vorac - Zgodnie z prezentacją problemem związanym z szablonami C ++ jest rozdęcie kodu.
jww
3
@DeerSpotter Dokładny problem jest znacznie większy. Jonizacja może uszkodzić bity twojego działającego programu obserwującego. Wtedy będziesz potrzebował obserwatora obserwatora, a następnie - obserwatora obserwatora i tak dalej ...
Agnius Vasiliauskas
116

Oto kilka myśli i pomysłów:

Korzystaj z ROM w bardziej kreatywny sposób.

Przechowuj wszystko, co możesz, w pamięci ROM. Zamiast obliczać rzeczy, przechowuj tabele przeglądowe w pamięci ROM. (Upewnij się, że Twój kompilator wyświetla tabele przeglądowe w sekcji tylko do odczytu! Wydrukuj adresy pamięci w czasie wykonywania, aby to sprawdzić!) Przechowuj tabelę wektorów przerwań w pamięci ROM. Oczywiście, uruchom kilka testów, aby zobaczyć, jak niezawodna jest twoja pamięć ROM w porównaniu do pamięci RAM.

Użyj swojej najlepszej pamięci RAM dla stosu.

Jednostki SEU na stosie są prawdopodobnie najbardziej prawdopodobnym źródłem awarii, ponieważ tam zwykle występują takie rzeczy, jak zmienne indeksowe, zmienne statusu, adresy zwrotne i wskaźniki różnego rodzaju.

Wdrożenie procedur timera tykania i watchdoga.

Możesz uruchomić procedurę „sprawdzania rozsądku” przy każdym tyknięciu zegara, a także procedurę kontrolną do obsługi blokowania systemu. Twój główny kod może również okresowo zwiększać licznik wskazujący postęp, a procedura sprawdzania czystości może zapewnić, że tak się stało.

Zaimplementuj kody korekcji błędów w oprogramowaniu.

Możesz dodać redundancję do swoich danych, aby móc wykryć i / lub poprawić błędy. To wydłuży czas przetwarzania, potencjalnie pozostawiając procesor narażony na promieniowanie przez dłuższy czas, zwiększając w ten sposób ryzyko błędów, więc musisz rozważyć kompromis.

Pamiętaj o pamięci podręcznej.

Sprawdź rozmiary pamięci podręcznej procesora. Dane, do których ostatnio uzyskano dostęp lub które zmodyfikowano, prawdopodobnie znajdą się w pamięci podręcznej. Uważam, że możesz wyłączyć przynajmniej niektóre pamięci podręczne (przy dużym koszcie wydajności); powinieneś spróbować, aby zobaczyć, jak podatne są pamięci podręczne na SEU. Jeśli pamięci podręczne są trudniejsze niż pamięć RAM, możesz regularnie odczytywać i ponownie zapisywać krytyczne dane, aby upewnić się, że pozostają one w pamięci podręcznej i przywracają pamięć RAM z powrotem do linii.

Używaj sprytnie procedur obsługi błędów stron.

Jeśli zaznaczysz stronę pamięci jako nieobecną, procesor spowoduje błąd strony podczas próby uzyskania do niej dostępu. Można utworzyć moduł obsługi błędów strony, który sprawdza niektóre elementy przed obsłużeniem żądania odczytu. (Systemy operacyjne PC używają tego do przezroczystego ładowania stron, które zostały zamienione na dysk).

Używaj języka asemblera do krytycznych rzeczy (które mogą być wszystkim).

Dzięki językowi asemblera wiesz, co jest w rejestrach, a co w pamięci RAM; ty wiesz jakie tabele specjalny RAM CPU korzysta i można projektować rzeczy w okrężny sposób, aby zachować swoje ryzyko w dół.

Służy objdumpdo przeglądania wygenerowanego języka asemblera i obliczania ilości kodu, jaką zajmuje każda z procedur.

Jeśli używasz dużego systemu operacyjnego, takiego jak Linux, to prosisz o kłopoty; jest tyle złożoności i tylu rzeczy do zrobienia.

Pamiętaj, że to gra prawdopodobieństwa.

Komentator powiedział

Każda procedura napisana w celu wychwycenia błędów będzie ulegać awarii z tej samej przyczyny.

Chociaż jest to prawda, szanse na błędy w (powiedzmy) 100 bajtach kodu i danych wymaganych do prawidłowego działania procedury sprawdzającej są znacznie mniejsze niż prawdopodobieństwo wystąpienia błędów w innym miejscu. Jeśli twój ROM jest dość niezawodny i prawie cały kod / dane faktycznie znajdują się w ROM, twoje szanse są jeszcze większe.

Użyj nadmiarowego sprzętu.

Użyj 2 lub więcej identycznych konfiguracji sprzętowych z identycznym kodem. Jeśli wyniki różnią się, należy uruchomić reset. Na 3 lub więcej urządzeniach możesz użyć systemu „głosowania”, aby spróbować ustalić, które zostało naruszone.

Artelius
źródło
14
Obecnie ECC jest dostępne za pośrednictwem sprzętu, co oszczędza czas przetwarzania. Pierwszym krokiem byłoby wybranie mikrokontrolera z wbudowanym ECC.
Lundin,
23
Gdzieś w głębi mojego umysłu jest odniesienie do sprzętu lotniczego awioniki (może promu kosmicznego?), W którym nadmiarowa architektura została wyraźnie zaprojektowana, aby nie była identyczna (i przez różne zespoły). Zmniejsza to możliwość wystąpienia błędu systemowego w projekcie sprzętu / oprogramowania, zmniejszając możliwość awarii wszystkich systemów głosowania w tym samym czasie w przypadku konfrontacji z tymi samymi danymi wejściowymi.
Peter M
8
@PeterM: AFAIK, który jest również zastrzeżony dla oprogramowania do lotu dla Boeinga 777: trzy wersje trzech zespołów w trzech językach programowania.
Przywróć Monikę - M. Schröder
7
@DanEsparza RAM zwykle ma albo kondensator (DRAM) lub kilka tranzystorów w pamięci zwrotnej (SRAM) przechowujących dane. Zdarzenie radiacyjne może fałszywie ładować / rozładowywać kondensator lub zmieniać sygnał w pętli sprzężenia zwrotnego. ROM zwykle nie wymaga umiejętności zapisu (przynajmniej bez specjalnych okoliczności i / lub wyższych napięć), a zatem może być z natury bardziej stabilny na poziomie fizycznym.
nanofarad
7
@DanEsparza: Istnieje wiele rodzajów pamięci ROM. Jeśli „ROM” jest emulowany przez np. Eeprom lub flash tylko do odczytu przy 5 V, ale programowalny przy 10 V, to rzeczywiście „ROM” jest nadal podatny na jonizację. Może tylko mniej niż inni. Są jednak dobre, hardcorowe rzeczy, takie jak Mask ROM lub PROM oparty na bezpiecznikach, które moim zdaniem wymagałyby naprawdę poważnej ilości promieniowania, aby zacząć zawodzić. Nie wiem jednak, czy nadal są produkowane.
quetzalcoatl
105

Być może zainteresuje Cię także bogata literatura na temat algorytmicznej odporności na uszkodzenia. Obejmuje to stare przypisanie: Napisz rodzaj, który poprawnie sortuje dane wejściowe, gdy nie powiedzie się stała liczba porównań (lub, nieco bardziej zła wersja, gdy asymptotyczna liczba nieudanych porównań skaluje się jak w log(n)przypadku nporównań).

Miejscem do rozpoczęcia czytania jest artykuł Huanga i Abrahama z 1984 r. „ Tolerancja błędów oparta na algorytmach dla operacji matrycowych ”. Ich pomysł jest niejasno podobny do homomorficznego szyfrowanego obliczenia (ale tak naprawdę nie jest taki sam, ponieważ próbują wykryć / skorygować błąd na poziomie operacyjnym).

Nowszym potomkiem tego artykułu jest Bosilca, Delmas, Dongarra i Langou „ Tolerancja błędów oparta na algorytmach stosowana do obliczeń o wysokiej wydajności ”.

Eric Towers
źródło
5
Naprawdę podoba mi się twoja odpowiedź. Jest to bardziej ogólne podejście programowe do integralności danych, aw naszym produkcie końcowym zastosowane zostanie algorytmiczne rozwiązanie zapewniające odporność na uszkodzenia. Dzięki!
wieża
41

Pisanie kodu dla środowisk radioaktywnych nie różni się niczym od pisania kodu dla aplikacji o kluczowym znaczeniu.

Oprócz tego, co już wspomniano, oto kilka różnych wskazówek:

  • Używaj codziennych środków bezpieczeństwa typu „chleb i masło”, które powinny być obecne w każdym półprofesjonalnym systemie wbudowanym: wewnętrzny watchdog, wewnętrzny czujnik niskiego napięcia, wewnętrzny monitor zegara. O tych rzeczach nie trzeba nawet wspominać w 2016 roku i są one standardem w prawie każdym nowoczesnym mikrokontrolerze.
  • Jeśli masz MCU zorientowane na bezpieczeństwo i / lub motoryzację, będzie ono miało pewne funkcje watchdoga, takie jak dane okno czasowe, w którym musisz odświeżyć watchdoga. Jest to preferowane, jeśli masz krytyczny system czasu rzeczywistego.
  • Zasadniczo używaj MCU odpowiedniego dla tego rodzaju systemów, a nie jakiegoś ogólnego puchu głównego nurtu, który otrzymałeś w pakiecie płatków kukurydzianych. Niemal każdy producent MCU ma obecnie wyspecjalizowane MCU zaprojektowane do zastosowań związanych z bezpieczeństwem (TI, Freescale, Renesas, ST, Infineon itp.). Mają wiele wbudowanych funkcji bezpieczeństwa, w tym rdzenie blokujące: co oznacza, że ​​2 rdzenie CPU wykonują ten sam kod i muszą się ze sobą zgadzać.
  • WAŻNE: Musisz zapewnić integralność wewnętrznych rejestrów MCU. Wszystkie rejestry kontroli i statusu urządzeń peryferyjnych, które można zapisywać, mogą znajdować się w pamięci RAM i dlatego są podatne na ataki.

    Aby uchronić się przed uszkodzeniem rejestrów, najlepiej wybrać mikrokontroler z wbudowanymi funkcjami rejestrów „jednokrotnego zapisu”. Ponadto należy przechowywać wartości domyślne wszystkich rejestrów sprzętowych w NVM i regularnie kopiować te wartości do rejestrów. W ten sam sposób możesz zapewnić integralność ważnych zmiennych.

    Uwaga: zawsze używaj programowania obronnego. Oznacza to, że musisz skonfigurować wszystkie rejestry w MCU, a nie tylko te używane przez aplikację. Nie chcesz, aby przypadkowe urządzenia peryferyjne nagle się obudziły.

  • Istnieją różne metody sprawdzania błędów w pamięci RAM lub NVM: sumy kontrolne, „wzorce kroczące”, oprogramowanie ECC itp. Obecnie najlepszym rozwiązaniem jest nieużywanie żadnego z nich, ale użycie MCU z wbudowanym ECC i podobne kontrole. Ponieważ robienie tego w oprogramowaniu jest skomplikowane, a samo sprawdzenie błędów może w związku z tym powodować błędy i nieoczekiwane problemy.

  • Użyj redundancji. Możesz przechowywać zarówno ulotną, jak i nieulotną pamięć w dwóch identycznych „lustrzanych” segmentach, które zawsze muszą być równoważne. Do każdego segmentu może być dołączona suma kontrolna CRC.
  • Unikaj używania pamięci zewnętrznych poza MCU.
  • Zaimplementuj domyślną procedurę obsługi przerwań / domyślną procedurę obsługi wyjątków dla wszystkich możliwych przerwań / wyjątków. Nawet te, których nie używasz. Domyślna procedura nie powinna robić nic poza wyłączeniem własnego źródła przerwań.
  • Zrozum i przyjmij koncepcję programowania obronnego. Oznacza to, że Twój program musi obsługiwać wszystkie możliwe przypadki, nawet te, które nie mogą wystąpić w teorii. Przykłady .

    Wysokiej jakości oprogramowanie krytyczne dla misji wykrywa jak najwięcej błędów, a następnie ignoruje je w bezpieczny sposób.

  • Nigdy nie pisz programów, które opierają się na źle określonym zachowaniu. Jest prawdopodobne, że takie zachowanie może się drastycznie zmienić w przypadku nieoczekiwanych zmian sprzętowych spowodowanych promieniowaniem lub zakłóceniami elektromagnetycznymi. Najlepszym sposobem na zapewnienie, że Twój program jest wolny od takich bzdur, jest użycie standardu kodowania, takiego jak MISRA, wraz z narzędziem do analizy statycznej. Pomoże to również w programowaniu obronnym i usuwaniu błędów (dlaczego nie chcesz wykrywać błędów w jakiejkolwiek aplikacji?).
  • WAŻNE: Nie wdrażaj żadnego polegania na domyślnych wartościach statycznych zmiennych czasu przechowywania. Oznacza to, że nie ufaj domyślnej zawartości .datalub .bss. Od momentu inicjalizacji do momentu, w którym zmienna jest rzeczywiście używana, może upłynąć dowolna ilość czasu, może być dużo czasu na uszkodzenie pamięci RAM. Zamiast tego napisz program, aby wszystkie takie zmienne były ustawiane z NVM w czasie wykonywania, tuż przed pierwszym użyciem takiej zmiennej.

    W praktyce oznacza to, że jeśli zmienna zostanie zadeklarowana w zakresie pliku lub jako static, nie należy nigdy używać jej =do inicjalizacji (lub można, ale jest to bezcelowe, ponieważ nie można w żaden sposób polegać na wartości). Zawsze ustaw go w czasie wykonywania, tuż przed użyciem. Jeśli możliwe jest wielokrotne aktualizowanie takich zmiennych z NVM, zrób to.

    Podobnie w C ++, nie polegaj na konstruktorach dla zmiennych czasu przechowywania statycznego. Poproś konstruktorów o wywołanie publicznej procedury „konfiguracji”, którą możesz wywołać później w czasie wykonywania, bezpośrednio z aplikacji wywołującej.

    Jeśli to możliwe, usuń kod startowy „kopiuj” , który całkowicie inicjuje .datai .bss(i wywołuje konstruktory C ++), aby uzyskać błędy linkera, jeśli napiszesz na nim kod. Wiele kompilatorów ma możliwość pominięcia tego, zwykle nazywanego „minimalnym / szybkim uruchomieniem” lub podobnym.

    Oznacza to, że wszelkie biblioteki zewnętrzne muszą zostać sprawdzone, aby nie zawierały takich zależności.

  • Zaimplementuj i zdefiniuj bezpieczny stan programu, do którego powrócisz w przypadku błędów krytycznych.

  • Wdrożenie systemu raportów o błędach / dziennika błędów jest zawsze pomocne.
Lundin
źródło
Jednym ze sposobów radzenia sobie z uszkodzonymi booleanami (jak w twoim przykładowym linku) może być TRUEzrównanie z 0xffffffffużyciem POPCNTprogu.
wizzwizz4
@ wizzwizz4 Biorąc pod uwagę, że wartość 0xff jest wartością domyślną niezaprogramowanej komórki flash, brzmi to jak zły pomysł.
Lundin,
%01010101010101010101010101010101, XOR, a następnie POPCNT?
wizzwizz4,
1
@ wizzwizz4 Lub po prostu wartość 0x1, zgodnie z wymaganiami standardu C.
Lundin,
1
@ wizzwizz4 Dlaczego używasz niektórych lub wszystkich wyżej wymienionych metod (ECC, CRC itp.). W przeciwnym razie promień kosmiczny może równie dobrze obrócić o jeden bit w twojej .textsekcji, zmieniając kod operacyjny lub podobny.
Lundin,
34

Możliwe jest użycie C do pisania programów, które zachowują się solidnie w takich środowiskach, ale tylko wtedy, gdy większość form optymalizacji kompilatora jest wyłączona. Kompilatory optymalizujące zostały zaprojektowane w celu zastąpienia wielu pozornie redundantnych wzorców kodowania wzorcami „bardziej wydajnymi” i mogą nie mieć pojęcia, że ​​programista testuje, x==42gdy kompilator wie, że nie ma innego sposobu, xaby zatrzymać coś innego, ponieważ programista chce temu zapobiec wykonanie określonego kodu z xzachowaniem jakiejś innej wartości - nawet w przypadkach, w których jedynym sposobem na utrzymanie tej wartości byłby, gdyby system otrzymał jakąś usterkę elektryczną.

Deklarowanie zmiennych jako volatileczęsto pomocne, ale może nie być panaceum. Szczególnie ważne jest, aby pamiętać, że bezpieczne kodowanie często wymaga, aby niebezpieczne operacje posiadały blokady sprzętowe, które wymagają wielu kroków do aktywacji, i aby kod był zapisywany przy użyciu wzorca:

... code that checks system state
if (system_state_favors_activation)
{
  prepare_for_activation();
  ... code that checks system state again
  if (system_state_is_valid)
  {
    if (system_state_favors_activation)
      trigger_activation();
  }
  else
    perform_safety_shutdown_and_restart();
}
cancel_preparations();

Jeśli kompilator tłumaczy kod w stosunkowo dosłowny sposób i jeśli wszystkie sprawdzenia stanu systemu są powtarzane po prepare_for_activation(), system może być odporny na prawie każde możliwe zdarzenie pojedynczej usterki, nawet te, które arbitralnie uszkodzą licznik programu i stos. Jeśli usterka wystąpi tuż po wywołaniu prepare_for_activation(), oznacza to, że aktywacja byłaby odpowiednia (ponieważ nie było innego powodu, prepare_for_activation()który zostałby wywołany przed usterką). Jeśli usterka spowoduje prepare_for_activation()nieprawidłowe dotarcie kodu , ale nie będzie żadnych następnych zdarzeń usterki, kod nie będzie mógł później dotrzeć trigger_activation()bez przejścia sprawdzania poprawności lub wywołania anulowania_preparacji w pierwszej kolejności [jeśli stos zostanie uszkodzony, wykonanie może przejść do określonego miejsca przed chwilątrigger_activation()po kontekście, który wywołał prepare_for_activation()zwraca, ale wywołanie do cancel_preparations()miałoby miejsce między wywołaniami do prepare_for_activation()i trigger_activation(), co uczyniłoby to drugie nieszkodliwym.

Taki kod może być bezpieczny w tradycyjnym C, ale nie w nowoczesnych kompilatorach C. Takie kompilatory mogą być bardzo niebezpieczne w tego rodzaju środowisku, ponieważ agresywne starają się zawierać tylko kod, który będzie odpowiedni w sytuacjach, które mogłyby wystąpić za pomocą dobrze zdefiniowanego mechanizmu i których wynikające konsekwencje byłyby również dobrze określone. Kod, którego celem byłoby wykrywanie i usuwanie awarii po awarii, w niektórych przypadkach może pogorszyć sytuację. Jeśli kompilator ustali, że próba odzyskania w niektórych przypadkach wywołałaby niezdefiniowane zachowanie, może wywnioskować, że warunki, które wymagałyby takiego odzyskania w takich przypadkach, nie mogą wystąpić, eliminując w ten sposób kod, który by je sprawdził.

supercat
źródło
6
Mówiąc realistycznie, ile jest nowoczesnych kompilatorów, które nie oferują -O0przełącznika? GCC zrobi wiele dziwnych rzeczy, jeśli dasz mu na to zgodę , ale jeśli poprosisz, aby tego nie robił, ogólnie może być dość dosłowny.
Leushenko,
24
Przepraszamy, ale ten pomysł jest zasadniczo niebezpieczny. Wyłączenie optymalizacji powoduje spowolnienie programu. Lub innymi słowy potrzebujesz szybszego procesora. Tak się składa, że ​​szybsze procesory są szybsze, ponieważ ładunki na bramkach tranzystorów są mniejsze. To czyni je znacznie bardziej podatnymi na promieniowanie. Lepszą strategią jest użycie powolnego, dużego układu scalonego, w którym pojedynczy foton jest znacznie mniej podatny na przewrócenie się i przyspieszenie -O2.
MSalters
27
Drugim powodem, dla którego -O0jest to zły pomysł, jest to, że emituje on znacznie więcej bezużytecznych instrukcji. Przykład: wywołanie bez wstawiania zawiera instrukcje dotyczące zapisywania rejestrów, wykonywania połączenia, przywracania rejestrów. Wszystko to może zawieść. Instrukcja, której nie ma, nie może zawieść.
MSalters
15
Jest jeszcze jeden powód, dla -O0którego zły pomysł: ma tendencję do przechowywania zmiennych w pamięci zamiast w rejestrze. Teraz nie jest pewne, czy pamięć jest bardziej podatna na SEU, ale dane w locie są bardziej podatne niż dane w spoczynku. Należy unikać niepotrzebnego przenoszenia danych i -O2pomaga w tym.
MSalters
9
@MSalters: Ważne jest nie to, aby dane były odporne na zakłócenia, ale raczej, aby system był w stanie poradzić sobie z zakłóceniami w sposób spełniający wymagania. W wielu kompilatorach wyłączenie wszystkich optymalizacji daje kod, który wykonuje nadmierną liczbę ruchów rejestr-rejestr, co jest złe, ale przechowywanie zmiennych w pamięci jest bezpieczniejsze z punktu widzenia odzyskiwania niż przechowywanie ich w rejestrach. Jeśli jedna ma dwie zmienne w pamięci, które powinny spełniać jakiś warunek (np. v1=v2+0xCAFEBABEI wszystkie aktualizacje tych dwóch zmiennych zostały wykonane ...
supercat,
28

To niezwykle szeroki temat. Zasadniczo nie można tak naprawdę odzyskać sprawności po uszkodzeniu pamięci, ale można przynajmniej spróbować szybko zakończyć się niepowodzeniem . Oto kilka technik, których możesz użyć:

  • stałe dane kontrolne . Jeśli masz jakieś dane konfiguracyjne, które pozostają stałe przez długi czas (w tym skonfigurowane rejestry sprzętowe), oblicz jego sumę kontrolną przy inicjalizacji i okresowo ją weryfikuj. Kiedy zobaczysz niedopasowanie, czas ponownie zainicjować lub zresetować.

  • przechowuj zmienne z redundancją . Jeśli masz ważną zmienną x, napisać swoją wartość x1, x2a x3i odczytać go jako (x1 == x2) ? x2 : x3.

  • wdrożyć monitorowanie przepływu programu . XOR globalna flaga o unikalnej wartości w ważnych funkcjach / gałęziach wywoływanych z głównej pętli. Uruchomienie programu w środowisku wolnym od promieniowania z niemal 100% pokryciem testowym powinno dać ci listę dopuszczalnych wartości flagi na końcu cyklu. Zresetuj, jeśli zobaczysz odchylenia.

  • monitorować wskaźnik stosu . Na początku głównej pętli porównaj wskaźnik stosu z jego oczekiwaną wartością. Resetuj po odchyleniu.

Dmitrij Grigoriew
źródło
27

To, co może ci pomóc, to strażnik . Strażnicy byli szeroko wykorzystywani w komputerach przemysłowych w latach 80. Awarie sprzętowe były wtedy znacznie częstsze - inna odpowiedź dotyczy również tego okresu.

Watchdog to połączona funkcja sprzętowo-programowa. Sprzęt jest prostym licznikiem, który odlicza od liczby (powiedzmy 1023) do zera. Można zastosować TTL lub inną logikę.

Oprogramowanie zostało tak zaprojektowane, aby jedna procedura monitorowała prawidłowe działanie wszystkich niezbędnych systemów. Jeśli ta procedura zakończy się poprawnie = znajdzie komputer działający poprawnie, ustawi licznik z powrotem na 1023.

Ogólny projekt jest taki, że w normalnych okolicznościach oprogramowanie zapobiega zerowaniu licznika sprzętowego. W przypadku, gdy licznik osiągnie zero, sprzęt licznika wykonuje swoje jedyne zadanie i resetuje cały system. Z perspektywy licznika zero wynosi 1024, a licznik kontynuuje odliczanie ponownie.

Ten watchdog zapewnia, że ​​podłączony komputer zostanie zrestartowany w wielu, wielu przypadkach awarii. Muszę przyznać, że nie znam sprzętu, który jest w stanie wykonać taką funkcję na dzisiejszych komputerach. Interfejsy do zewnętrznego sprzętu są teraz o wiele bardziej złożone niż kiedyś.

Nieodłączną wadą watchdoga jest to, że system nie jest dostępny od momentu awarii, aż licznik watchdoga osiągnie zero + czas ponownego uruchomienia. Chociaż czas ten jest na ogół znacznie krótszy niż jakakolwiek interwencja zewnętrzna lub ludzka, obsługiwane urządzenia będą musiały być w stanie działać bez kontroli komputera w tym czasie.

OldFrank
źródło
9
Kontrole binarne ze standardowymi układami scalonymi TTL są rzeczywiście rozwiązaniem z lat 80. Nie rób tego Dzisiaj nie ma na rynku ani jednego MCU bez wbudowanego obwodu nadzoru. Wszystko, co musisz sprawdzić, to czy wbudowany watchdog ma indywidualne źródło zegara (dobra, najprawdopodobniej przypadek) lub czy dziedziczy swój zegar z zegara systemowego (zły).
Lundin
1
Lub zaimplementuj watchdog w FPGA: ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/20130013486.pdf
nr
2
Nawiasem mówiąc, nadal jest szeroko stosowany we wbudowanych procesorach.
Graham
5
@Peter Mortensen Łagodnie przerwij edycję przy każdej odpowiedzi na to pytanie. To nie jest Wikipedia, a te linki nie są pomocne (i jestem pewien, że każdy wie jak znaleźć Wikipedię ...). Wiele zmian jest niepoprawnych, ponieważ nie znasz tematu. Robię wycofywanie twoich niepoprawnych zmian, kiedy je spotykam. Nie zmieniasz tego wątku lepiej, ale gorzej. Zatrzymaj edycję.
Lundin
Jack Ganssle ma dobry artykuł na temat psów
Igor Skochinsky
23

Ta odpowiedź zakłada, że ​​obawiasz się, że system działa poprawnie, oprócz tego, że jest to system o minimalnym koszcie lub szybki; większość osób bawiących się rzeczami radioaktywnymi ceni poprawność / bezpieczeństwo w stosunku do prędkości / kosztów

Kilka osób zasugerowało zmiany sprzętowe, które możesz wprowadzić (dobrze - w odpowiedziach jest już wiele dobrych rzeczy i nie zamierzam powtarzać wszystkiego), a inni zasugerowali nadmiarowość (co do zasady świetne), ale nie sądzę ktoś sugerował, jak ta nadmiarowość może działać w praktyce. Jak przestawić się na awarię? Skąd wiesz, że coś poszło nie tak? Wiele technologii działa na zasadzie, że wszystko będzie działać, a porażka jest więc trudną sprawą. Jednak niektóre technologie przetwarzania rozproszonego zaprojektowane na skalę oczekują awarii (w końcu przy wystarczającej skali, awaria jednego z wielu węzłów jest nieunikniona w przypadku dowolnego MTBF dla jednego węzła); możesz wykorzystać to dla swojego środowiska.

Oto kilka pomysłów:

  • Upewnij się, że cały sprzęt jest replikowany nrazy (gdzie njest większy niż 2, a najlepiej nieparzysty) i że każdy element sprzętowy może komunikować się ze sobą. Ethernet jest jednym oczywistym sposobem, aby to zrobić, ale istnieje wiele innych, znacznie prostszych tras, które zapewniłyby lepszą ochronę (np. CAN). Minimalizuj typowe komponenty (nawet zasilacze). Może to na przykład oznaczać próbkowanie danych wejściowych ADC w wielu miejscach.

  • Upewnij się, że stan aplikacji znajduje się w jednym miejscu, np. W maszynie skończonej. Może to być całkowicie pamięć RAM, ale nie wyklucza stabilnego przechowywania. Będzie zatem przechowywany w kilku miejscach.

  • Przyjęcie protokołu kworum dla zmian stanu. Zobacz na przykład RAFT . Podczas pracy w C ++ istnieją do tego dobrze znane biblioteki. Zmiany w FSM zostaną wprowadzone tylko za zgodą większości węzłów. Skorzystaj ze znanej dobrej biblioteki dla stosu protokołów i protokołu kworum, a nie samodzielnie rozwijaj bibliotekę, albo cała Twoja dobra praca nad redundancją zostanie zmarnowana, gdy protokół kworum się rozłączy.

  • Upewnij się, że suma kontrolna (np. CRC / SHA) twojego FSM, i przechowuj CRC / SHA w samym FSM (jak również przesyłaj w wiadomości i sprawdzaj same wiadomości). Poproś węzły, aby regularnie sprawdzały swój FSM pod kątem tej sumy kontrolnej, sumy kontrolnej wiadomości przychodzących i sprawdzały, czy ich suma kontrolna odpowiada sumie kontrolnej kworum.

  • Zbuduj w systemie jak najwięcej innych wewnętrznych kontroli, dzięki czemu węzły, które wykryją swój własny restart, uruchamiają się ponownie (jest to lepsze niż kontynuowanie połowy pracy, pod warunkiem, że masz wystarczającą liczbę węzłów). Spróbuj zrezygnować z czystego usuwania się z kworum podczas ponownego uruchamiania na wypadek, gdyby nie pojawiły się ponownie. Po ponownym uruchomieniu uruchom sumę kontrolną obrazu oprogramowania (i wszystkiego, co ładują) i wykonaj pełny test pamięci RAM przed ponownym wprowadzeniem się do kworum.

  • Do obsługi używaj sprzętu, ale rób to ostrożnie. Możesz na przykład uzyskać pamięć RAM ECC i regularnie ją czytać / zapisywać, aby poprawić błędy ECC (i panikować, jeśli błędu nie da się naprawić). Jednak (z pamięci) statyczna pamięć RAM jest o wiele bardziej tolerancyjna na promieniowanie jonizujące niż pamięć DRAM, więc może być lepiej zamiast tego użyć statycznej pamięci DRAM. Zobacz także pierwszy punkt w „rzeczach, których nie zrobiłbym”.

Załóżmy, że masz 1% szansy na awarię dowolnego węzła w ciągu jednego dnia, i udawajmy, że możesz całkowicie uniezależnić awarie. Przy 5 węzłach będziesz potrzebować trzech, aby zawieść w ciągu jednego dnia, co daje 0,00001% szansy. Z więcej, cóż, masz pomysł.

Czego bym nie zrobił:

  • Nie doceniaj wartości braku problemu na początek. O ile waga nie stanowi problemu, duży blok metalu wokół twojego urządzenia będzie znacznie tańszym i bardziej niezawodnym rozwiązaniem, niż może wymyślić zespół programistów. Ditto optyczne sprzężenie sygnałów wejściowych EMI jest problemem itp. Niezależnie od tego, staraj się podczas pozyskiwania komponentów, aby pozyskiwać te, które najlepiej pasują do promieniowania jonizującego.

  • Rzuć własne algorytmy . Ludzie robili to wcześniej. Skorzystaj z ich pracy. Tolerancja błędów i algorytmy rozproszone są trudne. W miarę możliwości korzystaj z pracy innych osób.

  • Używaj skomplikowanych ustawień kompilatora w naiwnej nadziei, że wykryjesz więcej awarii. Jeśli masz szczęście, możesz wykryć więcej awarii. Bardziej prawdopodobne jest, że użyjesz ścieżki kodu w kompilatorze, który został mniej przetestowany, szczególnie jeśli sam go rzuciłeś.

  • Używaj technik, które nie zostały przetestowane w twoim środowisku. Większość osób piszących oprogramowanie o wysokiej dostępności musi symulować tryby awarii, aby sprawdzić, czy ich HA działa poprawnie, i w rezultacie pomija wiele trybów awarii. Jesteś w „szczęśliwej” sytuacji, gdy często popełniają awarie na żądanie. Przetestuj więc każdą technikę i upewnij się, że jej zastosowanie poprawia MTBF o kwotę przekraczającą złożoność jej wprowadzenia (ze złożonością pojawiają się błędy). Szczególnie stosuj to do moich rad dotyczących algorytmów kworum itp.

w płomieniach
źródło
2
Ethernet prawdopodobnie nie jest świetnym pomysłem do zastosowania w aplikacjach o kluczowym znaczeniu. Podobnie jak I2C, poza samą płytką drukowaną. Coś tak wytrzymałego jak CAN byłoby znacznie bardziej odpowiednie.
Lundin
1
@Lundin Fair point, choć wszystko, co jest podłączone optycznie (w tym Ethernet), powinno być OK.
abligh 28.04.16
1
Fizyczne media to nie tyle powód, dla którego Ethernet jest nieodpowiedni, ale brak deterministycznego zachowania w czasie rzeczywistym. Chociaż przypuszczam, że istnieją obecnie sposoby na zapewnienie nieco niezawodnego Ethernetu, po prostu pogrupowałem go razem z elektroniką komercyjną / zabawkową ze starego przyzwyczajenia.
Lundin
1
@Lundin jest to słuszna kwestia, ale ponieważ sugeruję użycie go do uruchomienia RAFT, w algorytmie (teoretycznie) nie będzie deterministyczne zachowanie w czasie rzeczywistym (np. Jednoczesne wybory lidera skutkujące ponownym wyborem podobnym do CSMA / PŁYTA CD). Jeśli potrzebne jest ścisłe zachowanie w czasie rzeczywistym, zapewne moja odpowiedź ma więcej problemów niż sieć Ethernet (i zauważ na początku mojej odpowiedzi, że powiedziałem, że „poprawne” często odbywa się kosztem „szybkiego”). Włączyłem twój punkt do CAN.
abligh 29.04.16
1
@Lundin: Żaden system, który obejmuje aspekty asynchroniczne, nie może być całkowicie niedeterministyczny. Myślę, że najgorsze zachowanie Ethernet może zostać ograniczone przy braku zakłóceń sprzętowych, jeśli protokoły oprogramowania są skonfigurowane w odpowiedni sposób, a urządzenia mają unikalne identyfikatory i istnieje znana granica liczby urządzeń (im więcej urządzeń, tym większe najgorsza liczba ponownych prób).
supercat
23

Skoro konkretnie pytasz o rozwiązania programowe i używasz C ++, dlaczego nie użyć przeciążenia operatora, aby stworzyć własne, bezpieczne typy danych? Na przykład:

Zamiast korzystać uint32_t(i double, int64_tetc), tworzyć własne SAFE_uint32_tktóra zawiera wielokrotność (minimum 3) z uint32_t. Przeciąż wszystkie operacje, które chcesz wykonać (* + - / << >> = ==! = Itd.) I spraw, aby przeciążone operacje wykonały niezależnie dla każdej wartości wewnętrznej, tzn. Nie rób tego ani razu i skopiuj wynik. Zarówno przed, jak i po, sprawdź, czy wszystkie wartości wewnętrzne są zgodne. Jeśli wartości się nie zgadzają, możesz zaktualizować niewłaściwy do najbardziej powszechnego. Jeśli nie ma najczęstszej wartości, możesz bezpiecznie powiadomić o wystąpieniu błędu.

W ten sposób nie ma znaczenia, czy nastąpi uszkodzenie w ALU, rejestrach, pamięci RAM lub w autobusie, nadal będziesz mieć wiele prób i bardzo duże szanse na złapanie błędów. Należy jednak pamiętać, że działa to tylko w przypadku zmiennych, które można zastąpić - na przykład wskaźnik stosu nadal będzie podatny.

Historia poboczna: napotkałem podobny problem, również na starym układzie ARM. Okazało się, że jest to zestaw narzędzi, który korzystał ze starej wersji GCC, która wraz z konkretnym układem, którego użyliśmy, spowodowała błąd w niektórych przypadkach krawędzi, który (czasami) uszkodziłby wartości przekazywane do funkcji. Upewnij się, że twoje urządzenie nie ma żadnych problemów przed obwinianiem go o aktywność radiową, i tak, czasami jest to błąd kompilatora =)

jkflying
źródło
1
Kilka z tych sugestii ma coś podobnego do myślenia o „wielobitowym sprawdzaniu rozsądku” w zakresie wykrywania korupcji, ale naprawdę podoba mi się ten z sugestią krytycznych dla bezpieczeństwa niestandardowych typów danych
WearyWanderer
2
Na świecie istnieją systemy, w których każdy redundantny węzeł został zaprojektowany i opracowany przez różne zespoły z arbitrem, aby upewnić się, że przypadkowo nie zdecydowali się na te same rozwiązania. W ten sposób nie wszystkie spadają z powodu tego samego błędu, a podobne stany przejściowe nie wykazują podobnych trybów awarii.
jwdonahue
16

Oświadczenie: Nie jestem specjalistą od radioaktywności ani nie pracowałem dla tego rodzaju aplikacji. Ale pracowałem nad miękkimi błędami i redundancją w celu długoterminowej archiwizacji krytycznych danych, która jest nieco powiązana (ten sam problem, różne cele).

Moim zdaniem główny problem z radioaktywnością polega na tym, że radioaktywność może zmieniać bity, a zatem radioaktywność może / będzie manipulować każdą pamięcią cyfrową . Błędy te są zwykle nazywane błędami miękkimi , gniciem bitów itp.

Pytanie brzmi zatem: jak niezawodnie obliczyć, kiedy twoja pamięć jest zawodna?

Aby znacznie zmniejszyć częstość błędów miękkich (kosztem narzutu obliczeniowego, ponieważ będą to głównie rozwiązania programowe), możesz:

  • polegać na starym dobrym schemacie redundancji , a dokładniej na bardziej wydajnych kodach korygujących błędy (ten sam cel, ale z bardziej inteligentnymi algorytmami, dzięki czemu można odzyskać więcej bitów przy mniejszej redundancji). Czasami jest to (błędnie) nazywane sumowaniem kontrolnym. Dzięki tego rodzaju rozwiązaniu będziesz musiał w dowolnym momencie zapisać pełny stan swojego programu w zmiennej głównej / klasie (lub strukturze?), Obliczyć ECC i sprawdzić, czy ECC jest poprawny przed zrobieniem czegokolwiek, a jeśli nie, napraw pola. To rozwiązanie nie gwarantuje jednak, że twoje oprogramowanie może działać (po prostu, że będzie działało poprawnie, gdy będzie to możliwe, lub przestanie działać, jeśli nie, ponieważ ECC może powiedzieć ci, czy coś jest nie tak, iw takim przypadku możesz zatrzymać swoje oprogramowanie, abyś mógł nie otrzymuj fałszywych wyników).

  • lub możesz użyć elastycznych algorytmicznych struktur danych, co gwarantuje, do pewnego stopnia, że ​​Twój program będzie nadal dawał prawidłowe wyniki nawet w przypadku wystąpienia błędów miękkich. Algorytmy te można postrzegać jako połączenie typowych struktur algorytmicznych z natywnymi domieszkowanymi schematami ECC, ale jest to o wiele bardziej odporne, ponieważ schemat odporności jest ściśle związany ze strukturą, dzięki czemu nie trzeba kodować dodatkowych procedur aby sprawdzić ECC, i zwykle są one znacznie szybsze. Struktury te zapewniają sposób, że Twój program będzie działał w każdych warunkach, aż do teoretycznej granicy błędów miękkich. Możesz także łączyć te elastyczne struktury ze schematem redundancji / ECC dla dodatkowego bezpieczeństwa (lub kodować najważniejsze struktury danych jako odporne, a resztę - dane jednorazowe, które możesz ponownie obliczyć z głównych struktur danych,

Jeśli jesteś zainteresowany elastycznymi strukturami danych (które są najnowszą, ale ekscytującą, nową dziedziną w zakresie algorytmiki i inżynierii redundancji), radzę przeczytać następujące dokumenty:

  • Wprowadzenie do struktur danych o odpornych algorytmach autorstwa Giuseppe F.Italiano, Universita di Roma „Tor Vergata”

  • Christiano, P., Demaine, ED i Kishore, S. (2011). Bezstratne, odporne na uszkodzenia struktury danych z dodatkowym obciążeniem. W Algorytmach i strukturach danych (str. 243–254). Springer Berlin Heidelberg.

  • Ferraro-Petrillo, U., Grandoni, F. i Italiano, GF (2013). Struktury danych odporne na uszkodzenia pamięci: eksperymentalne badanie słowników. Journal of Experimental Algorytmics (JEA), 18, 1-6.

  • Italiano, GF (2010). Odporne algorytmy i struktury danych. W Algorytmach i złożoności (s. 13–24). Springer Berlin Heidelberg.

Jeśli chcesz dowiedzieć się więcej na temat odpornych struktur danych, możesz sprawdzić prace Giuseppe F. Italiano (i przejść przez referencje) oraz model Faulty-RAM (wprowadzony w Finocchi i in. 2005; Finocchi i Italiano 2008).

/ EDIT: Zilustrowałem zapobieganie / odzyskiwanie po błędach programowych głównie dla pamięci RAM i przechowywania danych, ale nie mówiłem o błędach obliczeniowych (CPU) . Inne odpowiedzi wskazywały już na stosowanie transakcji atomowych, takich jak w bazach danych, dlatego zaproponuję inny, prostszy schemat: redundancja i głosowanie większościowe .

Chodzi o to, że po prostu wykonujesz x razy to samo obliczenie dla każdego obliczenia, które musisz wykonać, i zapisujesz wynik w x różnych zmiennych (przy x> = 3). Następnie możesz porównać swoje zmienne x :

  • jeśli wszyscy się zgadzają, to nie ma w ogóle błędu obliczeniowego.
  • jeśli się nie zgadzają, możesz użyć większości głosów, aby uzyskać poprawną wartość, a ponieważ oznacza to, że obliczenia zostały częściowo uszkodzone, możesz również uruchomić skanowanie stanu systemu / programu, aby sprawdzić, czy reszta jest w porządku.
  • jeśli większość głosów nie może określić zwycięzcy (wszystkie wartości x są różne), jest to doskonały sygnał do uruchomienia procedury awaryjnej (ponowne uruchomienie, powiadomienie użytkownika itp.).

Ten schemat redundancji jest bardzo szybki w porównaniu do ECC (praktycznie O (1)) i zapewnia wyraźny sygnał, gdy potrzebujesz zabezpieczenia przed awarią . Większość głosów jest również (prawie) gwarantowana, aby nigdy nie wytwarzać uszkodzonych danych wyjściowych, a także aby odzyskać po drobnych błędach obliczeniowych , ponieważ prawdopodobieństwo, że obliczenia x dają takie same dane wyjściowe, jest nieskończenie małe (ponieważ istnieje ogromna liczba możliwych wyników, prawie niemożliwe jest losowo uzyskaj 3 razy takie same, jeszcze mniejsze szanse, jeśli x> 3).

Tak więc przy większości głosów jesteś bezpieczny przed uszkodzonym wyjściem, a dzięki redundancji x == 3 możesz odzyskać 1 błąd (przy x == 4 można odzyskać 2 błędy itp. - dokładne równanie to nb_error_recoverable == (x-2)gdzie x jest liczbą powtórzeń obliczeń, ponieważ potrzebujesz co najmniej 2 zgodnych obliczeń, aby odzyskać większość głosów).

Wadą jest to, że musisz obliczyć x razy zamiast raz, więc masz dodatkowy koszt obliczeniowy, ale liniowa złożoność tak asymptotycznie, że nie tracisz wiele za korzyści, które zyskujesz. Szybkim sposobem na głosowanie większością głosów jest obliczenie trybu na tablicy, ale można również użyć filtra mediany.

Ponadto, jeśli chcesz mieć pewność, że obliczenia są przeprowadzane poprawnie, jeśli możesz stworzyć własny sprzęt, możesz zbudować urządzenie z x procesorami i połączyć system tak, aby obliczenia były automatycznie duplikowane na x procesorach z większością głosów na końcu mechanicznie (na przykład za pomocą bramek AND / OR). Jest to często realizowane w samolotach i urządzeniach o kluczowym znaczeniu (patrz potrójna redundancja modułowa ). W ten sposób nie będziesz mieć żadnych narzutów obliczeniowych (ponieważ dodatkowe obliczenia będą wykonywane równolegle) i masz kolejną warstwę ochrony przed błędami miękkimi (ponieważ duplikacja obliczeń i głosowanie większościowe będą zarządzane bezpośrednio przez sprzęt, a nie przez oprogramowanie - które może łatwiej ulec uszkodzeniu, ponieważ program jest po prostu bitami zapisanymi w pamięci ...).

gaboryczny
źródło
9

Jedna kwestia, o której nikt chyba nie wspomniał. Mówisz, że rozwijasz się w GCC i kompilujesz na ARM. Skąd wiesz, że nie masz kodu, który zakłada założenia dotyczące wolnej pamięci RAM, rozmiaru liczb całkowitych, rozmiaru wskaźnika, ile czasu zajmuje wykonanie określonej operacji, jak długo system będzie działał w sposób ciągły, czy różnych podobnych rzeczy? To bardzo częsty problem.

Odpowiedzią są zwykle zautomatyzowane testy jednostkowe. Napisz wiązki testowe, które ćwiczą kod w systemie programistycznym, a następnie uruchom te same wiązki testowe w systemie docelowym. Poszukaj różnic!

Sprawdź także erratę na swoim urządzeniu osadzonym. Może się okazać, że jest coś takiego: „nie rób tego, bo się zawiesi, więc włącz tę opcję kompilatora, a kompilator obejdzie to”.

Krótko mówiąc, najprawdopodobniej źródłem awarii są błędy w kodzie. Dopóki nie upewnisz się, że tak nie jest, nie martw się (jeszcze) o więcej ezoterycznych trybów awarii.

Graham
źródło
1
Rzeczywiście, nigdzie w teście pytania autor nie wspomina, że ​​aplikacja działa dobrze poza środowiskiem radioaktywnym.
Marc.2377,
9

Chcesz ponad 3 niewolników z urządzeniem nadrzędnym poza środowiskiem promieniowania. Wszystkie wejścia / wyjścia przechodzą przez moduł główny, który zawiera mechanizm głosowania i / lub ponawiania. Niewolnicy muszą mieć każdego strażnika sprzętowego, a wezwanie do ich uderzenia powinno być otoczone CRC lub podobnymi, aby zmniejszyć prawdopodobieństwo mimowolnego uderzenia. Podbijanie powinno być kontrolowane przez urządzenie nadrzędne, więc utracone połączenie z urządzeniem nadrzędnym oznacza ponowne uruchomienie w ciągu kilku sekund.

Jedną z zalet tego rozwiązania jest to, że można używać tego samego interfejsu API dla urządzenia master, jak i urządzeń slave, dzięki czemu nadmiarowość staje się funkcją przezroczystą.

Edycja: Na podstawie komentarzy uważam za konieczne wyjaśnienie „pomysłu CRC”. Prawdopodobieństwo, że Slave podbił swój własny organ nadzorczy, jest bliskie zeru, jeśli otoczysz wybrzuszenie CRC lub podsumujesz kontrole losowych danych z mastera. Te losowe dane są wysyłane z mastera tylko wtedy, gdy kontrolowany slave jest wyrównany z innymi. Losowe dane i CRC / skrót są natychmiast usuwane po każdym uderzeniu. Częstotliwość uderzeń master-slave powinna być większa niż dwukrotnie limit czasu watchdoga. Dane wysyłane z urządzenia nadrzędnego są generowane jednoznacznie za każdym razem.

Jonas Byström
źródło
7
Próbuję pojąć scenariusz, w którym możesz mieć mistrza poza środowiskiem promieniowania, zdolnego do niezawodnej komunikacji z niewolnikami wewnątrz środowiska promieniowania, w którym nie można po prostu umieścić niewolników poza środowiskiem promieniowania.
fostandy
1
@ostandy: Niewolnicy mierzą lub kontrolują za pomocą sprzętu, który potrzebuje kontrolera. Powiedz licznik Geigera. Master nie potrzebuje niezawodnej komunikacji z powodu redundancji slave.
Jonas Byström
4
Wprowadzenie mistrza nie oznacza automatycznie zwiększenia bezpieczeństwa. Jeśli slave x oszalał z powodu uszkodzenia pamięci, a więc wielokrotnie powtarzał sobie: „master is here, master is happy”, to żadna ilość CRC ani szczekanych rozkazów mastera go nie uratuje. Musiałbyś dać mistrzowi możliwość odcięcia mocy tego niewolnika. A jeśli wystąpi błąd o wspólnej przyczynie, dodanie większej liczby urządzeń podrzędnych nie zwiększy bezpieczeństwa. Należy również pamiętać, że liczba błędów oprogramowania i liczba rzeczy, które mogą się zepsuć, rosną wraz ze złożonością.
Lundin
5
Biorąc to pod uwagę, oczywiście byłoby miło „zlecić” jak najwięcej programu programowi w miejscu mniej narażonym, jednocześnie utrzymując elektronikę w środowisku radioaktywnym tak prostym, jak to możliwe, jeśli masz taką opcję.
Lundin
7

Co powiesz na uruchamianie wielu wystąpień aplikacji? Jeśli awarie są spowodowane przypadkowymi zmianami bitów pamięci, istnieje prawdopodobieństwo, że niektóre instancje Twojej aplikacji przejdą i wygenerują dokładne wyniki. Prawdopodobnie dość łatwo (dla kogoś z wykształceniem statystycznym) obliczyć, ile wystąpień potrzebujesz, biorąc pod uwagę prawdopodobieństwo bit flop, aby osiągnąć tak mały ogólny błąd, jak chcesz.

ren
źródło
2
Z pewnością wbudowany system zdecydowanie wolałby, aby krytyczne dla bezpieczeństwa wyłapanie w jednym przypadku solidnej aplikacji, niż wystrzelenie kilku wystąpień, zwiększenie wymagań sprzętowych i do pewnego stopnia nadzieja na ślepe szczęście, że co najmniej jedna instancja da sobie radę? Rozumiem pomysł i jest ważny, ale bardziej
skłaniam się
7

To, o co pytasz, jest dość złożonym tematem - trudno na nie odpowiedzieć. Inne odpowiedzi są w porządku, ale obejmowały tylko niewielką część wszystkich rzeczy, które musisz zrobić.

Jak widać w komentarzach , nie można naprawić problemów sprzętowych w 100%, jednak z dużym prawdopodobieństwem można je zmniejszyć lub złapać za pomocą różnych technik.

Na twoim miejscu stworzyłbym oprogramowanie o najwyższym poziomie nienaruszalności bezpieczeństwa (SIL-4). Pobierz dokument IEC 61513 (dla przemysłu jądrowego) i postępuj zgodnie z nim.

BЈовић
źródło
11
A raczej przeczytaj wymagania techniczne i zastosuj te, które mają sens. Duża część standardów SIL to bzdury, jeśli będziesz ich przestrzegać dogmatycznie, otrzymujesz niebezpieczne i niebezpieczne produkty. Dzisiejszy certyfikat SIL polega głównie na tworzeniu dużej ilości dokumentacji, a następnie przekupieniu domu testowego. Poziom SIL nic nie mówi o rzeczywistym bezpieczeństwie systemu. Zamiast tego warto skupić się na faktycznych technicznych środkach bezpieczeństwa. W dokumentach SIL jest kilka bardzo dobrych, a niektóre kompletne bzdury.
Lundin
7

Ktoś wspomniał o stosowaniu wolniejszych czipów, aby jony nie były tak łatwo odwracane. W podobny sposób być może użyj specjalnego procesora / pamięci RAM, który faktycznie używa wielu bitów do przechowywania jednego bitu. W ten sposób zapewnia się sprzętową odporność na uszkodzenia, ponieważ bardzo mało prawdopodobne jest, aby wszystkie bity zostały odwrócone. Tak więc 1 = 1111, ale musiałby zostać trafiony 4 razy, aby faktycznie przerzucić. (4 może być złą liczbą, ponieważ jeśli 2 bity zostaną odwrócone, to już jest niejednoznaczna). Więc jeśli wybierzesz 8, otrzymasz 8 razy mniej pamięci RAM i nieco krótszy czas dostępu, ale znacznie bardziej niezawodną reprezentację danych. Prawdopodobnie można to zrobić zarówno na poziomie oprogramowania za pomocą wyspecjalizowanego kompilatora (alokacja x więcej miejsca na wszystko), jak i implementacji językowej (napisz opakowania dla struktur danych, które alokują to w ten sposób).

Alex C.
źródło
7

Być może pomogłoby wiedzieć, czy oznacza to, że sprzęt jest „zaprojektowany dla tego środowiska”. Jak to poprawia i / lub wskazuje na obecność błędów SEU?

W jednym projekcie związanym z eksploracją kosmosu mieliśmy niestandardową jednostkę MCU, która wywoływałaby wyjątek / przerwanie w przypadku błędów SEU, ale z pewnym opóźnieniem, tj. Niektóre cykle mogą przejść / instrukcje zostaną wykonane po jednej insn, która spowodowała wyjątek SEU.

Szczególnie narażona była pamięć podręczna danych, więc program obsługi unieważniałby niewłaściwą linię pamięci podręcznej i ponownie uruchamiał program. Tyle, że z powodu nieprecyzyjnego charakteru wyjątku sekwencja insn kierowanych przez insn zgłaszających wyjątek może nie zostać ponownie uruchomiona.

Zidentyfikowaliśmy niebezpieczne (nie do ponownego uruchomienia) sekwencje (jak lw $3, 0x0($2), po których następuje insn, który modyfikuje $2i nie jest zależny od danych $3), i dokonałem modyfikacji w GCC, więc takie sekwencje nie występują (np. W ostateczności, oddzielając dwie insynnop ).

Tylko coś do rozważenia ...

chłód
źródło
7

Jeśli twój sprzęt ulegnie awarii, możesz użyć mechanicznej pamięci masowej, aby go odzyskać. Jeśli baza kodu jest niewielka i ma trochę przestrzeni fizycznej, możesz użyć mechanicznego magazynu danych.

Wpisz opis zdjęcia tutaj

Powstanie powierzchnia materiału, na którą nie będzie miało wpływu promieniowanie. Będzie tam wiele biegów. Czytnik mechaniczny będzie działał na wszystkich biegach i będzie mógł się poruszać w górę iw dół. W dół oznacza, że ​​wynosi 0, a w górę oznacza, że ​​wynosi 1. Z 0 i 1 możesz wygenerować bazę kodu.

Hitul
źródło
2
Być może nośnik optyczny, taki jak CD-ROM, spełniałby tę definicję. Miałby dodatkowy bonus dużej pojemności.
Wossname
2
Tak, będzie podobnie, ale CD ROM użyje mniejszej ilości, ale będzie to w pełni mechaniczny system.
Hitul
7
Zastanawiam się, czy istnieje powód, dla którego nie używają czytników kart dziurkowanych w kosmosie.
Soren
3
@Soren Szybkość i przestrzeń fizyczna mogą być powodem.
Hitul
5

Użyj cyklicznego harmonogramu . Daje to możliwość dodawania regularnych czasów konserwacji w celu sprawdzenia poprawności krytycznych danych. Najczęściej spotykanym problemem jest uszkodzenie stosu. Jeśli twoje oprogramowanie jest cykliczne, możesz ponownie zainicjować stos między cyklami. Nie używaj ponownie stosów do połączeń przerywających, ustaw osobny stos każdego ważnego połączenia przerywającego.

Podobnie do koncepcji Watchdog są liczniki czasu. Uruchom sprzętowy timer przed wywołaniem funkcji. Jeśli funkcja nie powróci przed upływem terminu, przeładuj stos i spróbuj ponownie. Jeśli nadal nie powiedzie się po 3/5 próbach, musisz załadować ponownie z ROM.

Podziel swoje oprogramowanie na części i izoluj te części, aby użyć osobnych obszarów pamięci i czasów wykonania (szczególnie w środowisku kontrolnym). Przykład: akwizycja sygnału, przejęcie danych, główny algorytm i implementacja / transmisja wyników. Oznacza to, że awaria jednej części nie spowoduje awarii w pozostałej części programu. Tak więc, gdy naprawiamy akwizycję sygnału, reszta zadań jest kontynuowana na nieaktualnych danych.

Wszystko potrzebuje CRC. Jeśli wykonujesz z pamięci RAM, nawet twój .text potrzebuje CRC. Sprawdzaj CRC regularnie, jeśli używasz cyklicznego harmonogramu. Niektóre kompilatory (nie GCC) mogą generować CRC dla każdej sekcji, a niektóre procesory mają dedykowany sprzęt do wykonywania obliczeń CRC, ale myślę, że wypadałoby to poza zakresem twojego pytania. Sprawdzanie CRC monituje również kontroler ECC w pamięci o naprawienie błędów bitów, zanim stanie się to problemem.

Gerhard
źródło
4

Po pierwsze, zaprojektuj swoją aplikację pod kątem awarii . Upewnij się, że w ramach normalnej operacji przepływu oczekuje się zresetowania (w zależności od aplikacji i rodzaju awarii miękkiej lub twardej). Trudno to osiągnąć perfekcyjnie: operacje krytyczne wymagające pewnego stopnia transakcyjności mogą wymagać sprawdzenia i dostosowania na poziomie zespołu, aby przerwa w kluczowym punkcie nie mogła spowodować niespójnych poleceń zewnętrznych. Awaria szybko, jak tylko nie do odzyskania wykryte zostanie uszkodzenie pamięci lub odchylenie przepływu sterowania. Rejestruj awarie, jeśli to możliwe.

Po drugie, jeśli to możliwe, napraw korupcję i kontynuuj . Oznacza to częste sprawdzanie i ustawianie stałych tabel (i kodu programu, jeśli możesz); być może przed każdą większą operacją lub przerwą czasową i przechowywaniem zmiennych w strukturach, które autokorektują się (ponownie przed każdą większą operacją lub przerwą czasową, podejmują większość głosów z 3 i poprawiają, jeśli jest to pojedyncze odchylenie). Rejestruj poprawki, jeśli to możliwe.

Po trzecie, niepowodzenie testu . Skonfiguruj powtarzalne środowisko testowe, które losowo przerzuca bity w pamięci psuedo. Pozwoli to na odtworzenie sytuacji korupcji i pomoże zaprojektować aplikację wokół nich.

MrBigglesworth
źródło
3

Biorąc pod uwagę komentarze superkata, tendencje współczesnych kompilatorów i inne rzeczy, kusiłbym się, aby wrócić do starożytnych czasów i napisać cały kod w asemblerze i przydziałach pamięci statycznej wszędzie. Wydaje mi się, że dla tego rodzaju niezawodności montaż nie wiąże się już z dużą różnicą procentową kosztów.

Jozuego
źródło
Jestem wielkim fanem języka asemblera (jak widać z moich odpowiedzi na inne pytania), ale nie sądzę, że to dobra odpowiedź. Jest całkiem możliwe, aby wiedzieć, czego oczekiwać od kompilatora dla większości kodu C (pod względem wartości żyjących w rejestrach vs. pamięć), i zawsze możesz sprawdzić, czy jest to oczekiwane. Ręczne pisanie dużego projektu w asm to tylko mnóstwo dodatkowej pracy, nawet jeśli masz programistów, którzy bardzo wygodnie piszą w ARM asm. Może jeśli chcesz robić rzeczy takie jak obliczenie tego samego wyniku 3 razy, pisanie niektórych funkcji w asm ma sens. (kompilatory go opublikują)
Peter Cordes
Wyższe ryzyko, które w przeciwnym razie trzeba zrównoważyć, to aktualizacja kompilatora może spowodować nieoczekiwane zmiany.
Joshua
1

Oto ogromna liczba odpowiedzi, ale postaram się podsumować moje pomysły na ten temat.

Coś się zawiesza lub nie działa poprawnie może być wynikiem twoich własnych błędów - wtedy powinno być łatwo to naprawić po zlokalizowaniu problemu. Ale są też możliwe awarie sprzętu - a to jest trudne, jeśli nie niemożliwe, do naprawienia w ogóle.

Poleciłbym najpierw spróbować złapać problematyczną sytuację, logując się (stos, rejestry, wywołania funkcji) - albo logując je gdzieś do pliku, albo przesyłając jakoś bezpośrednio („o nie - mam awarię”).

Odzyskiwanie po takiej sytuacji błędu to albo restart (jeśli oprogramowanie nadal żyje i kopanie), albo reset sprzętowy (np. Hw watchdogs). Łatwiej zacząć od pierwszego.

Jeśli problem jest związany ze sprzętem - rejestrowanie powinno pomóc Ci zidentyfikować, w którym wystąpił problem z wywołaniem funkcji i może dać ci wewnętrzną wiedzę o tym, co nie działa i gdzie.

Także jeśli kod jest względnie złożony - sensowne jest „dzielenie go i podbijanie” - co oznacza, że ​​usuwasz / wyłączasz niektóre wywołania funkcji tam, gdzie podejrzewasz problem - zazwyczaj wyłączasz połowę kodu i włączasz drugą połowę - możesz dostać „działa” / decyzja „nie działa”, po której można skupić się na innej połowie kodu. (Gdzie jest problem)

Jeśli problem pojawi się po pewnym czasie - wówczas można podejrzewać przepełnienie stosu - lepiej monitorować rejestry punktów stosu - jeśli stale rosną.

A jeśli uda ci się w pełni zminimalizować kod do czasu, aż aplikacja „hello world” - i nadal zawiedzie losowo - wtedy spodziewane są problemy ze sprzętem - i musi być „aktualizacja sprzętu” - co oznacza wynalezienie takiego procesora / ram / ... kombinacja sprzętu, która lepiej tolerowałaby promieniowanie.

Najważniejszą rzeczą jest prawdopodobnie sposób odzyskania logów, jeśli maszyna całkowicie zatrzymana / zresetowana / nie działa - prawdopodobnie pierwsza rzecz, którą powinien zrobić bootstap - to powrót do domu, jeśli wystąpi problematyczna sytuacja.

Jeśli w twoim środowisku jest również możliwe przesyłanie sygnału i odbieranie odpowiedzi - możesz spróbować zbudować jakieś zdalne środowisko debugowania online, ale wtedy musisz mieć przynajmniej działające media komunikacyjne i jakiś procesor / trochę pamięci RAM w stanie roboczym. A przez zdalne debugowanie mam na myśli albo podejście GDB / gdb albo własną implementację tego, co musisz odzyskać z aplikacji (np. Pobieranie plików dziennika, pobieranie stosu połączeń, pobieranie ram, restart)

TarmoPikaro
źródło
Przepraszamy, ale pytanie dotyczy środowiska radioaktywnego, w którym mogą wystąpić awarie sprzętu. Twoja odpowiedź dotyczy ogólnej optymalizacji oprogramowania i wyszukiwania błędów. Ale w tej sytuacji błędy nie są powodowane przez błędy
jeb
Tak, możesz winić również grawitację ziemi, optymalizacje kompilatora, bibliotekę zewnętrzną, środowisko radioaktywne i tak dalej. Ale czy jesteś pewien, że to nie twoje własne błędy? :-) O ile nie udowodniono - nie wierzę w to. Pewnego razu uruchomiłem aktualizację oprogramowania i testowanie sytuacji po wyłączeniu - moje oprogramowanie przetrwało wszystkie sytuacje po wyłączeniu, po tym, jak naprawiłem wszystkie własne błędy. (Ponad 4000 wyłączeń w nocy) Ale trudno uwierzyć, że w niektórych przypadkach wystąpił błąd. Zwłaszcza gdy mówimy o uszkodzeniu pamięci.
TarmoPikaro,
0

Naprawdę przeczytałem wiele świetnych odpowiedzi!

Oto moje 2 centy: zbuduj model statystyczny nieprawidłowości pamięci / rejestru, pisząc oprogramowanie do sprawdzania pamięci lub częstych porównań rejestrów. Ponadto utwórz emulator w stylu maszyny wirtualnej, w której możesz eksperymentować z problemem. Sądzę, że jeśli zmienisz rozmiar złącza, częstotliwość zegara, sprzedawcę, obudowę itp. Zaobserwujesz inne zachowanie.

Nawet nasza pamięć komputera stacjonarnego ma pewien stopień awarii, co jednak nie wpływa na codzienną pracę.


źródło