UDOSTĘPNIJ

Systemy wieloprocesorowe w oprogramowaniu krytycznym

Autor: Maciej Gajdzica (Senior Software Developer)

Systemy safety-critical w ogromnej większości wykorzystują wiele procesorów. Zwykle systemy wieloprocesorowe kojarzą nam się ze zwiększeniem wydajności. 

safety-critical systems

Jednak w tym wypadku powody najczęściej są inne – najważniejsze z nich to:

  • zwiększenie bezpieczeństwa przez zastosowanie redundancji
  • uproszczenie developmentu poprzez wydzielenie skomplikowanych ale mniej krytycznych elementów do oddzielnych procesorów.

Zacznijmy od względów bezpieczeństwa. System musi zachować bezpieczeństwo również w przypadku uszkodzenia albo chwilowego błędu procesora. W przypadku dostatecznie poważnych awarii procesor może nie być w stanie samodzielnie wprowadzić systemu w stan bezpieczny. Dlatego niezbędna jest redundancja procesorów dająca drugiej jednostce obliczeniowej możliwość wykrycia problemu i podjęcia odpowiedniej akcji.

Najprostszym systemem redundantnym jest układ z procesorem nadzorującym. W takiej konfiguracji procesor główny implementuje wszystkie najważniejsze funkcje systemu. Rolą procesora nadzorującego jest jedynie monitorowanie pracy i interwencja w przypadku wykrycia jakiś poważnych anomalii.

Bardziej zaawansowanym rozwiązaniem jest zastosowanie dwóch niezależnych kanałów przetwarzania. Kanały te mają oddzielne wejścia i wyjścia, ale każde z nich może w razie potrzeby przełączyć system w stan bezpieczny. Kanały wymieniają się między sobą informacjami i dzięki temu potrafią łatwo zauważyć kiedy wartości na poszczególnych kanałach zbytnio się różnią. Pojedynczy kanał zwykle nie jest w stanie ocenić, czy różnica wynika z jego błędu, czy problem leży po drugiej stronie. Jednak nie ma to znaczenia – wykryto błąd, więc należy przejść w stan bezpieczny. System z dwoma niezależnymi kanałami może wykrywać o wiele bardziej złożone problemy niż system z procesorem nadzorującym.

Jeszcze bardziej zaawansowane rozwiązanie to system głosujący. W tym wypadku mamy do czynienia z jeszcze większą ilością niezależnych kanałów na przykład trzema, czy pięcioma (najlepiej jeśli liczba kanałów jest nieparzysta). System głosujący zbiera informacje ze wszystkich kanałów i na podstawie wybranej strategii określa sygnał wyjściowy. Oczywiście projektując musimy pamiętać również o zapewnieniu redundancji systemu głosującego, aby nie stał się single point of failure, czyli słabym punktem układu.

Systemy głosujące i z dwoma kanałami kontrolującymi się nawzajem mogą być łączone w rozwiązania hybrydowe. Możemy na przykład każdy kanał systemu głosującego złożyć z dwóch niezależnych kanałów. Możliwe też jest połączenie w drugą stronę – dwa niezależne kanały, gdzie każdy jest zrealizowany jako system głosujący.

Przy okazji implementacji systemów z wieloma kanałami realizującymi to samo zadanie ciekawym zagadnieniem jest diverse programming. To podejście, gdzie każdy z niezależnych kanałów jest realizowany przez oddzielny zespół programistów. Celem tego zabiegu jest zmniejszenie prawdopodobieństwa wystąpienia tego samego błędu w sofcie we wszystkich kanałach. Zespoły działają na podstawie tej samej dokumentacji, ale nie dzielą się kodem ani nawet pomysłami na implementację. Aby zwiększyć dywersyfikacje kanały mogą być realizowane na innych typach procesorów, w innych językach programowania albo korzystając z innych technik i metodologii.

Drugim powodem stosowania systemów wieloprocesorowych jest chęć wydzielenia części mniej krytycznej. Dzięki zastosowaniu w niej niższego poziomu bezpieczeństwa, a przez to mniej rygorystycznych norm – development może trwać dużo krócej. Dodatkowo możemy na przykład wykorzystać pewne gotowe rozwiązania, które nie spełniają standardy wyższych poziomów bezpieczeństwa.

Takie podejście jest szczególnie kuszące jeżeli chcemy wyposażyć nasz system w moduły takie jak stos TCP/IP, czy obsługa wyświetlacza graficznego. Takie moduły zwykle potrzebują dużo pamięci, często z dynamiczną alokacją i mogą zajmować dużo czasu procesora. Poza tym istnieje wtedy większa szansa błędu związanego z wyciekiem pamięci, przekroczeniem stosu, deadlockiem. Wydzielenie tych elementów do oddzielnych procesorów rozwiązuje nam wiele problemów.

Z punktu widzenia procesora realizującego krytyczne dla bezpieczeństwa funkcje, dodatkowe procesory mogą być przezroczyste. Procesor w torze sieciowym możemy potraktować jako element infrastruktury sieciowej. Jest to element tak zwanego czarnego kanału (black channel). Podobnie procesor obsługujący wyświetlanie danych na ekranie reaguje na pewne zdefiniowane komendy – można go więc potraktować jako część interfejsu HMI.

Wydzielając zadania do różnych procesorów możemy również uniknąć skomplikowanej integracji, kiedy nad tym samym kodem pracuje wiele osób. Integracje kodu z oddzielnych procesorów działających zgodnie ze ściśle zdefiniowanym interfejsem są dużo prostsze.

 

Dowiedz się więcej o systemach safety-critical!

UDOSTĘPNIJ

Kilka wskazówek dot. rozwoju systemów od których zależy ludzkie życie

Autor: Maciej Gajdzica (Senior Software Developer)

V-model, normy branżowe, dyscyplina, skupienie… i co jeszcze?
Oto garść porad dot. rozwoju oprogramowania do systemów krytycznych.

AUTOMOTIVE-EMBEDDED

Rozwój oprogramowania krytycznego w oparciu o V-model:

v-model

V-model określa trzy części projektu:

  • Design
  • Implementacja
  • Weryfikacja

 

Konkretne normy takie jak np. EN50128 dla kolejnictwa określają zalecane praktyki dla każdej z tych części przekładające się na zwiększone bezpieczeństwo docelowego systemu. W EN50128 praktyki te zostały zebrane w formie tabel z rekomendacjami dla poszczególnych poziomów SIL (Safety Integrity Level). Dodatkowo każda z technik została opisana w normie.

 

Projektowanie powinno odbywać się w sposób ustrukturyzowany (Structured methodology). Powinniśmy określić granice systemu, zidentyfikować wymagania i stopniowo uszczegóławiać projekt rozbijając duże problemy na mniejsze. Pomóc nam w tym mogą checklisty, diagramy, maszyny stanów i narzędzia komputerowe. W trakcie projektowania możemy stosować metody nieformalne pomagające nam w zrozumieniu zagadnienia. Część z powstałych w ten sposób schematów trafi później do oficjalnej dokumentacji. Decyzje projektowe powinny być konsultowane w większym gronie podczas Design Review.

 

Norma wyszczególnia również bardziej techniczne aspekty, które powinny być uwzględnione m.in.:

  • Graceful Degradation – czyli ograniczanie błędów w jednej części systemu tak, aby minimalizować ich wpływ na pozostałe
  • Diverse Programming – powielanie implementacji tych samych funkcjonalności np. na innym procesorze aby uodpornić się na niektóre klasy błędów.

 

Są również praktyki wyraźnie odradzane przez normę takie jak dynamiczna zmiana konfiguracji podczas działania programu. Zamiast tego konfiguracja powinna być wykonywana podczas inicjalizacji a potem system ma działać według niej. Tak samo nie zaleca się przywracania systemu po błędzie do wcześniejszej działającej konfiguracji. Jeśli wystąpił poważny błąd, powinniśmy ograniczyć jego skutki poprzez wejście do procedury safe-state i czekać na interwencję operatora.

 

Kolejna grupa porad z normy EN50128 dotyczy implementacji. Przede wszystkim powinniśmy używać języków kompilowanych i silnie typowanych, aby jak najwięcej niebezpiecznych konstrukcji po prostu nie mogło się skompilować. Niebezpieczne konstrukcje dające się skompilować powinny być opisane w dokumencie Coding Standard i wyłapywane za pomocą analizy statycznej. Jest to szczególnie ważne w przypadku C i C++ dopuszczających wiele podejrzanych konstrukcji. Szeroko przyjętym standardem jest tutaj MISRA C.

 

Inne rekomendacje mówią, żeby nie używać zmiennych globalnych, pisać funkcje o ograniczonej długości, złożoności i ilości parametrów wejściowych. Moduły powinny mieć ograniczony rozmiar i skupiać się na pojedynczym zadaniu. System powinien być podzielony na warstwy abstrakcji i mieć ograniczone zależności pomiędzy modułami i warstwami. To wszystko są dobrze znane zasady tworzenia wysokiej jakości kodu wykorzystywane również poza systemami safety-critical. Istnieją jednak także bardziej restrykcyjne rekomendacje, których nie znajdziemy w konwencjonalnych systemach takie jak:

 

  • Nie używanie dynamicznej alokacji – systemy safety-critical mogą działać nawet 10 i więcej lat bez przerwy i fragmentacja pamięci może uniemożliwiać działanie systemu.
  • Nie używanie rekurencji – skutkiem może być przepełnienie stosu.
  • Ograniczone używanie wskaźników – mogą one powodować nadpisanie nie swoich danych, a w przypadku wskaźników na funkcje – wykonanie niewłaściwego kodu.

 

Trzecia grupa porad odnosi się do weryfikacji i testowania. Powinniśmy robić testy na różnych poziomach – jednostkowe, integracyjne, systemowe. Do tego testy niefunkcjonalne np. wydajnościowe. Testy powinny być zarówno white-boxowe jak i black-boxowe. Norma zaleca monitorowanie code coverage, najlepiej Modified Condition/Decision Coverage (MCDC). Do tego powinniśmy posiłkować się metrykami, analizą statyczną i dynamiczną. Zalecane jest również robienie Code Review.

 

Jak widać, w normach zebrano wiele dobrych praktyk znanych również z innych dziedzin programowania. Nic dziwnego – zostały one dobrze opisane i przeanalizowane oraz udowodniły swoją skuteczność. Jednocześnie systemy safety-critical muszą być wykonywane pod większym rygorem przez co istnieje też trochę technik specyficznych tylko dla systemów tego typu.

 

Dowiedz się więcej o tym jak projektujemy systemy safety-critical – czytaj!