Aufgabe 5: Zeitscheiben
Wir wollen unser StuBS um ein Zeitscheibenscheduling erweitern. Also brauchen wir einen Zeitgeber. Aber welche haben wir da bei einem modernen x86er überhaupt zur Verfügung?
Der erste IBM-PC 1981 hatte bereits den Programmable Interval Timer (PIC), ursprünglich ein Intel 8253 Chip. Der Baustein wird mit einer Frequenz von 1 193 181.8181 Hz getaktet. Eine krumme Zahl, mit der sich eigentlich gar nicht so schön rechnen lässt. Wer hat das verbrochen, woher kommt die Zahl? Nun, damals war es aus Kostengründen noch üblich, Fernseher als Ausgabe für die PCs zu verwenden. Der in Nordamerika verbreitete NTSC-Standard arbeitet mit einer Frequenz von 3 579 545,45 Hz, was exakt das 3-Fache ist. Da nun damals Schwingquarze nicht besonders günstig waren, hat man versucht – um Kosten zu sparen – die ganzen Frequenzen eines Systems an NTSC anzupassen und stattdessen nur Frequenzteiler verwendet. Selbst der Systemtakt des ersten IBM PCs mit rund 4.77 MHz, und somit vier Drittel der NTSC Frequenz, wurde dadurch bestimmt. NTSC spielt heutzutage keine Rolle mehr, aber wegen der Abwärtskompatibilität wurde die Frequenz im PIT natürlich beibehalten.
Ursprünglich hing der erste seiner drei Timer beim PIC an der ersten Leitung, hatte dort also auch die höchste Priorität bei Unterbrechungen. Der zweite Timer wurde zum Auffrischen der Arbeitsspeicher verwendet: Die Speicher der günstigen DRAM basieren auf Kondensatoren, welche regelmäßig wieder geladen werden müssen, und früher war der PIT dafür verantwortlich. Heutige Arbeitsspeicher haben natürlich eine eigene Hardware dafür, teilweise ist dieser Timer im PIT entsprechend auch gar nicht mehr vorhanden.
Den dritten Timer hingegen finden wir bei manchen Desktopsystemen noch, unter anderem auf unseren Testkisten: Mit ihm kann man den PC Speaker ansteuern, welcher insbesondere vor den Zeiten der Soundkarten häufig zum Einsatz kam. Der Piepser kennt zwar nur an oder aus, aber durch Pulsdauermodulation (PWM) kann man unterschiedliche Töne erzeugen – ein Beispiel dazu folgt in Aufgabe 6.
Heutzutage haben wir auch keinen PIC mehr, sondern verwenden I/O APIC und LAPIC. Und wenn man sich das nun anschaut, vom PIT, über I/O APIC, APIC-Bus und LAPIC an die CPU, dann sieht man schon das der Timer durchaus nicht optimal angebunden ist. Aber für das Zeitscheibenscheduling reicht das eigentlich aus. Bis Wintersemester 2013 wurde auch tatsächlich der PIT dafür in StuBS verwendet, aber das geht besser. Trotzdem verwenden wir den PIT in StuBS an anderen Stellen, da er einfach zu programmieren ist, zum Beispiel beim Systemstart um gewisse Zeiten abzuwarten.
Als nächster Zeitgeber wurde ab 1984 die Real Time Clock (RTC) eingebaut, bei welcher man sich dann durchgerungen hat einen extra Schwingquarz einzubauen – und zwar einen der auch viel Verwendung im Alltag, beispielsweise in Armbanduhren, findet. Dieser hat eine Frequenz von 32 768 Hz, was auch für uns eine wunderschöne Zahl zum Rechnen ist. Er ist auch in der Lage Unterbrechungen auszulösen, Standardeinstellung ist etwa Millisekundentakt, kann aber grob angepasst werden. Frühers war er mit der ersten Leitung des zweiten, kaskadierten *PIC*s verbunden – was den Nachteil hat, dass zum Beispiel die Tastaturinterrupts eine höhere Priorität hatten. Aber der primäre Einsatzzweck ist auch ein anderer: Man kann die aktuelle Zeit und Datum auslesen, eine kleine Knopfzelle auf dem Mainboard sorgt dafür, dass er auch ohne Netzspannung die Zeit beibehält. Wer seinem StuBS gerne die aktuelle Zeit beibringen will, hat in dieser Aufgabe die Gelegenheit dazu.
Seit dem Pentium gibt es den Timestamp Counter (TSC), einen 64 bit großen Zähler, der mit einer einzelnen Instruktion ausgelesen werden kann – auch im Usermode. Mit welcher Frequenz läuft er? Nun, das kommt auf euer Hardware an: Anfangs hat er mit dem Systemtakt hochgezählt, je nach aktuellem Stromsparmodus, dem P-State, entsprechend unterschiedlich schnell. Neuere Versionen laufen jedoch mit einer konstanten Rate. Für Benchmarkzwecke ist der TSC hervorragend geeignet, sofern man ein paar Eigenheiten wie die Out-Of-Order-Execution korrekt berücksichtigt. Spätestens in Betriebssystemtechnik (BST) werden wir den TSC brauchen. Für das Scheduling ist er jedoch nicht geeignet – er kann keine Unterbrechungen auslösen.
ACPI hat auch einen Timer, welcher mit der NTSC Frequenz taktet und zum Benchmarken verwendet werden kann – Beispielsweise, wenn man nur den nicht konstanten TSC zur Verfügung hat. Für uns ist er aber uninteressant, da er ebenfalls keine Unterbrechungen auslösen kann.
Intel hat nach 25 Jahre beschlossen, dass ein zeitgemäßer Timer kommen muss, und mit Microsoft den High Precision Event Timer (HPET) veröffentlicht. Er hat eine hohe Frequenz und somit hohe Genauigkeit, der Zähler ist mit 64 bit sehr groß und Unterbrechungen können damit auch ausgelöst werden. Eigentlich passt es für unseren Einsatzzweck, aber zur Ansteuerung muss er über die ACPI Tabellen gefunden werden, es geht da noch einen Tick schöner.
Der LAPIC einer jeden CPU hat auch einen eigenen Timer, mit einer sehr hohen Genauigkeit, da er das Taktsignal des APIC-Bus verwendet. Die Frequenz ist somit hardwareabhängig, aber im Betrieb konstant, also unabhängig vom aktuellen P-State. Unterbrechungen sind möglich, konfigurierbar durch Zielwert des Zählers und Vorteiler, allerdings werden diese nur an die jeweilige CPU zu der der LAPIC gehört gesendet. Dieser Timer erfüllt alle unsere Voraussetzungen, und da wir eh schon den LAPIC verwenden, brauchen wir auch gar nicht so viel neuen Code.
Schauen wir uns die Funktionsweise genauer an, konkret im Beispiel bei CPU 1: Dessen Local APIC hat also einen Timer, welcher auf der Frequenz des APIC-Bus basiert, bzw einem Bruchteil davon. Dies wird über den Vorteiler geregelt, welcher den Takt dann je nach Konfiguration 1 : 1, halbiert, geviertelt, bis hin zu einem 128tel der ursprünglichen Frequenz an den Timer weitergibt. Im Timer ist nun ein Zählerregister verbaut, welches mit jedem Takt dekrementiert und beim Erreichen von 0 eine Unterbrechung an der CPU auslöst. Den Vorteiler konfigurieren wir über das Divide Configuration Register, welches im Speicher knapp unter der 4 Gigabyte Grenze eingeblendet ist, jeweils für den LAPIC der entsprechenden CPU.
Bedeutet: CPU 1 kann über diese Adresse nur den eigenen Vorteiler einstellen, unabhängig von CPU 2, welche wiederum über die gleiche Adresse einen anderen Vorteil wählen kann, wie auch im Beispiel angedeutet. Gleiches gilt für die anderen Register: Das Timer Control Register dient – entsprechend seines Namens – der Konfiguration des Zeitgebers. Neben dem auszulösenden Interruptvektor und ob er überhaupt aktiv ist, lässt sich noch der Betriebsmodus einstellen: Entweder zählt er bis 0 und stoppt, oder setzt danach Zählerregister entsprechend dem eingestellten Startwert und beginnt erneut das herabzählen. Diesen Startwert konfiguriert man über das Initial Count Register, während mit den Current Count Register der aktuelle Zählerwert ausgelesen werden kann.