Ich habe vor einigen Monaten begonnen, Software für Echtzeitsysteme in C für Weltraumanwendungen und auch für Mikrocontroller mit C ++ zu entwickeln. In solchen Systemen gibt es eine Faustregel, dass man niemals Heap-Objekte erstellen sollte (also kein Malloc / New), da dies das Programm nicht deterministisch macht . Ich konnte die Richtigkeit dieser Aussage nicht überprüfen, als mir die Leute das sagten. Also, Ist das eine richtige Aussage?
Die Verwirrung für mich ist, dass Determinismus meines Wissens bedeutet, dass das zweimalige Ausführen eines Programms zu genau demselben Ausführungspfad führt. Nach meinem Verständnis ist dies ein Problem bei Multithread-Systemen, da bei mehrmaliger Ausführung desselben Programms jedes Mal unterschiedliche Threads in unterschiedlicher Reihenfolge ausgeführt werden können.
quelle
rdrand
Anweisung, die Sie über einen normalen User-Space-Prozess ausführen können. Es gibt echte Hardware-Zufälligkeit von einem mit AES konditionierten thermischen Rauschgenerator (es sei denn, die NSA hat das Design geschwächt ...). Natürlichrdtsc
ist es auch nicht deterministisch, wie David betont, insbesondere wenn man nur einen einzigen Prozess betrachtet, aber ein guter Punkt, dass die Synchronisation zwischen verschiedenen Taktdomänen einen echten Nichtdeterminismus ergibt.Antworten:
Im Kontext von Echtzeitsystemen ist Determinismus mehr als ein wiederholbarer "Ausführungspfad". Eine weitere erforderliche Eigenschaft ist, dass das Timing von Schlüsselereignissen begrenzt ist. In harten Echtzeitsystemen stellt ein Ereignis, das außerhalb des zulässigen Zeitintervalls auftritt (entweder vor dem Beginn dieses Intervalls oder nach dem Ende), einen Systemfehler dar.
In diesem Zusammenhang kann die Verwendung der dynamischen Speicherzuweisung zu Nichtdeterminismus führen, insbesondere wenn das Programm ein unterschiedliches Muster für die Zuweisung, Freigabe und Neuzuweisung aufweist. Der Zeitpunkt für die Zuweisung, Freigabe und Neuzuweisung kann im Laufe der Zeit variieren - und daher ist der Zeitpunkt für das gesamte System unvorhersehbar.
quelle
rand()
Millisekunden dauert und die Zeitgrenze größer als istRAND_MAX
, ist das System in Echtzeit.Der Kommentar ist wie angegeben falsch.
Durch die Verwendung eines Heap-Managers mit nicht deterministischem Verhalten wird ein Programm mit nicht deterministischem Verhalten erstellt. Das ist aber offensichtlich.
Etwas weniger offensichtlich ist die Existenz von Heap-Managern mit deterministischem Verhalten. Das vielleicht bekannteste Beispiel ist der Pool-Allokator. Es hat ein Array von N * M Bytes und eine
available[]
Maske von N Bits. Zum Zuweisen wird nach dem ersten verfügbaren Eintrag gesucht (Bittest, O (N), deterministische Obergrenze). Zum Aufheben der Zuordnung wird das verfügbare Bit (O (1)) gesetzt.malloc(X)
rundet X auf den nächstgrößeren Wert von M auf, um den richtigen Pool auszuwählen.Dies ist möglicherweise nicht sehr effizient, insbesondere wenn Sie zwischen N und M wählen. Und wenn Sie zu niedrig wählen, kann Ihr Programm fehlschlagen. Die Grenzwerte für N und M können jedoch niedriger sein als für ein gleichwertiges Programm ohne dynamische Speicherzuweisung.
quelle
malloc()
Blöcken fester Größe) von Natur aus nicht zeitdeterministisch ist oder zu einer unbegrenzten Fragmentierung führen kann. Wie zum Beispiel in "Timing-Predictable Memory Allocation in harten Echtzeitsystemen" [Herter 2014] gezeigt, existieren vorhersagbare und effiziente Algorithmen, von denen jedoch selten gesprochen wird. Ich habe hier eine von ca. 500 Codezeilen für ein eingebettetes Echtzeitsystem implementiert, an dem ich beteiligt war: github.com/pavel-kirienko/o1heapNichts im C11- Standard oder in n1570 sagt, dass dies
malloc
deterministisch ist (oder nicht); und auch keine andere Dokumentation wie malloc (3) unter Linux. Übrigens sind vielemalloc
Implementierungen freie Software .Aber
malloc
kann (und tut) fehlschlagen, und seine Leistung ist nicht bekannt (ein typischer Aufrufmalloc
auf meinem Desktop würde praktisch weniger als eine Mikrosekunde dauern, aber ich könnte mir seltsame Situationen vorstellen, in denen es viel länger dauern könnte, vielleicht viele Millisekunden bei einer sehr geladenen Computer; lesen Sie über das Verprügeln ). Und mein Linux-Desktop verfügt über ASLR (Address Space Layout Randomization), sodass das zweimalige Ausführen desselben Programms unterschiedlichemalloc
Adressen ergibt (im virtuellen Adressraum des Prozesses). Übrigens ist hier eine deterministische (unter bestimmten Annahmen, die Sie ausarbeiten müssen), aber praktisch nutzlosemalloc
Implementierung.Dies ist in den meisten eingebetteten Systemen praktisch falsch, da sich die physische Umgebung ändert. Beispielsweise kann die Software, die einen Raketentriebwerk antreibt, nicht erwarten, dass der Schub, der Luftwiderstand oder die Windgeschwindigkeit usw. von einem Start zum nächsten genau gleich sind.
(Ich bin also überrascht, dass Sie glauben oder wünschen, dass Echtzeitsysteme deterministisch sind; sie sind es nie! Vielleicht interessiert Sie WCET , das aufgrund von Caches immer schwieriger vorherzusagen ist. )
Übrigens implementieren einige "Echtzeit" - oder "eingebettete" Systeme ihre eigenen
malloc
(oder eine Variante davon). C ++ - Programme können ihre Allokatoren haben , die von Standardcontainern verwendet werden können . Siehe auch dies und das usw. usw. .....Auf hoher Ebene eingebetteter Software (denken Sie an ein autonomes Automobil und seine Planungssoftware ) werden zwar Heap-Zuweisungen und möglicherweise sogar Speicherbereinigungstechniken (von denen einige "in Echtzeit" ausgeführt werden) verwendet, sie werden jedoch im Allgemeinen nicht als sicherheitskritisch angesehen.
quelle
printf
mit dem%p
Ergebnis vonmalloc
) und kann zu heftigen Diskussionen führentl; dr: Es ist nicht so, dass die dynamische Speicherzuweisung von Natur aus nicht deterministisch ist (wie Sie es anhand identischer Ausführungspfade definiert haben). Es ist so, dass es Ihr Programm im Allgemeinen unvorhersehbar macht . Insbesondere können Sie nicht vorhersagen, ob der Allokator angesichts einer beliebigen Folge von Eingaben ausfallen könnte.
Sie könnten einen nicht deterministischen Allokator haben. Dies ist außerhalb Ihrer Echtzeitwelt üblich, in der Betriebssysteme beispielsweise die Randomisierung des Adresslayouts verwenden. Das würde Ihr Programm natürlich nicht deterministisch machen.
Dies ist jedoch kein interessanter Fall. Nehmen wir also einen perfekt deterministischen Allokator an: Dieselbe Reihenfolge von Zuweisungen und Freigaben führt immer zu denselben Blöcken an denselben Standorten, und diese Zuweisungen und Freigaben haben immer eine begrenzte Laufzeit.
Jetzt kann Ihr Programm deterministisch sein: Der gleiche Satz von Eingaben führt zu genau dem gleichen Ausführungspfad.
Das Problem ist, dass Sie beim Zuweisen und Freigeben von Speicher als Reaktion auf Eingaben nicht vorhersagen können, ob eine Zuweisung jemals fehlschlagen wird (und ein Fehler ist keine Option).
Erstens könnte Ihr Programm Speicher verlieren. Wenn es also auf unbestimmte Zeit ausgeführt werden muss, schlägt eine Zuordnung möglicherweise fehl.
Aber selbst wenn Sie nachweisen können, dass keine Lecks vorhanden sind, müssen Sie wissen, dass es niemals eine Eingabesequenz gibt, die mehr Speicher als verfügbar erfordern könnte.
Aber selbst wenn Sie nachweisen können, dass das Programm niemals mehr Speicher benötigt als verfügbar ist, kann der Allokator abhängig von der Reihenfolge der Zuweisungen und Freigaben den Speicher fragmentieren und somit möglicherweise keinen zusammenhängenden Block finden, um eine Zuordnung zu erfüllen, obwohl Insgesamt ist genügend freier Speicher vorhanden.
Es ist sehr schwierig zu beweisen, dass es keine Folge von Eingaben gibt, die zu einer pathologischen Fragmentierung führen.
Sie können Allokatoren entwerfen, um sicherzustellen, dass keine Fragmentierung auftritt (z. B. durch Zuweisen von Blöcken mit nur einer Größe). Dies stellt jedoch eine erhebliche Einschränkung für den Anrufer dar und erhöht möglicherweise den aufgrund der Verschwendung erforderlichen Speicherplatz. Und der Anrufer muss immer noch nachweisen, dass es keine Lecks gibt und dass unabhängig von der Reihenfolge der Eingaben eine zufriedenstellende Obergrenze für den Gesamtspeicher erforderlich ist. Diese Belastung ist so hoch, dass es tatsächlich einfacher ist, das System so zu gestalten, dass es keine dynamische Speicherzuweisung verwendet.
quelle
Bei Echtzeitsystemen muss das Programm bestimmte Rechen- und Speicherbeschränkungen unabhängig vom verwendeten Ausführungspfad strikt einhalten (der je nach Eingabe immer noch erheblich variieren kann). Was bedeutet die Verwendung der generischen dynamischen Speicherzuweisung (z. B. malloc / new) in diesem Zusammenhang? Dies bedeutet, dass der Entwickler irgendwann nicht in der Lage ist, den genauen Speicherverbrauch zu bestimmen, und es unmöglich ist zu sagen, ob das resultierende Programm die Anforderungen sowohl an den Speicher als auch an die Rechenleistung erfüllen kann.
quelle
Ja, das ist richtig. Für die Art der Anwendungen, die Sie erwähnen, muss alles, was auftreten kann, detailliert angegeben werden. Das Programm muss das Worst-Case-Szenario gemäß Spezifikation behandeln und genau so viel Speicher reservieren, nicht mehr und nicht weniger. Die Situation, in der "wir nicht wissen, wie viele Eingaben wir erhalten", existiert nicht. Das Worst-Case-Szenario wird mit festen Zahlen angegeben.
Ihr Programm muss in einem Sinne deterministisch sein, dass es alles bis zum schlimmsten Fall verarbeiten kann.
Der eigentliche Zweck des Heaps besteht darin, mehreren nicht verwandten Anwendungen die gemeinsame Nutzung des RAM-Speichers zu ermöglichen, z. B. auf einem PC, auf dem die Anzahl der ausgeführten Programme / Prozesse / Threads nicht deterministisch ist. Dieses Szenario existiert in einem Echtzeitsystem nicht.
Darüber hinaus ist der Heap seiner Natur nach nicht deterministisch, da Segmente im Laufe der Zeit hinzugefügt oder entfernt werden.
Weitere Informationen finden Sie hier: https://electronics.stackexchange.com/a/171581/6102
quelle
Selbst wenn Ihr Heap-Allokator ein wiederholbares Verhalten aufweist (dieselbe Zuordnungssequenz und freie Aufrufe ergeben dieselbe Blocksequenz, daher (hoffentlich) denselben internen Heap-Status), kann der Status des Heaps drastisch variieren, wenn die Aufrufsequenz geändert wird Dies kann möglicherweise zu einer Fragmentierung führen, die auf unvorhersehbare Weise zu Speicherzuordnungsfehlern führt.
Der Grund, warum die Heap-Zuweisung in eingebetteten Systemen völlig verboten ist, insb. Bei geschäftskritischen Systemen wie Leit- oder Lebenserhaltungssystemen für Flugzeuge oder Raumfahrzeuge gibt es keine Möglichkeit, alle möglichen Variationen in der Reihenfolge der malloc / freien Anrufe zu testen, die als Reaktion auf intrinsisch asynchrone Ereignisse auftreten können.
Die Lösung besteht darin, dass für jeden Handler ein Speicher für seinen Zweck reserviert wird und es keine Rolle mehr spielt (zumindest was die Speichernutzung betrifft), in welcher Reihenfolge diese Handler aufgerufen werden.
quelle
Das Problem bei der Verwendung von Heap in Echtzeit-Software besteht darin, dass Heap-Zuweisungen fehlschlagen können. Was machst du, wenn dir der Haufen ausgeht?
Sie sprechen von Weltraumanwendungen. Sie haben ziemlich harte No-Fail-Anforderungen. Sie dürfen keine Möglichkeit haben, Speicher zu verlieren, sodass nicht genügend Code für den abgesicherten Modus vorhanden ist. Du darfst nicht umfallen. Sie dürfen keine Ausnahmen auslösen, die keinen catch-Block haben. Sie haben wahrscheinlich kein Betriebssystem mit geschütztem Speicher, sodass eine abstürzende Anwendung theoretisch alles ausschalten kann.
Sie möchten wahrscheinlich überhaupt keinen Heap verwenden. Die Vorteile überwiegen nicht die Gesamtkosten des Programms.
Nicht deterministisch bedeutet normalerweise etwas anderes, aber in diesem Fall ist es am besten, wenn das gesamte Programmverhalten vollständig vorhersehbar sein soll.
quelle
Stellen Sie Integrity RTOS von GHS vor:
https://www.ghs.com/products/rtos/integrity.html
und LynxOS:
http://www.lynx.com/products/real-time-operating-systems/lynxos-178-rtos-for-do-178b-software-certification/
LynxOS und Integrity RTOS gehören zu der Software, die in Weltraumanwendungen, Raketen, Flugzeugen usw. verwendet wird, da viele andere nicht von Behörden (z. B. FAA) genehmigt oder zertifiziert sind.
https://www.ghs.com/news/230210r.html
Um die strengen Kriterien von Weltraumanwendungen zu erfüllen, bietet Integrity RTOS tatsächlich eine formale Überprüfung, dh eine mathematisch erprobte Logik, dass sich ihre Software gemäß den Spezifikationen verhält.
Unter diesen Kriterien, um von hier aus zu zitieren:
https://en.wikipedia.org/wiki/Integrity_(operating_system)
und hier:
Green Hills Integrity Dynamische Speicherzuordnung
ist das:
Ich bin kein Spezialist für formale Methoden, aber möglicherweise besteht eine der Anforderungen für diese Überprüfung darin, die Unsicherheiten im Timing zu beseitigen, die für die Speicherzuweisung erforderlich sind. In RTOS sind alle Ereignisse genau Millisekunden voneinander entfernt geplant. Und die dynamische Speicherzuweisung hat immer ein Problem mit dem erforderlichen Timing.
Mathematisch müssen Sie wirklich beweisen, dass alles funktioniert, und zwar unter den grundlegendsten Annahmen über das Timing und die Größe des Speichers.
Und wenn Sie an die Alternativen zum Heap-Speicher denken: statischer Speicher . Die Adresse ist fest, die zugewiesene Größe ist fest. Die Position im Speicher ist fest. Es ist also sehr einfach, über ausreichende Speicherkapazität, Zuverlässigkeit, Verfügbarkeit usw. nachzudenken.
quelle
Kurze Antwort
Es gibt einige Auswirkungen auf die Datenwerte oder deren statistische Unsicherheitsverteilungen, z. B. eines Triggerszintillatorgeräts der ersten oder zweiten Ebene, die sich aus der nicht reproduzierbaren Zeit ergeben können, auf die Sie möglicherweise warten müssen
malloc/free
.Das Schlimmste ist, dass sie weder mit der Hardware noch mit dem Zustand des Speichers (und seiner Geschichte) mit dem physikalischen Phänomen zusammenhängen.
In diesem Fall besteht Ihr Ziel darin, die ursprüngliche Abfolge von Ereignissen aus den von diesen Fehlern betroffenen Daten zu rekonstruieren. Die rekonstruierte / erratene Sequenz wird ebenfalls von Fehlern beeinflusst. Nicht immer wird diese Iteration zu einer stabilen Lösung führen. es wird nicht gesagt, dass es das richtige sein wird; Ihre Daten sind nicht mehr unabhängig ... Sie riskieren einen logischen Kurzschluss ...
Längere Antwort
Sie erklärte : „Ich nicht in der Lage war , die Richtigkeit dieser Aussage zu überprüfen , wenn Leute mir sagen , dass“ .
Ich werde versuchen, Ihnen eine rein hypothetische Situation / Fallstudie zu geben.
Stellen wir uns vor, Sie beschäftigen sich mit einem CCD oder mit einigen Szintillator-Triggern der 1. und 2. Ebene auf einem System, das Ressourcen sparen muss (Sie befinden sich im Weltraum).
Die Erfassungsrate wird so eingestellt, dass der Hintergrund auf
x%
dem liegtMAXBINCOUNT
.Es gibt einen Ausbruch, Sie haben eine Spitze in der Zählung und einen Überlauf im Behälterzähler.
Ich möchte alles: Sie wechseln zur maximalen Erfassungsrate und beenden Ihren Puffer.
Sie gehen, um mehr Speicher freizugeben / zuzuweisen, während Sie den zusätzlichen Puffer beenden.
Was wirst du tun?
Beachten Sie, dass:
Stattdessen ist das Signal jetzt um die
maxbincount
maximal von Ihrer Hardware zulässige Erfassungsrate variabel , und das Ereignis ist länger als gewöhnlich.Sie beenden den Raum und bitten um mehr ... währenddessen haben Sie das gleiche Problem wie oben.
Überlauf und systematische Peaks zählen Unterschätzung oder Löcher in der Zeitreihe?
Lassen Sie uns eine zweite Ebene verschieben (es kann sich auch um den Auslöser der ersten Ebene handeln).
Von Ihrer Hardware erhalten Sie mehr Daten, als Sie lagern oder übertragen können.
Sie müssen die Daten zeitlich oder räumlich gruppieren (2x2, 4x4, ... 16x16 ... 256x256 ... Pixelskalierung ...).
Die Unsicherheit aus dem vorherigen Problem kann die Fehlerverteilung beeinflussen .
Es gibt CCD-Einstellungen, für die Sie die Pixel des Rahmens mit einer Anzahl nahe an der haben
maxbincount
(dies hängt davon ab, "wo" Sie besser sehen möchten).Jetzt können Sie auf Ihrem CCD oder einem einzelnen großen Punkt mit der gleichen Gesamtzahl von Zählungen, aber mit einer anderen statistischen Unsicherheit (dem Teil, der durch die Wartezeit eingeführt wird) duschen ...
Wenn Sie beispielsweise ein Lorentz-Profil erwarten, können Sie dessen Faltung mit einem Gauß-Profil (Voigt) erhalten, oder wenn das zweite mit einem schmutzigen Gauß- Profil wirklich dominant ist ...
quelle
Es gibt immer einen Kompromiss. Die Ausführungsumgebung des Programms und die von ihm ausgeführten Aufgaben sollten die Grundlage für die Entscheidung sein, ob HEAP verwendet werden soll oder nicht.
Das Heap-Objekt ist effizient, wenn Sie die Daten für mehrere Funktionsaufrufe freigeben möchten. Sie müssen nur den Zeiger übergeben, da auf den Heap global zugegriffen werden kann. Es gibt auch Nachteile. Einige Funktionen können diesen Speicher freigeben, aber auch an anderen Stellen sind möglicherweise einige Referenzen vorhanden.
Wenn der Heap-Speicher nach Abschluss seiner Arbeit nicht freigegeben wird und das Programm weiterhin mehr Speicher zuweist, geht HEAP irgendwann der Speicher aus und beeinflusst den deterministischen Charakter des Programms.
quelle
.data
und.bss
wie ...?