Forum: Mikrocontroller und Digitale Elektronik Warum sind lokale Variablen reentrant


von Johannes H. (Gast)


Lesenswert?

Hallo Leute,

wie schon im Betreff schon angegeben ist meine Frage weshalb lokale 
Variablen reentrant sind? Ich weiß, dass lok. Variablen im Stack 
abgespeichert werden.

Lg

von batman (Gast)


Lesenswert?

Variablen sind nicht reentrant. Da bringst du was durcheinander.

von Hmmm (Gast)


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.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


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

von Pandur S. (jetztnicht)


Lesenswert?

lokale variablen sind deshalb brauchbar in einer parallel aufrufbaren 
Funktion, weil sie immer neu sind. Jeder Aufruf der Funktion hat seinen 
eigenen Satz an lokalen Variablen.

von (prx) A. K. (prx)


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.

: Bearbeitet durch User
von Peter D. (peda)


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.

von Johannes H. (Gast)


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?

von (prx) A. K. (prx)


Lesenswert?

Johannes H. schrieb:
> Liege ich da richtig?

Da du offenbar Multitasking meinst: In einem Betriebssystem oder RTOS 
hat jede Task ihren eigenen Stack und ihre eigenen Registerinhalte. 
Deshalb entsteht kein Konflikt.

: Bearbeitet durch User
von Axel S. (a-za-z0-9)


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.

von (prx) A. K. (prx)


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.

von Johannes H. (Gast)


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)

von Einer K. (Gast)


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.

von (prx) A. K. (prx)


Lesenswert?

Johannes H. schrieb:
> Heißt das, dass Register auch individuell gespeichert werden? (Bezogen
> auf Tasks)

Ja.

von Johannes H. (Gast)


Lesenswert?

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

von (prx) A. K. (prx)


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.

: Bearbeitet durch User
von Axel S. (a-za-z0-9)


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?

von round robin (Gast)


Lesenswert?

Wie sieht es denn mit local statics aus? Und Tschüss reentrant. ;-)

von Carl D. (jcw2)


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.

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.