Warum sind lokale Variablen reentrant

Gast #5218156
Lesenswert?

Der Platz auf dem Stack wird zu Beginn der Funktion durch Anpassung des 
Stack Pointers reserviert. Wenn die Funktion sich jetzt selbst aufruft, 
passiert dasselbe nochmal, beide Instanzen haben also ihre eigenen 
Bereiche auf dem Stack und kommen sich nicht in die Quere.

In Multithreading-Umgebungen funktioniert das ebenfalls, weil jeder 
Thread seinen eigenen Stack hat.

Bei globalen Variablen wiederum greifen beide Instanzen auf denselben 
Speicher zu, was Reentranz-Probleme mit sich bringen kann.
Moderator (Firma: Titel) Persönliche Seite #5218168
Lesenswert?

Johannes H. schrieb:
> weshalb lokale Variablen reentrant sind?
Funktionen sind reentrant, weil ein wiederholter/rekursiver Aufruf der 
Funktion andere Speicherplätze für "normale" lokale Variablen auf dem 
Stack belegt.
Statische lokale Variablen werden allerdings nur 1x angelegt. Eine 
Funktion mit solchen Variablen hat also durchaus Seitenwirkungen...

Siehe z.B. http://codepad.org/O3QFFd5D
#5218254
Lesenswert?

Johannes H. schrieb:
> wie schon im Betreff schon angegeben ist meine Frage weshalb lokale
> Variablen reentrant sind?

Ob Funktionen mit ihren Parametern und "auto" Variablen reentrant sind 
oder nicht, wird zunächst von der Programmiersprache definiert. Und C 
definiert das so, also ist das in vollständig konformen 
Implementierungen auch so.

Es gibt allerdings Hardware, die sich damit etwas schwer tut. Etwa wenn 
sie mangels entsprechender Adressiermöglichkeit statische Daten 
wesentlich leichter ansprechen kann, als Daten auf einem Stack (8051, 
8-Bit PICs unterhalb der erweiterten PIC18). Da kann es sein, dass 
Funktionen nur auf ausdrücklichen Wunsch reentrant sind.
#5218276
Lesenswert?

A. K. schrieb:
> Da kann es sein, dass Funktionen nur auf
> ausdrücklichen Wunsch reentrant sind (8051, 8-Bit PICs).

Ja, der Keil C51 erstellt einen Calling-tree und überlagert Variablen. 
Das ergibt einen deutlich kompakteren Code gegenüber Push/Pop-Orgien, da 
er die meisten Operationen direkt im SRAM ausführen kann. Das ist aber 
kein Nachteil, da reentrante Funktionen nur sehr selten benötigt werden.
Gast #5218279
Lesenswert?

Vielen Dank für die Antworten. Angenommen ich arbeite in einer 
Echtzeitumgebung, in der zwei Tasks auf eine Funktion zugreifrn.
1
 voif func_test( int x, int y)

Wenn jetzt der nieder priorer Task mitten in der Funktion durch den 
höhrer prioren Task preempted wird, werden die Variablen x und y im 
Stack vom nieder prioren Task gespeichert und sobald dieser Task wieder 
CPU zeit erhält werden diese vom Stack geladen und der Task setzt seine 
Arbeit in der Funktion fort?

Liege ich da richtig?
#5218340
Lesenswert?

Johannes H. schrieb:
> Vielen Dank für die Antworten. Angenommen ich arbeite in einer
> Echtzeitumgebung, in der zwei Tasks auf eine Funktion zugreifrn.
>
>
1
 voif func_test( int x, int y)
>
> Wenn jetzt der nieder priorer Task mitten in der Funktion durch den
> höhrer prioren Task preempted wird, werden die Variablen x und y im
> Stack vom nieder prioren Task gespeichert und sobald dieser Task wieder
> CPU zeit erhält werden diese vom Stack geladen und der Task setzt seine
> Arbeit in der Funktion fort?
>
> Liege ich da richtig?

Nicht so ganz. Sobald einer der beiden Threads (egal welcher) die 
Funktion aufruft, werden die lokalen Variablen der Funktion (die du hier 
gar nicht gezeigt hast; du redest nur von den Funktionsparametern, was 
wieder etwas anderes ist) auf dem Stack des gerade laufenden Threads 
angelegt. Und da jeder Thread seinen eigenen Stack(bereich) hat, kommen 
sie sich nicht in die Quere.

Bei einem Taskwechsel werden auch keine lokalen Variablen hin und her 
geschoben, sondern es wird der Stackpointer der CPU auf den gültigen 
Wert für den neuen Thread gesetzt. Da Variablen auf dem Stack relativ 
zum Stackpointer adressiert werden, findet der neue Thread seine lokalen 
Variablen wieder am gleichen Offset wie vorher.

Funktionsparameter werden entweder in Registern übergeben - da werden 
sie bei einem Taskwechsel zusammen mit allen alderen Registern mit 
weggesichert. Oder sie werden auf dem Stack übergeben und dann gilt das 
oben gesagte.
#5218345
Lesenswert?

Axel S. schrieb:
> Nicht so ganz. Sobald einer der beiden Threads (egal welcher) die
> Funktion aufruft, werden die lokalen Variablen der Funktion (die du hier
> gar nicht gezeigt hast; du redest nur von den Funktionsparametern, was
> wieder etwas anderes ist) auf dem Stack des gerade laufenden Threads
> angelegt.

Register werden beim Taskwechsel nicht notwendigerweise auf dem Stack 
abgelegt. Das kann auch woanders sein.
Gast #5218361
Lesenswert?

Axel S. schrieb:
> Funktionsparameter werden entweder in Registern übergeben - da werden
> sie bei einem Taskwechsel zusammen mit allen alderen Registern mit
> weggesichert. Oder sie werden auf dem Stack übergeben und dann gilt das
> oben gesagte.

Das heißt, wenn ich meiner Funktion die Parameter call-by-value 
übergebe(wie in dem obigen Beispiel), dann ist es sichergestellt, dass 
sich die Tasks nicht in die Quere kommen?

Heißt das, dass Register auch individuell gespeichert werden? (Bezogen 
auf Tasks)
Gast #5218387
Lesenswert?

Sie kommen sich nicht in die Quere.

Wie das im Detail abgehandelt wird, ist Systemabhängig.
Und sollte damit nur den Systemprogrammierer interessieren.
Der Anwendungsprogrammierer muss sich nicht damit belasten.
#5218418
Lesenswert?

Johannes H. schrieb:
> Ok und wie schaut das aus wenn ich die Parameter call-by-reference
> übergebe? Dann ist meine Funktion nicht mehr reentrant oder?

Das hängt nicht von der Funktion ab. Übergeben werden dann die Adressen 
von den Parametern per call by value. Was dahinter steht ist der 
Funktion selbst egal. Sind es Adressen von verschiedenen Variablen, gibt 
es kein Problem. Sind sie gleich, dann schon.
#5218562
Lesenswert?

A. K. schrieb:
> Axel S. schrieb:
>> Sobald einer der beiden Threads (egal welcher) die
>> Funktion aufruft, werden die lokalen Variablen der Funktion (die du hier
>> gar nicht gezeigt hast; du redest nur von den Funktionsparametern, was
>> wieder etwas anderes ist) auf dem Stack des gerade laufenden Threads
>> angelegt.
>
> Register werden beim Taskwechsel nicht notwendigerweise auf dem Stack
> abgelegt.

Huch. Wo soll ich das behauptet haben?
#5219315
Lesenswert?

Peter D. schrieb:
> A. K. schrieb:
>> Da kann es sein, dass Funktionen nur auf
>> ausdrücklichen Wunsch reentrant sind (8051, 8-Bit PICs).
>
> Ja, der Keil C51 erstellt einen Calling-tree und überlagert Variablen.
> Das ergibt einen deutlich kompakteren Code gegenüber Push/Pop-Orgien, da
> er die meisten Operationen direkt im SRAM ausführen kann. Das ist aber
> kein Nachteil, da reentrante Funktionen nur sehr selten benötigt werden.

Man könnte bei solchen Architekturen (wie x51), die absolute 
Adressierung "bevorzugen" und reentranten Funktionen mit vielen 
Zugriffen auf lokale Variablen, diese wie Statische ablegen und nur am 
Anfang/Ende diese am Stück auf/von den(/einen) (Software/)Stack 
sichern/zurückladen. Solange x51 ein Single-Core bleibt ;-)
Und ob sich der Aufwand lohnt, bei der Konkurrenz der 32Bitter, ist 
natürlich eine andere Frage.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren