Fortgeschrittene Scheduler
Lernziele
Funktionsweise realer Scheduler kennen lernen
I/O und Overlapping
Wir haben zwei Typen von Schedulern kennen gelernt
SJF/STCF optimiert Turnaround-Zeiten, ist jedoch ungünstig für Antwortzeiten
RR optimiert die Antwortzeit, ist aber ungünstig für die Turnaround-Zeit
Es gibt auf Basis des vorangehenden Abschnitts noch zwei Annahmen/Restriktionen, die »aufgelöst« werden müssen
Alle Jobs verwenden ausschließlich die CPU
Laufzeit eines jedes Jobs ist bekannt
Input/Output
Lösen wir daher die nächste Restriktion: Ab sofort können Jobs auch I/O-Operationen aufrufen
Scheduler muss nun entscheiden wann eine I/O-Operation durchgeführt wird, da in der Zeit der laufende Prozess die CPU nicht nutzen kann und sich somit im Status »blocked« befindet
Scheduler kann demnach in dieser Zeit einen anderen Job laufen lassen
Ist die I/O-Operation fertig (wird über Interrupt angezeigt), wird der zuvor geblockte Job wieder auf »ready« gesetzt
Ab jetzt kann er Job potentiell wieder la
Overlapping
Schlechte Ressourcen-Nutzung
Bessere Ressourcen-Nutzung dank Overlapping
Prozessdauer
In Wirklichkeit haben wir kein Wissen über Prozessdauer
Als letzte Restriktion lösen wir nun die Kenntnisse über die Prozesslaufzeit auf
D.h. der Scheduler weiß nichts über die Restlaufzeit eines Prozesses
Wie kann dann sinnvoll gescheduled werden?
Zuletzt lassen wir daher die Annahme fallen, dass wir die Laufzeit eines Prozesses im Vorhinein wissen
Wie kann ohne diese Kenntnisse ein Scheduler gebaut werden, er sowohl Antwortzeiten (z.B. für interaktive Anwendungen) als auch die Turnaround-Zeiten (d.h. ein Job möglichst schnell fertig stellen) ohne Wissen über die Laufzeit eines Prozesses minimiert?
Lösungsidee: sog. »Multi-Level Feedback Queue«-Ansätze verwenden die nahe Vergangenheit, um die Zukunft vorauszusagen! 🤩
Multi Level Feedback Queue (MLFQ)
Grundlegende Regeln
MLFQ hat mehrere Queues, jede mit einem Prioritäts-Level
Jobs mit höherer Priorität laufen zuerst (=höhere Queue)
Falls sich mehrere Jobs in der gleichen Queue befinden gilt:
Regel 1:
If Priority(A) > Priority(B), A runs (B doesn‘t)
Regel 2:
If Priority(A) == Priority(B), A & B run in Round Robin
Wie wird jedoch die Priorität für ein Job festgelegt?
Priorität nicht fix, sondern hängt vom beobachteten Verhalten des Jobs ab
Wenn die ganze CPU-Zeit auf A und B verteilt wird, wie kommen dann aber C und D zum Zug?
Um die Fragen zu beantworten nähern wir uns der Lösung schrittweisen:
1. Versuch - Prioritäten ändern
Workload Betrachtung: Mischung aus...
interaktiven Jobs, die kurz laufen, geben CPU schnell wieder frei und
langlaufende Jobs, die die CPU-intensiv in Anspruch nehmen, aber deren Antwortzeit »nicht relevant« ist.
Zusätzliche Regeln:
Regel 3: Ein neu eintreffender Job erhält immer die höchste Priorität (oberste Queue)
Regel 4a: Wenn ein Job die gesamte Zeitscheibe aufbraucht, wird seine Priorität herabgestuft (d.h. eine Queue nach unten geschoben)
Regel 4b: Wenn ein Job die CPU vor Ablauf der Zeitscheibe freigibt, bleibt er auf der gleichen Priorität (d.h. bleibt in der aktuellen Queue)
Beispiel 1: Ein langlaufender Job
Job läuft immer bis ans Ende der Time Slice
Nach jeder Time Slice wird der Job heruntergestuft
Am Ende läuft der Job auf der niedrigsten Priorität
Beispiel 2: Ein zusätzlicher »Kurzläufer«
Bei 𝑇=100 trifft ein zweiter, kurzlaufender Job ein
MLFQ trifft immer die Annahme, dass ein neuer Job ein »Kurzläufer« ist
Mischung aus I/O-intensivem und CPU-intensivem Job
Nach Regel 4 bleibt der Job, der die CPU schnell freigibt, weil er z.B. auf die Tastatur wartet, hoch priorisiert
Wer sieht denn das Problem?
Game the Scheduler
Programm so schreiben, dass es kurz vor Ablauf der Zeitscheibe einen Dateizugriff ausführt (die Datei selbst ist uns komplett egal)
Programm bleibt hoch priorisiert, da Zeitscheibe nicht vollständig aufgebraucht
Machen wir das immer bei ≈ 99% der Zeitscheibe, könnten wir die CPU 99% übernehmen
Langlaufende Jobs bleiben auf der Strecke (engl. starvation)
Job A kommt nie mehr in eine bessere Queue, selbst wenn sich sein Verhalten ändert
Wie könnten wir das besser machen?
Versuch 2: Priority Boost
Neue Regel
Regel 5: Nach definierten Zeit s werden alle Jobs wieder in die oberste Queue verschoben
Regel 5 löst zwei Probleme
Prozesse laufen nicht mehr Gefahr der »Starvation«
Wenn ein Job »plötzlich« interaktiv würde, kann er entsprechend priorisiert werden (s. folgende Seiten)
Voodoo Constant
Spannende Frage: Wie lange sollte die Zeitspanne s sein?
Der Wert s heißt nach John Ousterhout »Voodoo Constant«.
Für die Bestimmung sog. Voodoo-Konstanten benötigt das System etwas »schwarze Magie« zu deren Bestimmung
Dilemma: Wenn s zu groß gewählt wird, können CPU-intensive Jobs doch verhungern, ist sie zu klein gewählt bekommen interaktive Jobs nicht genügend CPU
Generell sollten Voodoo-Konstanten vermieden werden (Ousterhout's Law)
Versuch 3: Verbesserte Buchführung
Problem: Regel 4a und 4b ermöglichen immer noch, dass der Scheduler ausgespielt wird
Lösungsidee: Eine verbesserte Buchführung
Merken wie viel Zeit ein Prozess in einer Queue verbracht hat
Sobald ein Prozess kumuliert eine Zeitscheibe aufgebraucht hat, wandert er eine Queue nach unten
Aus den Regeln 4a und 4b wird
Regel 4: Sobald ein Job seine gesamte Zeit auf einer Prioritätsebene genutzte hat (ungeachtet dessen, wie viel Zeit er der CPU »zurück gibt«), wird seine Priorität reduziert (d.h. er wandert eine Queue nach unten).
Tuning und MLFQ Probleme
Wie sollte MLFQ priorisiert werden?
Wie viele Queues
Wie groß sollte die Zeitspanne (engl. time slice) pro Queue sein?
Machen unterschiedliche Time Slices pro Queue Sinn?
Wie oft findet Priority Boost statt?
MLFQ Regeln
R 1: If Priority(A) > Priority(B), A runs (B doesn’t)
R 2: If Priority(A) = Priority(B), A & B run in round-robin fashion using the time slice (quantum length) of the given queue.
R 3: When a job enters the system, it is placed at the highest priority (the topmost queue).
R 4: Once a job uses up its time allotment at a given level (regardless of how many times it has given up the CPU), its priority is reduced (i.e., it moves down one queue).
R 5: After some time period s, move all the jobs in the system to the topmost queue.
Wird MLFQ überhaupt irgendwo verwendet?
Solaris
MLFQ Time-Sharing Scheduling Class wird über eine Reihe von Tabellen konfiguriert
Diese können durch einen Admin angepasst werden 😱
60 Queues, mit langsam steigenden Time Slices von 20 ms bis zu 1 Sekunde
FreeBSD
Scheduler nutzt Formel um Priorität eines Jobs zu berechnen
Wie viel CPU hat der Prozess schon verbraucht + wie ist der Verbrauch abgefallen (sog. Decay-Usage Algorithms)
Lottery Scheduling
Proportional / Fair Share Scheduler
Anstelle Turnaround-Zeiten zu optimieren, versuchen Fair Share Scheduler sicherzustellen, dass jeder Job einen gewissen Prozentsatz der CPU-Ressourcen erhält
Beispiel: Lottery Scheduling
Grundidee: Es werden Tickets vergeben, die wie in einer Lotterie gezogen werden
Prozesse, die öfters laufen sollen, erhalten schlicht mehr Lotterielose…
Grundkonzept: Tickets represent your share
Grundlegendes Konzept: Es werden Tickets vergeben (entsprechen einem CPU Share)
Beispiel:
Job A erhält 75% der Tickets (hier: Lose 0..74)
Job B erhält 25% der Tickets (hier: Lose 75..99)
Scheduler muss nun wissen, wie viele Lose es insgesamt gibt (hier: 100)
Gewinnerticket gibt an, welcher Prozess läuft
Lottery Scheduler - Überlegungen
Statistische Annäherung an gewünschte Aufteilung
Je länger die Jobs laufen, desto besser ist die Annäherung
Was ist bei einer Verteilung 99% zu 1%?
Man benötigt einen guten Zufallsgenerator
Was macht man wenn ein neuer Job dazu kommt?
Ticket Währung
User mit mehreren Tickets, kann diese einer eigene »Währung« zuordnen
Beispiel
A und B haben je 100 Tickets
A hat zwei Jobs, A1 und A2, jeder Job bekommt 500 (von insg. 1.000) User Tickets in A‘s Währung
B hat 1 Job B1, dieser bekommt 10 von 10 (User Tickets) in B‘s Währung
System konvertiert A‘s Tickets pro Job zu je 50 Tickets in der Systemwährung
System konvertiert B‘s Ticktes zu 100 Tickets in Systemwährung
Linux Completely Fair Scheduler (CFS)
Problem: Scheduling kann bis zu 5% der CPU-Ressource ausmachen
CFS führt eine virtual runtime (vruntime) ein
Jeder Prozess, der läuft, sammelt vruntime an Bei Scheduling-Entscheidung wählt der Scheduler den Prozess mit der geringsten vruntime aus
CFS: Wie oft sollte ein Prozess gewechselt werden?
sched_latency
Time Slice Dauer, typischerweise 48ms
Wird durch Anzahl der Prozesse n geteilt
Ergibt die Zeitscheibe pro Prozess
Somit ist die Zeitverteilung vollständig fair
min_granularity
Mindestdauer, typischerweise 6ms
Dieser Wert wird niemals unterschritten (Bsp. 10 Prozesse ergäbe 4,8ms pro Prozess)
CFS - Beispiel
CFS nutzt regelmäßige Timer Interrupts, der Scheduler kann Entscheidungen also immer nur zu fixen Zeitpunkten treffen
Vier Jobs (A,B,C,D), wobei B, C und D kurz nach A eintreffen
Nach der ersten Zeitscheibe wird einer der Jobs aus (B,C,D) gewählt da hier vruntime von B, C und D < vruntime von A
Nach t = 100 sind C und D fertig, danach wird die vruntime zwischen A und B aufgeteilt
CFS - Weighting / Niceness
CFS ermöglicht die Angabe von Prioritäten, damit Prozesse mehr CPU-Ressourcen erhalten können.
In UNIX entspricht das dem »nice level«
Kann zwischen -20 und + 19 gesetzt werden
0 ist Standardwert
< 0 höhere Prio, > 0 kleinere Prio
CFS: Zeitscheibe berechnen
Gewichtungen erlauben es die Zeitscheibe pro Prozess zu berechnen:
Beispiel:
2 Prozesse A (Prio=-5), B (Prio=0)
𝑤𝑒𝑖𝑔ℎ𝑡𝐴 = 3121, 𝑤𝑒𝑖𝑔ℎ𝑡𝐵=1024
A erhält 36ms, B erhält 12ms
CFS: vruntime berechnen
Hinweis:
Gewichtung bleibt im Verhältnis gleich, wenn andere Prioritäten gewählt werden
Annahme A hat 5 und B hat 10
A und B werden noch im selben Verhältnis wie zuvor gescheduled
CFS Prozesslisten
Problem: Bei mehreren hundert oder gar 1.000 Prozessen, wie wird der nächste Prozess gefunden?
Kurzes Gedankenspiel: Skalieren Listen? Hier müssten man immer aller linear durchsuchen, was in einem linearen Aufwand von 𝑂(𝑛) resultiert.
Lösung: Geschickte Wahl der Datenstruktur:
CFS speichert Prozesse in Rot-Schwarz-Bäumen (ausgeglichener Baum)
Algorithmen auf Rot-Schwarz-Bäumen sind logarithmisch mit einem Aufwand von 𝑂(l𝑜𝑔𝑛)
Deswegen: Algorithmen und Datenstrukturen
Am Beispiel des CFS sieht man, dass die Wahl einer geeigneten Datenstruktur eine signifikante Auswirkung auf ein System haben kann. Deswegen macht es durchaus Sinn, sich mit dem Thema Algorithmen und Datenstrukturen in SEB3 auseinanderzusetzen.
CFS und I/O
Was passiert eigentlich wenn ein Prozess A permanent läuft, weil B aufgrund einer I/O-Operation blockiert (z.B. 10s)?
B wacht auf und hat die niedrigste vruntime (10s kleiner als bei A)
B würde nun die CPU für 10s monopolisieren, »Starvation« von A wäre potentiell möglich
Lösung: CFS setzt die vruntime zurück
Sobald ein Job aufwacht, erhält er den Minimum Wert im Baum (Liste aller laufende Jobs)
»Starvation« wird vermieden
Nachteil: Jobs, die nur kurz schlafen, bekommen hierdurch keinen fairen Anteil
Hausaufgabe
Machen Sie sich mit dem Befehl nice
(Linux) und start
(Windows) vertraut
Last updated