Ich habe einen ESP32 mit zwei Kernen, den ich zum Erfassen von Sensordaten nutze. Für jeden Sensor gibt es einen Task der auf das Eintreffen wartet und den Wert in einer globalen 32bit Variablen ablegt. Ein anderer Task liest die Sensorwerte periodisch aus. Normalerweise müsste der Zugriff auf 32bit-Variablen atomar sein, denn ein Core kann ja nicht gleichzeitig mehrfach auf das selbe Speicherwort zugreifen. Aber gilt das auch in einem Multiprozessorsytem? Muss ich die Zugriffe mittels Mutex schützen?
Fritz G. schrieb: > Muss ich die Zugriffe > mittels Mutex schützen? Ja, da musst du etwas Sorgfalt walten lassen. FreeRTOS dürfte auch für dich die passende Queue, oder so, haben. Auch den ganzen anderen Kram, den man für Multicore/Multitasking Angelegenheiten braucht.
:
Bearbeitet durch User
Fritz G. schrieb: > Muss ich die Zugriffe mittels Mutex schützen? Ich glaube nein. Auf dem ESP32 sind anscheinend nicht nur 32-Bit Registeroperationen atomar, sondern auch 32-Bit Speicheroperationen. Aber ich kann mich irren. LG, Sebastian
Das _Atomic / std::atomic API hilft auch bei sowas. Im Endeffekt genau wie beim PC.
Sebastian W. schrieb: > sondern auch 32-Bit Speicheroperationen. Sind sie! Aber eine Memory Barrier oder volatile wird dennoch nötig sein, damit das Datum auch wirklich im RAM landet, bzw. daraus gelesen werden kann.
Ich kenne den ESP32 nicht genauer. Im Linux-Kernel werden zur Synchronisation von echt parallelen Threads (i.e. Hyper-Threading/Multi-Core) Spinlocks eingesetzt. Die wiederum werden zur Implementation von Atomics und/oder Mutexes eingesetzt. Man darf nicht vergessen, dass der Speicher bei solchen Systemen eine relativ asynchron angeschlossene Peripherie ist (so muß ja z.B. beim Schreiben ins RAM der dazugehörige Cache-Eintrag in allen anderen Cores gelöscht werden, etc) und da gibt es viele Fallstricke und sie sind alle extrem CPU-spezifisch. Da es nicht "den" ESP32 gibt, sondern viele verschiedene Modelle mit unterschiedlichen CPUs, ist das ein Minenfeld. Wie arduinof schon schrieb, wenn du schon Multithreading machen willst, benutzt die Mittel des Betriebssystems. Das selbst zu implementieren/optimieren ist Strong Stuff© und nichts für jemanden, der fragt, ob er Mutexe benutzen muß. Die nächste Frage wäre, ob du wirklich Multithreading brauchst? Anfangs scheint es die Sache sehr einfach zu machen, erst später merkt: mit Multithreading fängt man sich auch eine Latte an Problemen ein, die man single threaded gar nicht hätte.
Foobar schrieb: > Die nächste Frage wäre, ob du wirklich Multithreading brauchst? Beim ESP32 unumgänglich, das IDF setzt FreeRTOS zwingend voraus. Foobar schrieb: > Das selbst zu implementieren/optimieren ist Strong Stuff© Tja, und die existierende Implementation von FreeRTOS ist auch teilweise buggy... Praktischerweise hat das ESP32 FreeRTOS eine sehr nützliche RingBuffer Implementation mitgeliefert.
Fritz G. schrieb: > Ich habe einen ESP32 mit zwei Kernen, den ich zum Erfassen von > Sensordaten nutze. Für jeden Sensor gibt es einen Task der auf das > Eintreffen wartet und den Wert in einer globalen 32bit Variablen ablegt. Also eine Variable pro Sensor, richtig? > Ein anderer Task liest die Sensorwerte periodisch aus. Woher weiß dieser Task, ob und welcher Sensorwert gültig bzw. aktuell ist? Woher wissen die Sensortasks, dass der Sammlertask den letzten Wert gelesen hat und es demzufolge sinnvoll wäre, den nächsten zu lesen? Das ist der eigentliche Knackpunkt bei der Synchronisation. > Normalerweise > müsste der Zugriff auf 32bit-Variablen atomar sein Ist er. Aber nicht unbedingt kohärent. Bedeutet: wenn wenigstens einmal ein gültiges Datum in den Variablen abgelegt wurde, wirst du auch ein gültiges Datum lesen. Aber nicht unbedingt das aktuelle...
Ob S. schrieb: > Also eine Variable pro Sensor, richtig? Genau, jeder Sensor hat seine eigene Variable. >> Ein anderer Task liest die Sensorwerte periodisch aus. > > Woher weiß dieser Task, ob und welcher Sensorwert gültig bzw. aktuell > ist? Woher wissen die Sensortasks, dass der Sammlertask den letzten Wert > gelesen hat und es demzufolge sinnvoll wäre, den nächsten zu lesen? Meine ursprüngliche Idee war, jedem Sensor ein Event-Flag zuzuordnen, die alle zur gleichen Event-Group gehören. Trudelt ein Messwert ein, wird das Flag gesetzt. Der Auswerte-Task wartet dann darauf, dass ein Bit der Gruppe auf 1 gesetzt wird (geht mit xEventGroupWaitBits(), wobei xWaitForAllBits auf false gesetzt ist). dann liest er den neuen Sensorwert und setzt das bit zurück. Alternativ könnte man das Eventbit in der Sensorvariablen selbst unterbringen - für die Sensorwerte brauche ich nicht die vollen 32bit. Allerdings gibt es dann nicht eine so schöne Wartefunktion und der Auswertetask müsste periodisch die Bits pollen. >> Normalerweise >> müsste der Zugriff auf 32bit-Variablen atomar sein > > Ist er. Aber nicht unbedingt kohärent. Bedeutet: wenn wenigstens einmal > ein gültiges Datum in den Variablen abgelegt wurde, wirst du auch ein > gültiges Datum lesen. Aber nicht unbedingt das aktuelle... Ein Problem hätte ich dann, wenn ein Core einen neuen Sensorwert in seinen lokalen Cache schreibt, der andere in seinem Cache aber noch den alten Wert hat. Kann man das Writethrough irgendwie erzwingen? Sonst käme nur die Signalisierung mittels Eventbit in der Sensorvariablen in Frage, mit dem oben erwähnten Nachteil.
Fritz G. schrieb: > Kann man das Writethrough irgendwie erzwingen? Laut C/C++ Standards geschieht das automatisch wenn man einen Mutex freigibt, ein Atomic beschreibt etc. D.h. die normalen Schutzmechanismen bewirken das automatisch. Es gibt aber auch explizite Memory Fence Operationen, die sind aber normalerweise nicht nötig. Allerdings haben die meisten Multicore-Prozessoren mit Cache auch integrierte Cache Kohärenz, sodass das gar nicht nötig ist (x86, und die normalen ARM Cortex-A Multicores). Es kann allerdings ARM-Prozessoren geben die das nicht haben, weiß aber nicht ob es sowas in der echten welt gibt. Bei Nutzung von DMA o.ä., was nicht auf die Cache Kohärenz zugreifen kann, braucht man aber die Cache Maintenance Operationen um den Cache zu leeren (DCCMVAC etc). Allerdings: Der ESP32 hat überhaupt keinen Cache. Ich würde trotzdem immer einen Synchronisationsmechanismus wie eben Atomics nutzen, das macht es auch portabel und vermeidet Überraschungen.
:
Bearbeitet durch User
Fritz G. schrieb: > Normalerweise > müsste der Zugriff auf 32bit-Variablen atomar sein, Ja, das ist normalerweise auch bei mehreren Cores so, sonst wäre es gar nicht möglich so Dinge wie Spinlocks zu implementieren. Im Prinzip kommt es auf die Breite der RAM Anbindung an welche Zugriffe bei Multicore atomar sind. Fritz G. schrieb: > Meine ursprüngliche Idee war, jedem Sensor ein Event-Flag zuzuordnen, > die alle zur gleichen Event-Group gehören. Trudelt ein Messwert ein, > wird das Flag gesetzt. Der Auswerte-Task wartet dann darauf, dass ein > Bit der Gruppe auf 1 gesetzt wird (geht mit xEventGroupWaitBits(), wobei > xWaitForAllBits auf false gesetzt ist). dann liest er den neuen > Sensorwert und setzt das bit zurück. > Alternativ könnte man das Eventbit in der Sensorvariablen selbst > unterbringen - für die Sensorwerte brauche ich nicht die vollen 32bit. > Allerdings gibt es dann nicht eine so schöne Wartefunktion und der > Auswertetask müsste periodisch die Bits pollen. Kann man so machen, wenn Dich immer nur der letzte Sensorwert interessiert.
Jeder Task schreibt in die selbe Queue und ein Task liest aus dieser Queue und wertet aus. am besten die Daten in ein Struct und noch eine ID dazu
was passiert denn wenn man das alles nicht beachtet und mit beiden kernen auf eine variable zugreift?
John P. schrieb: > was passiert denn wenn man das alles nicht beachtet und mit beiden > kernen auf eine variable zugreift? https://www.google.com/search?q=race+condition 😉
John P. schrieb: > was passiert denn wenn man das alles nicht beachtet und mit beiden > kernen auf eine variable zugreift? Bei einem zwei-Kern RP2040 passiert gar nichts (also schon, aber nichts Schlechtes) Auf der technischen Ebene (bei einem und nur einem 32bit Zugriff): Das interne RAM ist wird nicht durch einen cache angesprochen. Das heißt der arbiter der AHB-Lite crossbar regelt den Zugriff nach Proritäten. Sollten wirklich beide Kerne präzise zum gleichen Zeitpunkt zugreifen, dann bekommt der Kern mit der höheren Priorität den sofortigen Zugriff. Der Andere wird um einen einzelnen Zyklus angehalten und dann versorgt. Der gelesene/geschriebene Wert ist immer und zu jeder Zeit gültig. Aber, da können bei einem präzise synchronen Zugriff durchaus mal 8ns Wartezeit anfallen (beim Standard-Takt). PS. Es würde mich wundern wenn es bei STM32 und anderen Mikrocontrollern anders wäre. Da muss man das jeweilige Datenblatt studieren.
:
Bearbeitet durch User
Fritz G. schrieb: > Ich habe einen ESP32 mit zwei Kernen, den ich zum Erfassen von > Sensordaten nutze. Für jeden Sensor gibt es einen Task der auf das > Eintreffen wartet und den Wert in einer globalen 32bit Variablen ablegt. > Ein anderer Task liest die Sensorwerte periodisch aus. Normalerweise > müsste der Zugriff auf 32bit-Variablen atomar sein, denn ein Core kann > ja nicht gleichzeitig mehrfach auf das selbe Speicherwort zugreifen. > Aber gilt das auch in einem Multiprozessorsytem? Muss ich die Zugriffe > mittels Mutex schützen? In der Annahme, das du in C, oder etwas ähnlichem programmierst: Wenn du einen atomaren Zugriff willst, verwende _Atomic. Eine Mutex würde sicher auch funktionieren, aber _Atomic ist genau das, was du willst, und das kann der Compiler wahrscheinlich deutlich effizienter als die Mutex umsetzen (je nach Zielarchitektur).
:
Bearbeitet durch User
Norbert schrieb: > Das interne RAM ist wird nicht durch einen cache angesprochen. Das nicht! Aber: Die im C/C++ Compiler eingebaute Optimierung versucht Daten in den Registern zu halten. Es neigt nicht dazu diese Daten ins RAM zu schreiben, oder auch zu lesen, wenn/wann man sich das wünscht, sondern eher wenn es unvermeidlich ist.
Norbert schrieb: > John P. schrieb: >> was passiert denn wenn man das alles nicht beachtet und mit beiden >> kernen auf eine variable zugreift? > > Bei einem zwei-Kern RP2040 passiert gar nichts (also schon, aber nichts > Schlechtes) > > Auf der technischen Ebene (bei einem und nur einem 32bit Zugriff): > > Das interne RAM ist wird nicht durch einen cache angesprochen. > Das heißt der arbiter der AHB-Lite crossbar regelt den Zugriff nach > Proritäten. Sollten wirklich beide Kerne präzise zum gleichen Zeitpunkt > zugreifen, dann bekommt der Kern mit der höheren Priorität den > sofortigen Zugriff. Der Andere wird um einen einzelnen Zyklus angehalten > und dann versorgt. > Der gelesene/geschriebene Wert ist immer und zu jeder Zeit gültig. > > Aber, da können bei einem präzise synchronen Zugriff durchaus mal 8ns > Wartezeit anfallen (beim Standard-Takt). > > PS. Es würde mich wundern wenn es bei STM32 und anderen Mikrocontrollern > anders wäre. Da muss man das jeweilige Datenblatt studieren. Darauf wollte ich etwa hinaus. Ich habe beim ESP32 beide Kerne genutzt uns lasse diese auf gemeinsame variablen zugreifen. Ohne Mutex ohne atomic. Und habe bisher noch keine Probleme dabei festgestellt. Wenn mal ein Kern ein paar ns wartet ist es bei mir nicht schlimm
Philipp Klaus K. schrieb: > In der Annahme, das du in C, oder etwas ähnlichem programmierst: Nein. Darum schrieb ich: Auf der technischen Ebene … Es ging explizit nicht um irgendwelche höheren Programmiersprachen. Das heißt Maschinencode, oder für diejenigen welche sich in den Besitz eines Assemblers gebracht haben, Assemblercode.
John P. schrieb: > Darauf wollte ich etwa hinaus. > Ich habe beim ESP32 beide Kerne genutzt uns lasse diese auf gemeinsame > variablen zugreifen. > Ohne Mutex ohne atomic. > Und habe bisher noch keine Probleme dabei festgestellt. Ja genau, sobald der (Hardware)Buszugriff 32bit breit erfolgt, kann es ohne cache gar nicht anders sein. (Bei externem RAM jedoch durchaus möglich) Das ESP32 Datasheet lässt da durchaus eine Menge zu wünschen übrig.
Arduino F. schrieb: > Die im C/C++ Compiler eingebaute Optimierung… Schon klar. Wenn ich den µC in Cobol programmiere vermutlich auch noch schlimmer. Darum schrieb ich explizit: Auf der technischen Ebene …
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.