Ich habe ein Projekt mit einem ESP32-S3-Controller, der zwei µC-Cores enthält und FreeRTOS als Betriebssystem nutzt. Mehrkernprozessoren sind für mich Neuland, deshalb hier eine Grundsatzfrage: Mein System sammelt Daten aus mehreren unterschiedlichen Quellen: UDP über WLAN, RS232, RS485 und SPI. Jede Quelle hat einen eigenen Task, der sich um den Empfang der Daten kümmert. Die Daten werden dann in einer globalen Struktur gesammelt. Jedes Strukturelement wird nur von genau einem Task geschrieben, allerdings können mehrere Tasks darauf lesend zugreifen. Im Moment habe ich jeden Zugriff auf die Struktur, egal ob Lesen oder Schreiben, mit einem Mutex abgesichert. Meine Frage: Ist das in diesem Fall wirklich notwendig? Die Strukturelemente sind 8, 16- oder 32-bit Variablen und sollten eigentlich "in einem Rutsch" geschrieben und gelesen werden, so dass es bei einem Einkernprozessor nicht möglich sein sollte, dass teilweise geschriebene Werte gelesen werden. Nun hat der ESP32-S3 zwei Kerne. Was würde passieren, wenn ein Kern genau in dem Moment auf eine Speicherstelle schreibt während der andere diese liest? Wäre es zumindest möglich, die Strukturelemente als Atomic zu deklarieren, um nicht den umständlichen Weg über den Mutex gehen zu müssen? Zur Info: Queues brauche ich hier nicht, da nur der jeweils aktuelle Wert interessiert, nicht die lückenlose Vorgeschichte.
Fritz G. schrieb: > Die > Strukturelemente sind 8, 16- oder 32-bit Variablen und sollten > eigentlich "in einem Rutsch" geschrieben und gelesen werden, so dass es > bei einem Einkernprozessor nicht möglich sein sollte, dass teilweise > geschriebene Werte gelesen werden. Bist du dir da sicher? Wenn du z.B so schöne Struct-Union Kunstrukte verwendest um z.B. high-word und low-word einer 32-bit variable zu setzen, dann bist du zumindest in RAM-Bereichen, die kein unaligned-access mögen nicht mehr atomic unterwegs. Ganz ehrlich, solche Zugriffe auf gemeinsamen Speicher gehören einfach geschützt. Die Wahrscheinlichkeit, dass man irgendwann mal irgendeine Abhängigkeit übersieht, oder auf einmal eine 64-bit Variable ablegt ist einfach zu groß um sie zu ignorieren. Solche Fehler dann zu finden ist auch nicht unbedingt super einfach weil extrem schwer nachzustellen. Übrigens: ich verwende in solchen Fällen meistens Helper-Klassen in C++... Verhält sich nach außen wie eine beliebige Variable (Template sei dank) und schreib-lesezugriffe (operator-überladung) werden über einen Mutex geschützt. Minimaler mehraufwand und nie wieder ein Problem :) 73
Beitrag #7922627 wurde vom Autor gelöscht.
Tobias schrieb: > Für sowas gibt es mutexe, semaphoren und queueing. Fritz G. schrieb: > Wäre es zumindest möglich, die Strukturelemente als Atomic zu > deklarieren, um nicht den umständlichen Weg über den Mutex gehen zu > müssen? So ein Dreizeiler ist aber zu unbequem... Soweit ich weiß, haben einige Controller das schon in Hardware gegossen. Ob das auch für den ESP32 gilt, weiß ich nicht.
Auf dem ESP32 läuft grundsätzlich immer FreeRTOS. Auch wenn man "nur" mit Arduino darauf arbeitet. Im Prinzip kann man mit der ESP IDF und VS Code wie gewohnt alle FreeRTOS Funktionalitäten Nutzen.
Es ist wie bereits von dir vermutet. Aufgrund der Caches musst du zwangsläufig synchronisieren, sonst sehen die Cores ihre gegenseitigen Änderungen nicht sofort bzw. nie. Das hat sich erst mal nichts mit Interrupts zu tun. Wenn es um “einfache” Variablen geht, reicht std atomics wie von dir angesprochen. Da kannst du auch die Art der Synchronisation genau einstellen und optimieren falls nötig. Mutex hat auch eine Speicherbarriere drin, funktioniert also auch in jedem Fall. Mutex brauchst du aber nur bei größeren Strukturen bzw. falls du wirklich einen Programmabschnitt schützen willst. Bei einem Mutexlock passiert noch eine ganze Menge im Kernel.
Tobias schrieb: > Auf dem ESP32 läuft grundsätzlich immer FreeRTOS. Fast immer ;-) https://www.espressif.com/en/sdks/esp-zephyr
Alexander schrieb: > Wenn es um “einfache” Variablen geht, reicht std > atomics wie von dir angesprochen. Aber auch nur wenn die gewünschte Operation sich "atomic" abbilden lässt und man keine Abhängigkeiten zwischen mehreren Atomic-Variablen hat. Fritz G. schrieb: > Die > Strukturelemente sind 8, 16- oder 32-bit Variablen und sollten > eigentlich "in einem Rutsch" geschrieben und gelesen werden Schreibt jeder Thread wirklich nur eine 8-32bit Variable, oder sind es mehrere? Wenn es mehrere sind, jeweils Atomic, kann natürlich ein anderer Thread zwischendurch einen Zwischenzustand auslesen, wenn noch nicht alle Variablen gesetzt worden sind - aber jede Variable ist für sich intakt. Je nachdem, welche Memory-Order man bei Atomics angibt, kann es sogar sein, dass ein Thread die Änderungen der einzelnen Atomics in der falschen Reihenfolgie sieht. Bei den ESP32 kann das aber nicht passieren, da sie ein Cache-Coherency-Protokoll haben, genau wie x86 und ARM (teilweise je nach Konfiguration). Alexander schrieb: > Mutex hat > auch eine Speicherbarriere drin Atomics auch, je nach Einstellung der Memory-Order Alexander schrieb: > Bei einem Mutexlock > passiert noch eine ganze Menge im Kernel. Nicht unbedingt, zunächst versuchen typische Mutex-Implementationen die Sperre mittels Atomics zu setzen; nur wenn ein Thread wirklich warten muss, muss der Scheduler das umsetzen. FreeRTOS für Multicore ist übrigens etwas buggy, gerade auch auf ESP32. Anscheinend haben die ESP32 auch Hardware-Bugs bei simultanem Zugriff auf Daten im PSRAM.
Fritz G. schrieb: > Jedes Strukturelement wird nur von genau einem Task geschrieben, > allerdings können mehrere Tasks darauf lesend zugreifen. Im Moment habe > ich jeden Zugriff auf die Struktur, egal ob Lesen oder Schreiben, mit > einem Mutex abgesichert. Meine Frage: Ist das in diesem Fall wirklich > notwendig? Die Strukturelemente sind 8, 16- oder 32-bit Variablen und > sollten eigentlich "in einem Rutsch" geschrieben und gelesen werden, so > dass es bei einem Einkernprozessor nicht möglich sein sollte, dass > teilweise geschriebene Werte gelesen werden. "sollte" ist das richtige Wort. Du weißt es eben nicht 100% sicher, wenn du keine Atomic-Typen verwendest. Aber Synchronisation brauchst du ja meist nicht nur für Zugriffe auf Einzelwerte, sondern auch auf Gruppen von zusammegehörenden Werten. Sagen wir mal, du hast einen 3-Achs-Beschleunigungssensor. Dann möchte man in der Regel ja immer 3 zusammengehörende Werte auswerten und nicht 2 neue und einen alten, weil der noch nicht in die Variable geschrieben wurde. Oder du liest irgendwo vom LAN ein Datagramm ein. Dann gehören die Bytes (und der Wert für die Größe) ja auch zusammen, und es sollte nicht auf irgendwas halb fertiges zugegriffen werden.
:
Bearbeitet durch User
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.