Druckversion

Design Document Assignment 2

Bisher verfügt SWEB nur über einen statischen Speicher, in dem die Pages der Processe gelagert sind. Ziel dieses Assignments ist es SWEB um einen dynamischen Speicher zu erweitern, um die Inhalte vom schnellen Speicher auf das Filesystem auszulagern, sodass mehr Speicher vergeben werden kann. Um ein sinnvolles Caching zu betrieben, werden Statistiken zur Usage ermittelt und ein vorhandener Page-Replacement-Algorithmus implementiert. Kopieren des Speichers kostet Zeit, deswegen ist es ein Teil unserer Aufgabe bei einem fork() nicht auf einmal das komplette Speicherabild eines Prozesses zu kopieren, sondern erst bei Bedarf (Copy-On-Write). Ein weitere Teil der Aufgabe ist eine Interprozesskommunikation mittels Shared Memory zu ermöglicht (BonusTask). Das heißt das ein gemeinsamer Speicherbereich zum Austausch von Daten zur Verfügung gestellt und verwaltet wird. Selbstverständlich dürfen alle bestehenden Funktionen, unter Anderem die des vorigen Assignments, davon nicht eingeschränkt werden.

Grundlegende Designentscheidungen

Verwendung einer Inverted Page Table

Die von der x86 CPU-Architektur angebotenen Techniken zur Speicherverwaltung reichen für die Verwendung eines virtuellen Speichers nicht mehr aus. Deswegen verwenden wir eine Inverted Page Table die Metadaten zum statischen Speicher beinhaltet. Die Pagefault-Funktion ermöglicht uns einen Eingriff in den Prozess der Adressenauflösung.

Page Replacement via WS-Clock

Dieser durchsucht den Speicher nach Pages, die ein guter Kandidat für die Auslagerung wären. Da dieser Algorithmus maßgebend für eine effiziente Speicherverwaltung ist, wird die Implementierung flexibel gestaltet. So können nachträgliche Verbesserungen/Untersuchungen einfacher durchgeführt werden.

Shared Memory

Den Zugriff auf geteilte Ressourcen verwalten wir mit SIDs in der Prozessgruppen zusammengefasst werden.

Task 1 - Virtual Memory

Die Speicherverwaltung die bisher aus PageManager, den Loader -Instanzen, den UserProcessen und der Pagefault -Funktion resultierte, wird um einen zusätzlichen SwapManager erweitert. Diese Instanz bindet eine BDVirtualDevice (swap) ein und verwaltet diese.

Was ist alles Swap?

Alle Partitionen ab und inklusive der dritten werden als Swappartition erkannt und für diese wird ein SwapManager instanziert. Es wird jedoch immer der erste SwapManager als default verwendet. Ein SwapManager wird durch Ableiten von der Klasse Thread ausführbar gemacht, sodass er ab einem bestimmten Speicherverbrauch anfängt Pages auf die Partition auszulagern. Instanziert werden die Objekte in der startup -Funktion von SWEB.

Asynchron

Da benötigte I/O-Funktionen von der Klasse BDVirtualDevice verwendet werden und die Operationen viel Zeit beanspruchen, wird das SwapOut asynchron gestaltet. Dazu verwenden wir eine Liste die maximal 5 Einträge beinhaltet. Dadurch vermeiden wir, dass die Liste veraltet und der SwapManager nur mehr mit Pages auslagert, die schon gar nicht mehr verwendet werden.

Addressauflösung

Zur Auflösung der physikalischen zur virtuellen Adresse wird eine Inverted-Page-Table ( IPT) verwendet, die vom IPTManager verwaltet wird.

Beim Mapping von swap auf den virtuellen Speicher vom UserProcess werden Pageframes verwendet, die ähnlich wie die Pages am RAM organisiert sind. Dazu wird die Blocksize von swap auf die PAGE_SIZE gesetzt und die Belegung der Blöcke wird in einem Array mit aufgezeichnet.

Die Funktionen in ArchMemory wurden erweitert um den Speicherzustand in der IPT abzubilden.

Die verschiedenen Typen von Pages und deren Kennzeichnungen im PTE (Page-Table-Entry) sind:

  • SWAP: Die Page ist am swap verfügbar. Der Index auf dem Pageframe am swap ist in der page_base_adress vom PTE gespeichert oder im IPT sofern die Page auch im statischen Speicher verfügbar ist.
  • SHARED: Die Page wird von mindestens zwei Prozessen referenziert. Solche Pages entstehen im Fall von Shared Memory oder Copy-On-Write Mappings.
  • DIRTY: Die Inhalte der Page wurden verändert. -> Der Inhalt am Swap ist outdated.

Eine virtuelle Page "existiert", wenn entweder PRESENT, SWAPPED oder MEMIO-Flag gesetzt ist. Das SHARED-Flag wird immer im Zusammenhang mit diesen verwendet.

Swapoperationen

Die Auslagerung findet erst statt, sobald wenige Pages im statischen Speicher frei sind. Ab wann bestimmt eine Konstante (SWAPOUT_THRESHOLD). Diese ist in debug.h definiert . Defaultmäßig ist sie auf 5 (zu Testzwecken). EXEC Pages werden zwar als SWAPPED markiert aber nicht in das Swapdevice geschrieben, beim Ersetzen (=evict) der EXEC Page wird SWAPPED wieder auf 0 gesetzt und PRESENT auf 0. Dies bewirkt, dass bei einem Pagefault wieder aufs binary zugegriffen wird. Ersetzt werden Pages aus dem RAM erst dann, wenn eine Speicheranforderung stattfindet und keine Seite im RAM mehr frei ist.

Ablauf von SwapIn

Ein SwapIn wird immer dann ausgeführt, sobald bei einem PageFault das PRESENT -Flag nicht gesetzt und das SWAPPED -Flag gesetzt ist.

Anhand der page_table_base_adress im IPTs wird die Swap-Adresse ermittelt. Anschließend wird die Page an eine neue physikalische Adresse kopiert. Schlussendlich wird die Page noch zur IPT hinzugefügt.

Ablauf von SwapOut

Es wird auf die Liste to_be_swapped_ zugegriffen, die vom PRAThread befüllt wird. Ist diese leer, wird eine zufällige Page ausgewählt. Es wird überprüft ob das DIRTY -Flag der übergebenen Page gesetzt ist. Ist dies der Fall wird die Seite in den virtuellen Speicher geswappt, das SWAPPED -Flag auf 1 gesetzt. Sind das DIRTY und SWAPPED -Flag nicht gesetzt, wird auch in den Swap geschrieben. Fuer den Fall, dass DIRTY = 0 und SWAPPED = 1 geschieht nichts (Swap ist aktuell).

Virtuelle Zeitmessung

Die von WSClock geforderte virtuelle Zeit wird lokal im jeweiligen UserProcess gespeichert und bei jedem Timer -Interrupt erhöht (wenn dieser Prozess ausgefuehrt wird). Die Systemzeit wird in der Variable uptime_ gespeichert (InterruptUtils). Dies ist ein Counter der in INT0 erhöht wird um die Laufzeit des Systems zu approximieren.

Statistiken zum virtuellen Speicher

Informationen zu den Speicherzugriffen werden aufgezeichnet, um die Qualität des PRA zu messen. Die Libc -Funktion int stats(int which_stat, int which_process) ruft diesen Syscall auf. Dieser implementiert grundlegende Statistikabfragen.

Der Parameter which_stat entscheidet, welche Statistik wir abfragen wollen:

  • 0 = # der Pagefaults im Prozess
  • 1 = # der verwendeten Pageframes im RAM vom Prozess
  • 2 = # der verwendeten Pageframes im Swap vom Prozess
  • 3 = # der freien Pageframes im RAM
  • 4 = # der freien Pageframes im Swap
  • 5 = Runtime des Prozesses
  • 6 = Systemuptime
  • 7 = Anzahl der Pageframes des Prozesses, die nur im RAM liegen
  • 8 = Anzahl der Pageframes des Prozesses, die nur im Swap liegen

Der Übergabeparameter which_process entscheidet, für welchen Prozess wir die Statistiken haben wollen, wobei 0 für den aufrufenden Prozess steht und jede Zahl > 0 für eine ProzessID.

Änderungen im PageManager

Im PageManager ist eine neue Funktion throttleRAM(uint32 pages_to_throttle) vorhanden. Diese Funktion setzt eine Anzahl an Pages im statischen Speicher auf Reserved. Damit können wir zu Testzwecken den Speicher begrenzen. Der PageManager sucht jetzt zusätzlich wenn keine freie Page im statischen Speicher vorhanden ist eine Page (via SwapManager::evictPage()) die überschrieben werden kann.

SwapManager

Dieser Thread läuft im Hintergrund und schreibt Pages in den zugehörigen swap, die zum Auslagern markiert sind, die noch nicht im swap liegen und Pages, die zum Auslagern markiert sind und bereits im Swap liegen, aber ihr dirty bit gesetzt haben.

Im SwapManager wird in einer Liste festgehalten welche Blöcke im swap belegt sind (0 = frei, 1 = belegt). In der wird nach First Fit ein Block zum Beschreiben ausgesucht.

Page Replacement Algorithmus

Dieser Thread markiert die Pages zum auslagern. Dieser implementiert den von uns gewählten Page-Replacement-Algorithmus. Wir haben uns für WSClock entschieden, weil dieser Algorithmus für uns großes Verbesserungspotenzial aufweist und zudem leicht erweitert werden kann. Ein Möglichkeit wäre eine Modifizierung des zur Page gehörenden Zählers, um zusätzlich zur Aktualität der Page die long-term-utility einfließen zu lassen.

Beim Durchsuchen des Speichers wird das ACCESSED -Flag, das DIRTY -Flag und der Timestamp angeschaut. Wenn der Timestamp kleiner als das Workingset ist, dann gehen wir zur nächsten Page. Wenn der Timestamp älter ist als das Workingset und das DIRTY -Flag gesetzt, dann teilen wir dem SwapManager mit, dass diese Seite ausgelagert werden kann. Das Flag ACCESSED wird auf 0 gesetzt und der aktuelle Timestamp wird geschrieben.

Die Größe des Workingsets wird durch die Anzahl der Pagefaults bestimmt. Wenn ein unterer Schwellwert unterschritten wird, wird sie verkleinert, wenn ein oberer Schwellwert überschritten wird, wird sie vergrößert.

Inverted Page Table

Dient zum Übersetzen der physikalische Adresse mit PID auf die virtuelle Page. Wird direkt im statischen Speicher angelegt (unmittelbar nach den Kernelpages) und kann nicht ausgelagert werden. Die Größe wird mit 'IPT_ENTRY_SIZE * (Total RAM Pages - Pages of Kernel)' bestimmt. Es werden also darin nur die vom UserSpace benutzbaren Pages, aber nicht die Pages des Kernels gemappt.

Beinhaltet folgende Informationen zu jeder Page im statischen Speicher

  • virtuelle_page
  • pid
  • swap_adresse
  • memory type (exec, stack, .. um zu entscheiden ob und wie swappbar)

Die swap_adresse wird beim SwapOut in die page_base_adress im PTE der zugehörigen virtuellen Page kopiert. Der Memory Type wird nach einem SwapIn auf einen Typ gesetzt, der anzeigt, dass diese Page immer ausgelagert werden kann.

Task 2 - Memory Mapped I/O

Die Implementierung erlaubt es, einen Bereich in den Speicher zu mappen, der größer als das File ist. Es stehen 3 Syscalls zur Verfuegung: SC_FMAP, SC_FUNMAP und SC_FMAPFLUSH. SC_FMAP erstellt das Mapping, SC_FUNMAP entfernt das Mapping und SC_FMAPFLUSH schreibt Änderungen in das File zurueck, wenn es mit Schreibberechtigung geöffnet wurde.

  • void *fmap(int fildes, int len, int prot, int flags)
  • int funmap(void *pa)
  • int fmapflush(void *pa)

SC_FMAP

Beim mappen eines Files in den RAM wird mittels Syscall im UserProcess die Funktion mapFileToMem ausgefuehrt. Der Filedescriptor des zu mappenden Bereichs muss bereits existieren (= das File geoeffnet sein). Die Laenge in Bytes wird uebergeben und ob das File nur lesbar oder auch schreibbar ist. Im UserProcess wird fuer jeden mappenden Bereich ein Eintrag in der file_map_list_ erstellt. Dieser ist ein struct, der den Filedescriptor, die virtuelle Adresse der Startpage des gemappten Bereichs, die Laenge in Bytes und in Pages und den Zugriffsschutz speichert. ArchMemory::getFreeVirtualMem sucht nach einem freien Speicherbereich der benoetigten Laenge (von der oberen 2GB Grenze nach unten, firstfit). Wenn kein freier Speicher mehr verfuegbar ist, wird eine Kernel Panic geworfen. Ansonsten werden die Pages reserviert (PAGE_IOMAPPED = 1) und die unterste als Pointer auf diese Adresse zurueckgeliefert.

Hier ist on-demand paging anwenden, es wird also nicht sofort physikalischer RAM Speicher für die Kopie des gesamten Files angefordert, sondern jeweils nur eine Page.

SC_FUNMAP

Beim unmappen wird ueberprueft, ob es sich um einen gueltigen Pointer handelt. Dann werden die Pages unmapped und der Eintrag aus der file_map_list_ entfernt.

SC_FMAPFLUSH

Mit diesem Syscall werden Aenderungen ins File zurueckgeschrieben. Es wird immer die Anzahl an Bytes in das File geschrieben, die gemapped wurde. Wenn also mehr Bytes gemapped wurden als im File waren, wird das File vergroessert. Zuerst wird auf einen gueltigen Pointer ueberprueft und ob das File schreibbar geoeffnet wurde. Dann wird von den veraenderten Pages gelesen und in das File zurueckgeschrieben.

Shared Memory mit CoW als Sonderfall.

Durch ein geeignetes Sharing Konzept ist es moglich mit einer Klasse ShareManager und einer zentralen Liste sowohl das CoW als auch Shared Memory als beinahe identische Faelle zu verwalten.

Es wird ein PTE Flag Shared eingeführt.
PIDs die Seiten miteinander teilen werden in ShareIDs zusammengefasst.

In einer neuen Singleton Klasse ShareManager wird eine globale Liste shared_list_ erstellt und verwaltet.
In dieser sind gespeichert (SID, (PID, (vPage_base, #Pages, CoW Type) ) )
Eine SID haelt also mehrere PIDs. Eine PID kann natürlich auch mehreren SIDs angehören.
Das Paar (SID, PID) identifiziert den gesharten Bereich mit der Information vPage_base, #Pages, CoW Type, CoW Pages Remaining.

Ein Vorhandensein der virtuellen Page einer PID im Bereich einer share_list_ (vpage_base - vpage_base+#pages) ist noch keine Garantie, dass diese tatsächlich geshart wird, erst das Share Flag gibt Gewissheit.
Damit spart mann sich das genaue Festhalten in Listen, welche Seiten von CoW Prozessson tatsächlich noch geshart sind.

In dieser Art ist es möglich einen gesharten Bereich in verschiedenen Prozessen virtuell verschieden zu mappen.
Lediglich die SID muss übereinstimmen.

CoW Type identifiziert das Verhalten des PID in der SID bei Schreibzugriff.

CoW 0 .. kein CoW (Bonustask Share Memory)
CoW 1 .. CoW Verhalten

Auf diese Weise können der Fall unterschieden werden in dem ein geforkter Prozess einen write Zugriff macht.
In diesem Fall wird eine lokale Kopie des gesharten Bereichs erstellt, die PTE.Base Adresse angepasst und shared 0 gesetzt.

Da in der share_list_ nicht genau vermerkt ist welche Seiten tatsächlich noch geshart werden, sondern nur das grundlegende Verhältniss der PIDs, kann über das Shared Flag der virtuellen Page aller PIDs tatsächlich geprüft werden ob die Seite noch von einer bestimmten PID geshart wird.

Fork Ablauf mit CoW:

Setzen aller existierenden PTE Flags (existierend = swapped oder present flag gesetzt) auf Shared 1, Writeable 0.
Deep Copy aller existierenden PTEs in neuen Prozess. (PTE.Base Adressen zeigen auf Speicher des forkenden).
Eintragen der PID in SID (wenn Prozess mit CoW bereits in SID vorhanden, dieser beitreten, sonst neue).

Lesender Zugriff: kein Pagefault, PTE.Base Adressen aller Processe zeigen auf selben physikalischen Speicher .

Schreibender Zugriff:
PageFault.
Pruefen ob aufgrund von writeable 0, und ob shared 1.
Wenn ja, dann CoW Situation.
Aus eigener PID und share_list den CoW Typ ermittlen.
CoW 0 .. Access Error
CoW 1 .. lokale Speicherkopie des aktuellem Abbildes von PTE.Base Adresse erstellen,
PTE.Base Adresse auf lokale Kopie zeigen lassen und shared auf 0 setzen.
Nur tun, wenn auch tatsächlich mit jemandem geshart wird.

Freigabe von Shared Memory (shared Flag 1):
Wenn CoW 0, prüfen ob Seite mit jemandem geshart wird, wenn nein nur eigene PTE löschen (Shared = 0),
sonst physikalische Kopie löschen.

Es reicht hier in der IPT eine PID (irgendeine der entsprechenden SID) zu speicher.
Wenn nun ein Swap Ereigniss auftritt, kann ueber die virtuelle Page und die PID die SID und somit alle anderen PIDs mit denen geshared wird ermittelt werden.
Es muss also gar keine Information für den Swap Bereich gespeichert werden, da bei SwapIn ja die virtuelle Page und die PID bekannt ist und das selbe möglich ist.

Bei Swap Out also prüfen ob virtuelle Page Shared=1,
dann über share_list_ alle PIDs in SID ermitteln und das
Present, Swapped Flag und die Base Adresse zu allen
(wo Shared=1) propagieren.

Copy On Write

Bei Copy On Write werden alle PTEs kopiert und die Einträge zu den zu kopierenden Pages auf read only gesetzt. Somit wird beim ersten Schreibzugriff von egal welchem Prozess ein Pagefault auftreten. Bei diesem Pagefault muss eine Kopie für den schreibenden Prozess erstellt werden und die Page des schreibenden Prozesses wieder auf writeable gesetzt werden. Genauer Ablauf siehe Shared Memory mit CoW als Sonderfall.

Appendix

Implementationsdetails

Neue Klassen

  • PRAThread - Thread der die Timestamps aktualisiert und Seiten zum Swappen markiert
  • IptManager - Verwaltet eine Liste mit Metadaten zu jeder Seite im RAM
  • ShareManager - Regelt gemeinsamen Speicherzugriff
  • SwapManager - Verwaltet Swappartition und Cached Seiten auf der Swappartition und stellt eine reservierte Seite dem PageManager zur Verfügung
  • Random - Generiert Pseudorandomisierte Zahlen
  • Elf - Definiert ELF Struktur

Grobe Änderungen

  • ArchMemory
    • getFreeVirtualMem - such ab der oberen Grenze vom Userspace hinab bis eine Lücke gefunden wurde, die groß genug ist
    • checkAdressValid - Page ist im Virtuellen Speicher verfügbar falls present oder iomapped oder swapped
  • Loader
    • loadOnePageFromMemMappedFile - wird bei pagefault mit iomapped flag aufgerufen, page wird angefordert, gemapped und inhalt von datei wird geladen falls offset innerhalb der datei
  • Scheduler
  • Syscall
  • UserProcess
  • PageManager

Scheduler

Hält jetzt eine List processlist_; dazugehörige Funktionen sind: void addToProcessList(UserProcess* to_add); void removeFromProcessList(uint32 pid); UserProcess* getProcessFromList(uint32 pid);

Neue Attribute/Methoden in UserProcess

  • processpagefaults_ = Anzahl der pagefaults (je Prozess)
  • processruntime_ = Counter, der jedes Mal, wenn der Prozess dran kommt, hochzählt
  • tau_ = Tau des jeweiligen Processes

  • getruntime()
  • increaseruntime()
  • getpagefaults()
  • increasepagefaults()
  • checkprocesstau()
  • getprocesstau()
  • setprocesstau()

Änderungen

  • paging-definitions
  • InterruptUtils
  • debug.h
  • UserProcess
  • MountMinix
  • main.cpp

Neue Libc Bibliotheksfunktion

  • /libc/src/vscalls.c

Neue Tests

Zum Testen sind in der main.cpp Testfaelle einzukommentieren. (Siehe main.cpp bzw README fuer Anleitung)

  • stats.sweb: gibt verschiedene Statistiken ueber das System aus. Dazu wird stdin-test benoetigt, da von diesem Statistiken ausgeben werden. Nach beenden von stats.sweb kann dieses noch einmal von der Konsole gestartet werden
  • swap1000.sweb: Zeigt ein ausfuehrliches Swapverhalten mit verschiedenen Statistiken, mit inkludiertem fork und cow.
  • cowtest.sweb: Zeigt das neue Verhalten wobei bei einem fork nicht sofort der gesamte Speicher kopiert wird.
  • memiotest.sweb: Ein erstelltes Textfile wird in den Speicher gemappt, gelesen, geandert, und wieder zuruckgeschrieben.
  • ondemandmemio.sweb: Zeigt impizit das on demand paging Verhalten des Memory Mapped IO.
  • memiomultiproc1.sweb: Zeigt Interprozesskommunikation mithilfe eines gemeinsam gemappten Files.
  • sharedmem.sweb: Demonstriert Interprozesskommunikation ueber gesharte Pages.
  • fork-stress.sweb: CoW Verhalten mit unbegrenzten Forks.

Neue Syscalls

  • sc_share(arg1, arg2) wenn die sid noch nicht existiert wird eine share in der share_map erstellt mit den page welche dafür allokiert wurden und dem useprocess. wenn die sid existiert wird dem share eintrag eine share struct hinzugefügt
  • sc_unshare(arg1)
  • sc_stats(arg1, arg2) #pagefaults, #pages_in_ram, #pages_in_swap, #pages_ram_free, #pages_swap_free
  • sc_fmap(uint32 fildes, uint32 len, uint32 prot, uint32 flags) - speicherstelle wird gesucht (fd, #bytes, #pages, prot) wird in UserProcess::file_map_list gespeichert, IOMAPPED wird gesetzt, pointer zurückgeliefert
  • sc_funmap(arg1) - es wird der passende eintrag zum pointer gesucht (dieser muss auf die erste page zeigen), alle pages werden unmapped (PTE gelöscht)
  • sc_fmapflush(arg1) - der inhalt im speichert wird aufs file geschrieben

Änderungen Syscalls

  • execve arg wird nicht mit angegeben
  • pthread_create to_create wird nicht mit angegeben
  • pthread_join return_val wird nicht mit angegeben

Topic attachments
I Attachment Action Size Date Who Comment
jpgjpg howtoswap.jpg manage 21.6 K 29 Nov 2009 - 11:29 BerndKampl  

 
Creative Commons Attribution Non-Commercial Share Alike 3.0 AustriaIf not mentioned otherwise content is licensed under CC Attribution Non-Commercial Share Alike 3.0 Austria
Seepaul.org ist eine im allgemeinen offene Plattform. Für die Inhalte auf dieser Seite und den Inhalten auf anderen Seiten wird keine Verantwortung übernommen. Es wird jedoch das Recht einbehalten Werbung, unsachliche, rechtswidrige oder beleidigende Beiträge zu löschen. Es wird darauf hingewiesen, dass Benutzergenerierte Inhalte nicht die Ansichten Anderer, noch der Sicht des Benutzers wiedergeben müssen. more..