Forum: Mikrocontroller und Digitale Elektronik Mutex Problem bei ISR


von Michael W. (Gast)


Lesenswert?

Hallo !

Ich möchte in einem Interrupt Handler (UART; ist aber letztlich egal) 
das empfangene Zeichen in einen Ring schreiben - RingWrite(). Der 
Schreibvorgang ist übrigens höchst einfach.

Ein Task holt sich die Zeichen vom Ring ab, und verarbeitet diese - 
RingRead().

Nun muss gewährleistet sein, dass RingWrite() nicht durch RingRead() 
unterbrochen wird, und umgekehrt.

Angenommen der Task liest den Ring; hier wird der Lesezeiger 
weitergestellt. Wenn zufällig an einer ungünstigen Stelle unterbrochen 
wird (innerhalb von zwei drei Anweisungen), kann es zu Inkonsistenzen 
kommen.

Wie löst man solche Zugriffsprobleme richtig?

Ich machte es bisher immer so, dass ich im neuralgischen Teil der 
Zugriffe Interrupts kurzfristig deaktivierte, sodass die Code-Sequenz 
garantiert unterbrechungsfrei wird.

Ich könnte auch über binäre Semaphore arbeiten: Die ISR muss sich, bevor 
sie in den Ring schreibt einen Semaphor holen (mein OS unterstützt das 
auch für ISRs), und gibt diesen zurück, wenn sie fertig ist. Umgekehrt 
muss sich auch der Task einen Semaphor holen und zurückgeben. Dann wird 
es aber bald passieren, dass die ISR keinen Semaphor bekommt, da der 
gerade dem Task gehört. Müsste ich hier "blockierend" warten, bis der 
Semaphor frei wird? Irgendwie gefällt mir der Gedanke nicht, in einer 
ISR zu blockieren...

Die Frage: Gibt es hier noch eine andere Möglichkeit, die ich übersehen 
habe und besser ist, als meine Vorgehensweise?

Danke !

von sebi707 (Gast)


Lesenswert?

Michael W. schrieb:
> Müsste ich hier "blockierend" warten, bis der
> Semaphor frei wird? Irgendwie gefällt mir der Gedanke nicht, in einer
> ISR zu blockieren...

Das wird so schonmal gar nicht funktionieren, außer du hast einen µC mit 
mehreren Kernen und kannst tatsächlich parallel Code ausführen. Wenn du 
im Interrupt auf den Semaphor wartest hast du einen schönen Deadlock 
weil die ISR nie fertig wird und auch der restliche Code nicht weiter 
läuft und so der Semaphor niemals frei wird. Falls du bei Semaphors 
bleiben willst darf die ISR nicht blockieren und falls du in der ISR den 
Semaphor nicht kriegst musst du die ISR abbrechen. Dann musst du aber 
später dafür sorgen, dass die ISR erneut aufgerufen wird wenn der 
Semaphor wieder frei ist.

Im Endeffekt läuft das aber darauf hinaus, dass du genauso gut 
Interrupts (oder speziell nur den UART Interrupt) deaktivierst, solange 
du Daten aus dem Ring ließt. So habe ich das bisher immer gemacht.

von Michael W. (Gast)


Lesenswert?

Ja, blockierend in einer ISR, das ist natürlich Unsinn. Ich habe bisher 
Interrupts immer einfach deaktiviert.
Nun verwende ich ein sehr einfaches Betriebssystem (freeRTOS) und ich 
frage mich, ob ich nicht irgendwelche Features des OS verwenden kann, um 
solche Dinge eleganter zu machen.
Mir ist im Manual die Funktion xQueueReceiveFromISR aufgefallen, diese 
kann in einer ISR verwendet werden um aus einer Queue zu lesen. Diese 
Funktion ist aber nicht blockierend...und für mein Anliegen 
wahrscheinlich nicht zu gebrauchen.

von c-hater (Gast)


Lesenswert?

Michael W. schrieb:

> Ich machte es bisher immer so, dass ich im neuralgischen Teil der
> Zugriffe Interrupts kurzfristig deaktivierte, sodass die Code-Sequenz
> garantiert unterbrechungsfrei wird.

Genau so ist es auch richtig und das ist auch die einzige Möglichkeit, 
ISRs "wechselseitig" mit normalem Code zu synchronisieren.

> Ich könnte auch über binäre Semaphore arbeiten: Die ISR muss sich, bevor
> sie in den Ring schreibt einen Semaphor holen

Das geht nicht. ISRs können zwar freie Semaphoren belegen, aber sie 
können nicht darauf warten, daß ein belegter Semaphor irgendwann mal 
freigegeben wird, denn das wird niemals passieren, weil die ISR exklusiv 
läuft. Der Code, der den Semaphor belegt hat, kommt also nie zu der 
Stelle, wo er ihn wieder freigeben würde. Ein klassischer deadlock ist 
die Folge dieser Situation.

> (mein OS unterstützt das
> auch für ISRs)

In einer ISR auf einen belegte Semaphor warten, das kann es sicher 
nicht. Oder das OS isoliert dich von den wirklichen ISRs und es sind nur 
queued handler, mit denen du da tatsächlich zu tun hast. Die laufen als 
normaler Task (wenn auch typisch mit Realtime-Priorität) und können 
dadurch dann unterbrochen werden, wenn sie implizit oder explizit eine 
Wartefunktion aufrufen.

von Programmierer (Gast)


Lesenswert?

Vielleicht kannst du den Ring mithilfe von atomaren Instruktionen so 
implementieren, dass du gar kein Blockieren brauchst. Ansonsten darfst 
du halt nicht von der ISR aus schreiben, sondern musst von dieser aus 
einen normalen Task aufwecken, der normal unterbrechbar ist und über 
Mutex in den Ring schreibt.

von Michael W. (Gast)


Lesenswert?

c-hater schrieb:
> In einer ISR auf einen belegte Semaphor warten, das kann es sicher
> nicht.

Wie gesagt: es kann "nicht blockierend" ein Item auf einer Queue 
empfangen. Aber das hilft mir hier nicht weiter...

von Adib (Gast)


Lesenswert?

Mit welchem Controller arbeitest du denn.
Mit einem avr sollte man einen Buffet mit 8-bittigem Index konfliktfrei 
updaten können.

Zuerst Daten in den BUfer schreiben
Dann neuen Index ausrechnen und mit einer Operation updaten.
Unter der Annahme, du machst Kein read-modify-Write sollten alle 8bit 
zugriffe atomar sein.
Außerdem schreibt di Isr nur den in-Index und das Main schreibt nur den 
out-Index. Da gibt es also keine schreib- Konflikte.

Bei Arm controllern geht das dann auch mit 32 bit.

Grüße, adib.

von (prx) A. K. (prx)


Lesenswert?

Wenn in einer ISR ein Zeichen reinkommt, aber der Puffer voll ist, dann 
ist das Pech. Warten geht nicht. Da bleibt nur wegwerfen und "overflow" 
signalisieren.

Um Konflikte zwischen der dazu gehörende Main-Routine und ISRs zu 
verhindern schaltet man Interrupts ab, oder arbeitet mit speziellen 
konfliktfreien Befehlssequenzen, wenn das im betreffenden Fall geht. 
Letzteres ist etwas Denksport.

Interrupts kann man mit der Brechstange abschalten, indem man alle 
abschaltet. Man kann die Prioritäts-Ebenen explizit kontrollieren, was 
insbesondere bei Cortex-M gut funktioniert. Und man kann gezielt nur den 
Interrupt des betreffenden Device abschalten.

von Bastler (Gast)


Lesenswert?

xQueueReceiveFromISR macht genau das selbe wie xQueueReceiveFrom, 
nämlich ein Element (hier wohl Byre) in eine Queue einstellen. Das 
Semaphor-Problem wird dabei durch taskENTER_CRITICAL gelöst, was in der 
ISR-Variante nicht notwendig ist, denn taskENTER_CRITICAL ist in der 
Regel "CLI", um's mal in AVR zu sagen. Das macht doch ein OS aus, daß es 
für solche Probleme Lösungen bereit hält. Warum bei FreeRTOS beiden 
Varianten das ganze Queue-Geturne machen selbst machen müssen, who 
knows?

von Nosnibor (Gast)


Lesenswert?

A. K. schrieb:
> Um Konflikte zwischen der dazu gehörende Main-Routine und ISRs zu
> verhindern schaltet man Interrupts ab, oder arbeitet mit speziellen
> konfliktfreien Befehlssequenzen, wenn das im betreffenden Fall geht.
> Letzteres ist etwas Denksport.

Würde ich auch so machen (den Denksport). Mit einem modernen GCC muß man 
da aber aufpassen; der spielt gelegentlich an der Reihenfolge der 
Speicherzugriffe herum. Herauszufinden, welche Optimierungen man 
abschalten muß, damit er das bleibenläßt, artet schon fast in ein 
Compilerbau-Studium aus.

von Michael W. (Gast)


Lesenswert?

Danke!
Jedenfalls weiß ich nun, dass es OK ist die ISR zwischendurch kurz 
auszuschalten.

von Georg G. (df2au)


Lesenswert?

Nosnibor schrieb:
> Mit einem modernen GCC muß man
> da aber aufpassen

Eine gute libc bringt dir atomic Definitionen mit. Die überreden dann 
den GCC zu passender Arbeit.

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
Noch kein Account? Hier anmelden.