Blog
Den Speicher eines Threads freigeben
2022-07-11
Wir wollen jeglichen Speicher, den ein Thread belegt, freigeben, sobald sich dieser mittels exit()
beendet.
Das Problem: Wir sind zu diesem Zeitpunkt sowohl im virtuellen Adressraum als auch auf dem Kernelstack des betreffenden Threads.
Eine Lösung wäre nun eine spezielle Aufräumfunktion/-thread, durch welche alles später weggeräumt wird - bis dahin residiert der Thread als Zombie im Speicher. Zur Umsetzung muss allerdings auf ordentliche Synchronisation und Datenstrukturen (Liste) geachtet werden, damit kein Zombie verloren geht (d.h. in keiner einzigen Liste mehr auftaucht).
Ein anderer Ansatz geht aber auch etwas einfacher/kürzer, bei dem wir die oben geschilderten Probleme direkt anpacken:
- Da wir beim
exit()
komplett im identitätsabgebildeten Kernelbereich agieren, ist es nicht notwendig, dass wir im aktuellen virtuellen Adressraum bleiben, wir können auch in einen beliebigen anderen Wechseln – zum Beispiel dem des nachfolgenden Thread, der als nächstes eingelastet werden soll. Damit können wir bereits alle Pagingstrukturen (und Userspaceseiten) frei geben. - Außerdem laufen (hoffentlich) alle Zugriffe auf den Page Frame Allocator ausschließlich über die Epilogebene – und wir sind gerade auf der Epilogebene. Zudem kennen wir die Implementierung und wissen, dass wir auf unbenutzte Seiten auch nichts schreiben. Wenn wir also zum Beispiel den Kernelstack wieder an den Page Frame Allocator zurück geben, aber vom Codepfad wissen, dass keine Seite von diesem bis zum Kontextwechsel angefordert wird, so können wir noch auf der Seite selbst weiterarbeiten.
- Beim dynamischen Allokator (mit welchem wir das Threadobjekt selbst allokiert haben) sieht es leicht anders aus, hier wollen wir nach einem
delete
besser nicht mehr darauf zugreifen (die Implementierung könnte komische Dinge machen).
Wenn wir nun den Scheduler::exit()
entsprechend anpassen wollen, so müßte er in etwa wie folgt arbeiten:
void Scheduler::exit() {
Thread* curr = active();
Thread* next = getNext();
// Change to mapping of next thread
next->mapping->activate();
// Destroy our current thread
delete curr;
// use `Dispatcher::go` to prevent access of `curr` data structures
// (it needs a slight modification: removing the `assert(active() == nullptr)`
go(next); }
Unabhängig vom Mechanismus gilt natürlich, dass nur die Seiten freigegeben werden dürfen, welche exklusiv von nur diesem zu beendenden Thread/Prozess verwendet (eingeblendet) wurden!