Forum: Mikrocontroller und Digitale Elektronik Unterschied zwischen Reentrant und Critical Section


von Johannes H. (Gast)


Lesenswert?

Hallo liebe Forenbesucher :)

Wie im Betreff schon erwähnt, handelt sich meine Frage über den 
Unterschied von Reentrant und Critical Section. Irgendwie habe ich da 
ein Denkfehler oder tu mir mit den Begriffen schwer.

Also man spricht von Critical Section (bessert mich bitte aus wenn ich 
falsch liege), wenn mehrere Tasks/Threads auf eine Shared Data zugreifen 
wollen, vor allem dann wenn ein Task lesen und einer schreiben will. 
(zum Beispiel Memory welches ich durch malloc erstellt habe). Hier kann 
es passieren, dass Dateninkonsistenz (Data race) entsteht.


Nicht-Reentrante Funktionen sind Funktionen oder librarys, welche 
globale oder statische Variablen besitzen (also alles was sich nicht am 
Stack befindet). Hier kann ebenso Dateninkonsistenz entstehen.

Bei beiden Fällen muss ich diese Kritischen Sektionen durch Mutex oder 
Semaphoren schützen.

Meine Frage ist jetzt: Betrifft der Begriff Reentrant nur Funktionen und 
Librarys oder kann ich zu einer Crtical Section auch sagen, dass diese 
Reentrant gemacht werden muss oder können nur librarys oder Funktionen 
reentrant sein?

Also mir geht vorallem um die Begrifflichkeiten und bitte bessert mich 
aus falls ich oben bei den Erklärungen fehler gemacht habe.

Lg Jooo

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

Hallo Johannes,

Johannes H. schrieb:
> Wie im Betreff schon erwähnt, handelt sich meine Frage über den
> Unterschied von Reentrant und Critical Section. Irgendwie habe ich da
> ein Denkfehler oder tu mir mit den Begriffen schwer.
>
> Also man spricht von Critical Section (bessert mich bitte aus wenn ich
> falsch liege), wenn mehrere Tasks/Threads auf eine Shared Data zugreifen
> wollen, vor allem dann wenn ein Task lesen und einer schreiben will.

Ja, etwas genauer, ist eine Critical Section der Pfad im Code, in dem 
eine Invariante verletzt ist und so halt nicht beobachtet werden darf.

> Nicht-Reentrante Funktionen sind Funktionen oder librarys, welche
> globale oder statische Variablen besitzen (also alles was sich nicht am
> Stack befindet). Hier kann ebenso Dateninkonsistenz entstehen.
>
> Bei beiden Fällen muss ich diese Kritischen Sektionen durch Mutex oder
> Semaphoren schützen.

Nein, eine Mutex oder eine Semaphore macht so eine durch gegenseitigen 
Ausschluss gesicherte Critical Section garantiert nicht reentrant.

Reentrante Funktionen sind Funktionen, die Du mitten in der Ausführung 
unterbrechen kannst und sind dann von einem anderen Kontext aus wieder 
aufrufen kannst. Wenn die Funktion einen Mutex locked, dann unterbrochen 
wird und dann wieder aufgerufen wird, blockt sie beim zweiten Versuch, 
den Mutex zu locken.

> Meine Frage ist jetzt: Betrifft der Begriff Reentrant nur Funktionen und
> Librarys oder kann ich zu einer Crtical Section auch sagen, dass diese
> Reentrant gemacht werden muss oder können nur librarys oder Funktionen
> reentrant sein?

Reentrant und thread safe sind unterschiedlich starke Garantien. Eine 
Funktion, die reentrant ist, kannst Du aus einer Interrupt service 
routine heraus aufrufen, eine Funktion die einem Mutex verwendet ist 
nicht reentrant und Du kannst sie nicht von einer ISR aus aufrufen. Eine 
Funktion, die reentrant ist, ist auch thread safe.

Um eine Funktion, die eine critical section enthält, reentrant zu 
machen, musst Du entweder potentielle interrupts disablen oder aber, die 
Änderungen müssen atomar sein, bzw. ein entsprechendes, auf atomaren 
Operationen aufbauendes Protokoll implementieren (z.B. 
pthread_mutex_lock()).

mfg Torsten

von Johannes H. (Gast)


Lesenswert?

Vielen dank für die rasche Antwort :)

von Johannes H. (Gast)


Lesenswert?

Eine Frage hätte ich noch und zwar du diesem Abschnitt:

Torsten R. schrieb:
> Reentrante Funktionen sind Funktionen, die Du mitten in der Ausführung
> unterbrechen kannst und sind dann von einem anderen Kontext aus wieder
> aufrufen kannst. Wenn die Funktion einen Mutex locked, dann unterbrochen
> wird und dann wieder aufgerufen wird, blockt sie beim zweiten Versuch,
> den Mutex zu locken.

Wer blockt hier wem? Das habe ich nicht ganz verstanden. Uns wurde in 
der Schule gesagt, dass Mutexes auch bei nicht reentrant Problemen die 
Lösung ist. Wenn ein Task die Mutex besitzt und die die Kritische 
Funktion eintritt, kann kein anderer Task diese betreten, solange der 
eine Task diese nicht freigibt oder?

Und wie mache ich es dann bei Echtzeitbetriebssytemen?

Lg

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

Johannes H. schrieb:
> Eine Frage hätte ich noch und zwar du diesem Abschnitt:
>
> Torsten R. schrieb:

> Wer blockt hier wem? Das habe ich nicht ganz verstanden. Uns wurde in
> der Schule gesagt, dass Mutexes auch bei nicht reentrant Problemen die
> Lösung ist. Wenn ein Task die Mutex besitzt und die die Kritische
> Funktion eintritt, kann kein anderer Task diese betreten, solange der
> eine Task diese nicht freigibt oder?

Die CPU sich selbst. Wenn eine Funktion z.B. einen spin lock verwendet, 
um eine kurze critical section zu locken, dann eine ISR ausführt und die 
nun wieder versucht, den spin lock zu locken, dann hängt es halt. 
Unterprechbar ist das dann nur noch mit einem ISR einer höheren Prio.

> Und wie mache ich es dann bei Echtzeitbetriebssytemen?

Kommt darauf an, was Du mit der Frage meinst ;-) Als Nutzer solltest Du 
ja eigentlich nicht mehr mit der HW mit ISRs kommunizieren mussen. Als 
Autor müsstest Du Dein OS entsprechend designen.

von Johannes H. (Gast)


Lesenswert?

Torsten R. schrieb:
> Die CPU sich selbst. Wenn eine Funktion z.B. einen spin lock verwendet,
> um eine kurze critical section zu locken, dann eine ISR ausführt und die
> nun wieder versucht, den spin lock zu locken, dann hängt es halt.
> Unterprechbar ist das dann nur noch mit einem ISR einer höheren Prio.

Also wenn eine ISR ausgeführt wird und diese die Mutex locken will, die 
Mutex aber schon durch einen Task besetzt ist dieser Task aber wiederum 
durch die ISR unterbrochen wurde, kann die ISR die reentrate Funktion 
betreten, weil sie die Mutex nicht besitzt? Hab ich das richtig 
verstanden?

> Unterprechbar ist das dann nur noch mit einem ISR einer höheren Prio.

Meinst du dass die alte ISR (welche versucht die Mutex zu locken) 
solange ausgeführt wird, bis eine höher priorisierte ISR die alte ISR 
unterbricht und ihre Arbeit erledigt? Wenn die höher priorisierte ISR 
fertig exekutiert hat, wird dann nicht wieder die alte ISR versuchen die 
besetzte Mutex zu locken?

Es tut mir wirklich Leid, aber ich hänge da irgendwie am Schlauch.

von Ruediger A. (Firma: keine) (rac)


Lesenswert?

Johannes H. schrieb:
> Torsten R. schrieb:
>> Die CPU sich selbst. Wenn eine Funktion z.B. einen spin lock verwendet,
>> um eine kurze critical section zu locken, dann eine ISR ausführt und die
>> nun wieder versucht, den spin lock zu locken, dann hängt es halt.
>> Unterprechbar ist das dann nur noch mit einem ISR einer höheren Prio.
>
> Also wenn eine ISR ausgeführt wird und diese die Mutex locken will, die
> Mutex aber schon durch einen Task besetzt ist dieser Task aber wiederum
> durch die ISR unterbrochen wurde, kann die ISR die reentrate Funktion
> betreten, weil sie die Mutex nicht besitzt? Hab ich das richtig
> verstanden?
>

Die Mutual Exclusion zwischen Tasks/Prozessen funktioniert per 
Definition grundsätzlich anders als zwischen Tasks und ISRs bzw. ISRs 
untereinander.

Die Grundregel heisst, dass ISRs im Gegensatz zu Tasks NIEMALS während 
der Ausführung "warten" dürfen. Ein gegenseitiger Ausschluss, in dem 
ISRs beteiligt sind, ist grundsätzlich "asymmetrisch." Wenn ein niedrig 
priorisierter ISR oder eine Task versucht, kritischen Code auszuführen, 
der gegen einen (höher priorisierten) ISR konkurriert, darf in dem 
Zeitraum der gesamte ISR gar nicht erst starten (also muss dieser ISR 
ausmaskiert werden). Erst wenn der "geschützte" Code fertig ist, darf 
der Interrupt wieder freigegeben werden.

Im Gegensatz dazu arbeiten Mutexes "symmetrisch," d.h. ihre Benutzung 
ist nur von tasks untereinander erlaubt und (von Details wie Priority 
Inversion Prevention abgesehen) für alle beteiligten tasks von der 
Benutzungsseite her identisch.


>> Unterprechbar ist das dann nur noch mit einem ISR einer höheren Prio.
>
> Meinst du dass die alte ISR (welche versucht die Mutex zu locken)
> solange ausgeführt wird, bis eine höher priorisierte ISR die alte ISR
> unterbricht und ihre Arbeit erledigt? Wenn die höher priorisierte ISR
> fertig exekutiert hat, wird dann nicht wieder die alte ISR versuchen die
> besetzte Mutex zu locken?
>

s.o.

> Es tut mir wirklich Leid, aber ich hänge da irgendwie am Schlauch.

Ist auch nicht ganz einfach, die Materie... ;-)

P.S. In Aller Regel versucht man Szenarien, in denen Tasks und ISRs um 
Ressourcen konkurrieren, zu vermeiden, um den ISR Durchsatz nicht zu 
verringern. Z.B. ist der typische Kontrollfluss in Gerätetreibern (Rx 
Seite) so, dass der ISR einen Ringpuffer befüllt und einer 
Verarbeitungstask Signale sendet, die den Ringpuffer wegarbeitet. Viele 
Entwickler denken nun, dass man den Ringpuffer gegen nebenläufigen 
Zugriff schützen muss, also den Interrupt ausmaskieren, während ein 
Zeichen ausgelesen wird. Bei näherer Betrachtung ist das aber in der 
Regel nicht notwendig, weil (wie eine Analyse ergibt) erst ein Ring 
Buffer Overflow ein Problem ergibt. Solange noch Platz im Puffer ist, 
darf der ISR gerne die Abarbeitung unterbrechen, um weitere Zeichen in 
den Ringpuffer einzuarbeiten. Zuviel Syncronisation ist in der 
nebenläufigen Programmierung genau so schlecht wie Zu Wenig (alte 
Bitpulerweisheit).

: Bearbeitet durch User
von Johannes H. (Gast)


Lesenswert?

Ok verstehe, also ist die einzige Möglichkeit, bevor man die Funktion 
betritt, die ISR abschalten ausmaskieren und sobald man fertig ist 
wieder einschalten, und keine statischen oder globalen Variablen 
verwenden oder alles atomar ausfüren...

Puhh das war ziemlich heftig :) jedenfalls vielen Dank für die Hilfe.

Lg Johannes

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

Johannes H. schrieb:
> Ok verstehe, also ist die einzige Möglichkeit, bevor man die Funktion
> betritt, die ISR abschalten ausmaskieren und sobald man fertig ist
> wieder einschalten, und keine statischen oder globalen Variablen
> verwenden oder alles atomar ausfüren...

Ja, und praktischer Weise macht man das nicht extern zur Funktion, 
sonder in der Funktion, den die Funktion selbst kennt Ihre Anforderung 
an Exklusivität besser.

von Peter D. (peda)


Lesenswert?

Eine Funktion ist reentrant, wenn beliebig viele Instanzen davon 
ausgeführt werden können. Ein typisches Beispiel sind Rekursionen (die 
Funktion ruft sich selbst auf).
Da Rekursionen nicht sonderlich effizient sind (Zeit-, Stackverbrauch), 
versucht der Compiler oft, sie in eine Schleife aufzudröseln.

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.