hallo, eine kurze Frage zur Fehlerwahrscheinlichkeit beim Einsatz von Zeigern auf ein Array. Gibt es für einen Controller nachweisbar einen Unterschied in der Fehlerwahrscheinlichkeit, wenn man einen Zeiger "wild" in einem Feld herumsprignen lässt im Vergleich zu einem stetigen Inkrementieren? Man könnte ja beispielsweise alle Elemente eines Arrays aufaddieren wollen und den Zähler im ersten Fall der Reihe nach von [0] bis [n-1] laufen lassen, im zweiten Fall in zufälliger Reihenfolge, wobei natürlich jedes Feld nur einmal ausgelesen werden soll. Wäre diese zweite Variante physikalisch fehlerträchtiger oder nicht?
Wenn du sicherstellst, dass jedes Elemant nur einmal gelesen wird, dann sollte es keinen Unterschied machen, ob durchgehend aufsteigend oder wild durch einander gelesen wird. Mir stellen sich nur die Fragen: 1) Echte Zufallszugriffe beim Mikrocontroller? Wie willst du das umsetzen? 2) Warum sollte sich jemand die Arbeit machen mit Zufallszugriffen + check, auf Einzelzugriff pro Element, wenn er doch einfach alle Elemente der Reihe durchgehen kann.
@Error (Gast) >eine kurze Frage zur Fehlerwahrscheinlichkeit beim Einsatz von Zeigern >auf ein Array. Der Zugriff auf ein Array über einen Index ist sicherer, weil man den Index vor jedem Zugriff auf den gültigen Bereich prüfen kann. Beim Zugiff über einen einfachen Pointer mit Post/pre Dekrement etc. geht das nicht! >Gibt es für einen Controller nachweisbar einen Unterschied in der >Fehlerwahrscheinlichkeit, wenn man einen Zeiger "wild" in einem Feld >herumsprignen lässt im Vergleich zu einem stetigen Inkrementieren? Hmm, der formale Nachweis könnte einfacher und sicherer sein, weil deterministisch und einfacher strukturiert. >laufen lassen, im zweiten Fall in zufälliger Reihenfolge, wobei >natürlich jedes Feld nur einmal ausgelesen werden soll. Dann brauchst du aber einen Algorithmus, der alle Indizes zufällig, aber dennoch jeweils einmalig erzeugt. Machbar, aber nicht einfacher. >Wäre diese zweite Variante physikalisch fehlerträchtiger oder nicht? Theoretisch vielleicht nicht, praktisch schon.
Error schrieb: > Wäre diese zweite Variante physikalisch fehlerträchtiger oder nicht? ? Wenn du da unterschiedliche Ergebnisse kriegst, dann ist entweder tatsächlich in der CPU irgendwas grauslich kaputt (das würde sich aber auch an anderen Stellen bemerkbar machen) oder aber (die viel wahrscheinlichere Variante): du hast ganz banal einen Fehler im Programm. Für die CPU ist ein Adressierung über Pointer nichts irgendwie spezielles. 2 Zahlen werden addiert. Erst danach trennen sich die Wege und in dem Fall wird das Ergebnis auf den Adressbus gelegt und vom Speicher der Wert von dieser Stelle abgerufen. Letzteres passiert aber auch, wenn du sequentiell durch das Array durchgehst. Der einzige Unterschied: die Addition, die in dem einen Fall aus irgendeinem berechnetem Wert besteht und im anderen Fall aus einer schnöden Addition mit einer Konstanten.
Hallo, Falk Brunner schrieb: > Der Zugriff auf ein Array über einen Index ist sicherer, weil man den > Index vor jedem Zugriff auf den gültigen Bereich prüfen kann. > > Beim Zugiff über einen einfachen Pointer mit Post/pre Dekrement etc. > geht das nicht! Da möchte ich doch wiedersprechen. Auch bei einem Post/Pre-Dekrement kann geprüft werden, ob der Pointer noch in den gültigen Bereich zeigt - zumindest insoweit, dass keine anderen Speicherbereiche betroffen sind, häufig auch exakt:
1 | struct ArrayTest { |
2 | int ArrayBeginn ; |
3 | int Array[100] ; |
4 | int ArrayEnde ; |
5 | } ; |
6 | |
7 | int SumArray (struct ArrayTest *Sum) |
8 | {
|
9 | int *Pointer ; |
10 | int i ; |
11 | int Summe ; |
12 | |
13 | Pointer = Sum->Array ; |
14 | Summe = 0 ; |
15 | |
16 | for (i=0;i<100;i++) { |
17 | if ((Pointer<=&(Sum->ArrayBeginn))||(Pointer>=&(Sum->ArrayEnde))) { |
18 | printf ("Fehler\n") ; |
19 | exit(0) ; |
20 | } ; |
21 | Summe += *(Pointer++) ; |
22 | } ; |
23 | return (Summe) ; |
24 | } ; |
Alternativ braucht man auch keine Zählvariable, sondern bricht ab, wenn man das Ende erreicht hat:
1 | int SumArray (struct ArrayTest *Sum) |
2 | {
|
3 | int *Pointer ; |
4 | int Summe ; |
5 | |
6 | Summe = 0 ; |
7 | |
8 | for (Pointer=Sum->Array;Pointer<&(Sum->ArrayEnde);Pointer++) { |
9 | if ((Pointer<=&(Sum->ArrayBeginn))||(Pointer>=&(Sum->ArrayEnde))) { |
10 | printf ("Fehler\n") ; |
11 | exit(0) ; |
12 | } ; |
13 | Summe += *Pointer ; |
14 | } ; |
15 | return (Summe) ; |
16 | } ; |
oder ohne Struktur (wenn bekannt ist, dass das Array 100 Elemente enthält:
1 | int SumArray (int *Array) |
2 | {
|
3 | int *Pointer ; |
4 | int Summe ; |
5 | |
6 | Summe = 0 ; |
7 | |
8 | for (Pointer=Array;Pointer<&(Array[100]);Pointer++) { |
9 | if ((Pointer<Array)||(Pointer>&(Array[100]))) { |
10 | printf ("Fehler\n") ; |
11 | exit(0) ; |
12 | } ; |
13 | Summe += *Pointer ; |
14 | } ; |
15 | return (Summe) ; |
16 | } ; |
Dabei sind allerdings zur vollständigen Sicherheit noch weitere Kleinigkeiten zu beachten, wie z.B. dass die Überprüfung des Pointers sowie das Auslesen des Wertes zusammen als eine Atomic Operation durchgeführt werden muss. Ich würde sogar so weit gehen, dass das sequenzielle Auslesen des Speichers mit Pre- oder Postinkrement sogar weniger fehleranfällig ist, da im Fall der Indizierung zwei Variablen fehlerhaft sein könnten (der Pointer und der Index) und eine (oder mehrere) zusätzliche Operation(en) (Addition des Index auf den Pointer) notwendig sind. Insgesamt unterscheiden sich die beiden Varianten allerdings nur marginal, wenn sie richtig implementiert werden. Handelt es sich um sicherheitsrelevante Anwendungen, kann man über einen Speicherschutz nachdenken (entweder in Hardware - ECC - oder in Software - Redundante, möglichst komplementäre Ablage, oder sogar durch redundante, sich im Wertebereich unterscheidende Berechnung). Dann ist aber auch ein Befehlssatzcheck sowie mindestens ein externer Watchdog angeraten. Schöne Grüße, Martin
Hallo, kleine Korrektur (typischer C-Fehler ;-)): in der letzten Funktion muss es natürlich
1 | if ((Pointer<Array)||(Pointer>&(Array[99]))) { |
heißen. Schöne Grüße, Martin
...aber ist nicht ein Pointer nichts anderes als ein Datum auf dem Adressbus? Wenn ich nun ein Array derart auslese, dass ich den Adressbus in Form eines Gray-Codes belege, also so, dass sich nach Möglichkeit von Adresse zu Adresse immer nur ein Bit ändert, dann stelle ich mir schon vor, dass insgesamt weniger Bitfehler auftreten würden... ...Oder verliert der Adressbus nach jedem Zugriff seine Daten absolut vollständig?
Ich bevorzuge auch die Index-Variante. Dann kann man die Zugriffe bequem absichern:
1 | if( idx >= sizeof(reply_time) / sizeof(uint16_t) ) // array guard |
2 | return ERROR1; |
3 | reply_time[idx] = time; |
Error schrieb: > ...aber ist nicht ein Pointer nichts anderes als ein Datum auf dem > Adressbus? Ein Arrayzugriff über Index ist aber auch nichts anderes als ein Datum auf dem Adressbus. Lediglich die Schreibweise ist eine andere. > Wenn ich nun ein Array derart auslese, dass ich den Adressbus in Form > eines Gray-Codes belege, also so, dass sich nach Möglichkeit von Adresse > zu Adresse immer nur ein Bit ändert, dann stelle ich mir schon vor, dass > insgesamt weniger Bitfehler auftreten würden... Da dürfen sowieso keine Bitfehler auftreten! Auf dieser Ebene muss alles 100% perfekt funktionieren, ansonsten ist das alles unbrauchbar. Wenn von der CPU der Befehl kommt, das Byte von der Adresse 100 zu lesen, dann muss da auch das Byte von der Adresse 100 gelesen werden. Und wenn der nächste Befehl von 105 lesen will, dann muss auch von 105 gelesen werden. Was tust du denn wenn 2 Variablen einmal zufällig hintereinander im Speicher liegen und bei einer Programmänderung kommen sie getrennt voneinander zu liegen? Das eine mal funktioniert der Zugriff und das andere mal nicht? Oder wie? Wenn die CPU das Byte von einer bestimmten Adresse anfordert, dann muss das klappen (sofern es den Speicher überhaupt an dieser Adresse gibt). Und zwar 100% und zu jeder Zeit. Alles andere ist ein Zufallszahlengenerator aber kein Computer.
Hallo, Error schrieb: > ...Oder verliert der Adressbus nach jedem Zugriff seine Daten absolut > vollständig? So ist es. Gray-Code würde sich nur dann anbieten, wenn er extern überwacht wird. Dann kann man aber auch einen einfachen Zähler einsetzen. Insgesamt eignet sich der Gray-Code nicht wirklich zur Fehlererkennung, da die Hamming-Distanz weiterhin nur 1 beträgt. Allerdings ist er gut geeignet (und dafür auch entwickelt worden), um bei einer parallelen Datenübertragung (oder Sensorauswertung) ungewünschte Zwischenzustände zu vermeiden. Ein Einsastzgebiet sind z.B. digitale Weg- oder Winkelencoder. Schöne Grüße, Martin
Hallo, Karl Heinz Buchegger schrieb: > Und zwar 100% und zu jeder Zeit. Alles andere ist ein > Zufallszahlengenerator aber kein Computer. Naja, bei komplexeren Microcontrollern kommt man schon auf Fehlerraten im Bereich von 1000-3000 FIT, also 1 bis 3 Fehler alle 10^6 Betriebsstunden. Wenn man entsprechend viele davon einsetzt (z.B. im Automobilbereich), dann muss man sich hier schon Gedanken machen, wie mit Fehlern umzugehen ist - und dabei gibt es dann kein "darf nicht" oder "kann nicht". Oder um es 'mal so auszudrücken: Wenn in jedem Fahrzeug im Schnitt 3 sicherheitsrelevante Steuergeräte verbaut sind (Motor, ABS, Airbag), kommt es bei 43 Mio PKW in Deutschland bei einer durchschnittlichen Betriebszeit von rund 500 Stunden im Jahr zu rund 60.000 Fehlern im Jahr. Die ISO 26262 schreibt vor, dass man für wirklich sicherheitskritische Fehler, die in mindestens 10% der Fahrsituationen zu einem tödlichen Unfall führen können und in denen der Fahrer nicht reagieren kann (ASIL D) einen Diagnostischen Deckungsgrad von 99% erreichen muss. Damit wären aber 600 Fehler pr Jahr potentiell tödlich, wovon dann statistisch mindestens 60 tatsächlich mit einem tödlichen Unfall enden müssten. Gut, dass das die Steuergeräte nicht wissen, und die tatsächliche Rate erheblich darunter liegt... Besonders deswegen, da in modernen Fahrzeugen in der Regel von den bis zu 70 eingesetzten Steuergeräten 5 bis 10 als sicherheitsrelevant einzustufen sind. Insofern macht es schon Sinn sich zu überlegen, wie man die Berechnungen in einem µC auch auf niedrigster Ebene überprüfen kann - die angedachte pseudozufälligen Zugriffe sind jedoch dafür leider nicht geeignet. Schöne Grüße, Martin
Hi Martin, wenn du davon ausgehen "musst", das in der CPU beim Zugriff auf die Register oder den Speicher Fehler auftreten, dann spielt es keine Rolle in welcher Weise du darauf zugreifst der Fehler wird in jedem Fall auftreten die einzigste Möglichkeit solche Fehler zu erkennen und evenentuell zu korrigieren besteht darin, das ganze mehrfach redundant auszulegen das kannst du dann beliebig "Kompliziert" gestallten (mehrere Zugriffe, mehrere CPUs, mehrere Compiler usw) Gruss Uwe
Uwe B. schrieb: > das ganze mehrfach redundant auszulegen Dann wäre noch näher zu untersuchen wie sich das Gerät nach kurzem Spannungsausfall verhält. Entweder fängt es definiert bei Punkt NULL an oder irgenwo mittendrin?? Da nützen auch 100 CPUs nichts wenn die Strategie falsch war.
Na da wird es schon Techniken geben die das wieder sincron bekommen. In der Luftfahrt sind ja auch redundante Systeme üblich. Und da glaube ich nicht das sich alle Triebwerke im Flug abschalten und eine Windows Loginfenster im Amaturenfeld erscheint :)
>Dann wäre noch näher zu untersuchen wie sich das Gerät nach kurzem >Spannungsausfall verhält. Entweder fängt es definiert bei Punkt NULL an >oder irgenwo mittendrin?? Da nützen auch 100 CPUs nichts wenn die >Strategie falsch war. mir ging es nur um das Eingangsthema vom TE : "Fehlerwahrscheinlichkeit beim Einsatz von Zeigern" und wollte damit sagen das es meiner Meinung nach egal ist wie die Software den Zugriff handelt, wenn von einem Hardwarefehler in der CPU ausgegangen wird. und ich glaube auch nicht das sich bei 100 CPUs zu einem Zeitpunkt x bei allen genau der gleiche Fehler zeigt... man hat dann halt 99 CPUs die richtig funktionieren und die müssen die "defekte" dann halt erkennen und ausklammern aber wie gesagt, ist ein beliebig kompliziertes Thema
Hallo, in der Praxis wird man wie so oft ein abgestuftes Verfahren einsetzen; je nach maximaler Kritikalität mit unterschiedlicher Tiefe und Komplexität. Ein Beispiel könnte so aussehen: Programm läuft auf einem µC; dieser überprüft die ihm zugängliche Peripherie und sich selber so weit wie möglich (AD-Wandler durch Referenzspannungen, Speucher durch ECC oder redundante Ablage, Befehlssatz durch Durchführen einer Kontrollrechnung, Programmablauf). Er wiederum wird von einem externen Element überwacht (kleiner Zweitprozessor, Dual-Core-Lockstep, intelligenter Watchdog), welches bei einer Abweichung des Verhaltens (auch das Zeitverhalten) des Hauptprozessors vom Erwarteten unabhängig von diesem einen sicheren Zustand herstellen kann (je nach der Komplexität des sicheren Zustands richtet sich dann auch die Komplexität der Überwachungseinheit). Ergänzend wird die Versorgungsspannung der beiden überwacht und bei Unter- oder Überspannung der sichere Zustand ausgelöst. Dies entspricht dann im Groben dem VDA-E-Gas-Konzept; die Details auszuarbeiten ist allerdings knifflig, und dabei kann es dann durchaus auch auf die Art des Zugriffs ankommen, da dieser zumindest bei dem Befehlssatzcheck berücksichtigt sein muss (register indirect; register indirect with index und/oder Art des Pipelinings). Ganz interessant wird die Zugriffsmethode natürlich dann, wenn Caches mit im Spiel sind. Dabei dürfte die zufällige Methode dann tatsächlich eine leicht höhere Fehlerwahrscheinlichkeit haben, da es dann häufiger zu Cache-Miss und Cache-Fill-Szenarien kommt. Zum Thema 100 Prozessoren: Die in der Luft-/Raumfahrt üblichen Methodik, mehrere Systeme parallel arbeiten zu lassen, sind natürlich (unter der Voraussetzung, dass sie nicht redundant, sondern divergent ausgeführt sind) ebenfalls zielführend. Hier ergeben sich dann - neben den gewaltigen Kosten - ganz andere Problematiken bezüglich Synchronisation und der Entscheidung, wer nun recht hat... Insofern kann ich mich meinem Vorredner anschließen: "ist ein beliebig komplexes Thema" ;-) Schöne Grüße, Martin
Error schrieb: > Man könnte ja beispielsweise alle Elemente eines Arrays aufaddieren > wollen und den Zähler im ersten Fall der Reihe nach von [0] bis [n-1] > laufen lassen, im zweiten Fall in zufälliger Reihenfolge, wobei > natürlich jedes Feld nur einmal ausgelesen werden soll. > > Wäre diese zweite Variante physikalisch fehlerträchtiger oder nicht? Ja. Ich habe häufig mit parallelen Interfaces am FPGA zu tun (DDR-RAM, ADC). Die Testpattern die die meisten Fehlern provozieren sind, die aus einem Pseudo-Noise- Generator. Wenn man genauer hinschaut da sind die Arbeitsphasen am kritischsten bei dem die meisten bits wackeln. also sechsel von adresse 0x0000 auf 0xFFFF und zurück wäre auch so was kritisches. Da sackt auch die betriebsspannung ab, das kann schon in kritische bereiche (-5%) gehen. Beliebt sind auch Muster wie 01010, da kann das Übersprechen zwischen den leitungen Bits kippen lassen. MfG
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.