Forum: Compiler & IDEs Context Switching


von Peter Pan (Gast)


Lesenswert?

Hallo!

Ich versuche gerade das Context Switching zwischen Tasks 
nachzuvollziehen. Bei ChibiOS (Cortex-M3) und in diversen Artikeln über 
CS wird der Stack-Pointer und die relevanten Register wiederhergestellt.

Nun meine eigentliche Frage: Ein Thread startet nach einem anderen, d.h. 
dieser hat einen SP oberhalb des alten. Wenn nun umgeschalten wird, wird 
der SP des ersten Threads geladen. Soweit so gut, was passiert aber wenn 
innerhalb des ersten Threads funktionen aufgerufen werden, also Dinge in 
den Stack geschrieben werden. Wird dann nicht der Stack korrumpiert?

Mag sein, dass ich etwas im ARM ARM, Procedure Call Standard oder der 
Technical Reference Manual überlesen habe. Das sind aber auch dicke 
Schinken und wenn man nicht genau weiß was man sucht ist man verloren.

Gruß,
    Peter

von Peter II (Gast)


Lesenswert?

Peter Pan schrieb:
> Soweit so gut, was passiert aber wenn
> innerhalb des ersten Threads funktionen aufgerufen werden, also Dinge in
> den Stack geschrieben werden. Wird dann nicht der Stack korrumpiert?

ganz bestimmt nicht.

Ich kenne zwar ChibiOS  nicht, aber üblich ist das jeder Thread seinen 
eigenen Stack hat. Dieser wird beim Takswechel mit umgeschaltet.

von Andreas B. (andreas_b77)


Lesenswert?

Die Stacks müssen natürlich ausreichend Abstand haben, sprich die 
Stackgröße muss ausreichend bemessen sein. Stack Overflows sind nie gut, 
ob der Overflow jetzt in einen anderen Stack reinläuft oder in den Heap 
oder globale Variablen ist egal, nur das Fehlerbild ändert sich.

von Sam P. (Gast)


Lesenswert?

Dazu gibt es zwei Techniken (drei, wenn man eine MMU hat, also nicht in 
diesem Fall):

- Jeder Thread kriegt seinen eigenen Stack, und man lässt auf gut Glück 
genug Platz zwischen den Stacks, dass das dann schon passen wird (mit 
Erfahrungswerten, Daumen drücken und hoffen und so)

- Man benutzt einen sogenannten segmentierten Stack. Das ist hierbei der 
übliche Stand der Technik.

Der Compiler, nicht das OS, steuert das: Er kann für jede Funktion 
während des Compilierens feststellen, wie viel Stack die Funktion 
braucht (ausser man benutzt alloca oder variabel große Arrays, in dem 
Fall wird der Compiler das gesondert behandeln). Wenn er das weiß, kann 
er zu Beginn der Funktion bereits allen nötigen Stack-Speicher 
reservieren, so dass bei einem Thread-Wechsel gefahrlos eine anderer 
Thread Stack belegen kann. Jeder Thread hat also seinen Stack-Pointer, 
und ein zusätzlicher Pointer zeigt an, wo neuer Speicher belegt werden 
kann.

Da das Verfahren in dieser Weise viel Verwaltungsaufwand bei jedem 
einzelnen Funktionsaufruf benötigt und zudem zu Speicherfragmentation 
(d.h. ungenutzte Speicherbereiche, die nicht neu belegt werden können, 
weil sie z.B. zu klein sind) führen kann, wird einem Thread 
üblicherweise Stack-Speicher in fester Blockgröße (z.B. 128 Byte) 
zugewiesen. Eine Funktion muss dann am Anfang nur prüfen, ob der 
aktuelle Block noch genug Platz hat.

Das erledigen manche Compiler in nur 2 Maschinenbefehlen, ist also für 
den häufigsten Fall sehr schnell. Passt das, dann arbeitet die Funktion 
als ob es keinen segmentierten Stäck gäbe. Reicht der aktuelle Block 
nicht aus, oder benötigt eine Funktion mehr als einen ganzen Block, dann 
wird erst die langsame Speicherverwaltung durchgeführt.

In der Mitte freigewordene Blöcke können dann auch problemlos 
wiederverwendet werden, so dass die Fragmentation wesentlich geringer 
ausfällt. Sie entsteht aber trotzdem, da ein Block am Ende üblicherweise 
immer ein paar ungenutzte Bytes hat, weil dort die nächste Funktion 
nicht mehr passte.

von Peter Pan (Gast)


Lesenswert?

Stack Overflows hbe ich auch schon fabriziert... da auf den Fehler muss 
man  erstmal drauf kommen ohne große Erfahrung :-). Vielen Dank an Euch, 
ich suche dann mal danach wo die Stacks zugewiesen werden. Es war nur 
etwas eigenartig, dass auf einem Core ohne MMU den SP quasi 'beliebig' 
ändert, ohne dass Probleme auftreten.

Vielen Dank für die Raschen Antworten!
    Peter

von Lothar (Gast)


Lesenswert?

Sam P. schrieb:
> wenn man eine MMU hat

Der Cortex-M3 hat eine MPU mit der die Stacks der Threads begrenzt 
werden können. Somit sollte es keinen Stack Overflow geben. Zudem hat 
der Cortex-M3 User und Privileged Mode, und das OS sollte im Privileged 
Mode laufen und notfalls über den Memory Fault Handler mehr Stack 
bereitstellen.

von (prx) A. K. (prx)


Lesenswert?

Lothar schrieb:
> Der Cortex-M3 hat eine MPU mit der die Stacks der Threads begrenzt
> werden können.

Die MPU ist optional, nicht obligatorisch. Es gibt sie also nicht in 
allen Implementierungen mit Cortex-M3.

> notfalls über den Memory Fault Handler mehr Stack
> bereitstellen.

Das ist mit einer MPU, die keine Adressen verändert sondern nur 
überprüft, kaum realistisch. Denn ob da grad Platz für eine Erweiterung 
wäre, das wäre entweder pures Glück, oder der betreffende Platz war 
dafür reserviert wurden - dann aber hätte man den auch genauso gut 
vorneweg zuweisen können.

Man sollte auch im Auge behalten, dass die MPU der Cortex-M3 nur wenige 
kontrollierte Speicherbereiche ermöglicht und bei Grösse und Platzierung 
nur Zweierpotenzen zulässt. Das schränkt die Möglichkeiten von 
dynamischer Speicherverwaltung bei Systemen mit eher knapper 
RAM-Ausstattung weiter ein (das ist der Hauptmarkt von Systemen mit 
diesem Core).

Um Stacks in den Grenzen des Gesamtverbrauchs kalkulierbar dynamisch 
gestalten zu können benötigt man eine MMU, keine MPU. Eine MPU eignet 
sich besser dafür, eine vorwiegend statische Speicherzuteilung gegen 
Amok laufenden Code abzusichern.

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.