
Guten Tag, lieber Leser, höchstwahrscheinlich haben Sie in meinem vorherigen Artikel gesehen, dass Sie selbst in relativ kurzer Zeit ein funktionsfähiges Betriebssystem schreiben können. Nun, heute werden wir über die Implementierung von Multitasking in meinem Betriebssystem sprechen.
Nun, Sie können sich 2018 wahrscheinlich kein Single-Tasking-Betriebssystem vorstellen, deshalb habe ich beschlossen, über die Implementierung von Multitasking in meinem Betriebssystem zu sprechen. Und als erstes müssen Sie sich für die Art des Multitasking entscheiden. Ich habe mich für präventiv entschieden.
Wie ist sie Preemptive Multitasking ist ein System zur Verteilung der Prozessor-Rechenleistung auf Prozesse: Jeder hat seine eigene Zeitmenge, jeder hat seine eigene Priorität. Und das erste Problem ist, welches Quantum in der Länge gewählt werden soll, wie der Prozess nach Ablauf des Quantums am Laufen gehalten werden kann. In der Tat ist alles einfacher als je zuvor! Wir werden PIT mit der ursprünglich eingestellten Frequenz von 10026 mit einem Cent Unterbrechungen pro Sekunde verwenden. Genau dort lösen wir ein weiteres Problem: Wir stoppen bereits den vorherigen Prozess. Beginnen wir also mit PIT.
Grube
PIT - Programmable Interval Timer - ein Zähler, der bei Erreichen einer programmierten Anzahl von Inkrementen ein Signal gibt. Mit diesem Timer können Sie auch einen Quietscher im Computer quietschen (das, was nach dem Bestehen des Gerätetests quietscht). Und so zählt er mit einer Frequenz von 1193182 Hertz, was bedeutet, dass wir es mit 119 programmieren müssen (1193182/119 ist ungefähr gleich 10026). Senden Sie dazu 2 Bytes an den Port des ersten Generators, zuerst das Low-Byte und dann das High:
unsigned short hz = 119; outportb(0x43, 0x34); outportb(0x40, (unsigned char)hz & 0xFF);
Jetzt lohnt es sich, die Programmierung des Interrupts von PIT aus zu starten. Er hat einen IRQ von 0 und nach der Neuzuordnung von PIC beträgt er 0 x 20 m. Für den IRQ des ersten PIC habe ich dieses Makro geschrieben:
Struktur und Prozesse
Wie Sie verstehen, müssen wir für jeden Prozess eine Struktur sowie eine Struktur entwickeln, die es mir ermöglicht, mich an alle meine Speicherzuordnungen zu erinnern.
Folgendes habe ich:
typedef struct _pralloc { void * addr; struct _pralloc * next; } processAlloc; typedef struct { void * entry; processAlloc *allocs; } ELF_Process; typedef struct __attribute__((packed)) _E { unsigned int eax;
Zunächst müssen wir Folgendes verstehen: Wir können irgendwo an der globalen Adresse, z. B. bei 0xDEAD, die Nummer des aktuell ausgeführten Prozesses eingeben. Wenn wir dann einen Code ausführen, können wir sicher sein: Wir haben die Nummer des aktuell ausgeführten Prozesses. Dies bedeutet, dass Wenn wir auf malloc zugreifen, wissen wir, wem wir Speicher zuweisen, und wir können sofort die Adresse des zugewiesenen Speichers zur Liste der Zuordnungen hinzufügen.
void addProcessAlloc(ELF_Process * p, void * addr) { void * z = p->allocs; p->allocs = malloc_wo_adding_to_process(sizeof(processAlloc)); p->allocs->addr = addr; p->allocs->next = z; }
Nun, wir haben die Struktur der Tabelle mit der Beschreibung der Prozesse geschrieben. Wie geht es weiter, wie werden Aufgaben gewechselt?
Zunächst möchte ich darauf hinweisen, dass beispielsweise im Handler lokale Variablen auf dem Stapel gespeichert sind, was bedeutet, dass der Compiler uns nach dem Aufrufen des Handlers besonders verwöhnt. Um dies zu verhindern, erstellen Sie eine Variable mit einer absoluten Adresse. Bevor Sie den Handler aufrufen, wird das ESP dort abgelegt. Im Handler müssen wir die EOI an den ersten PIC senden und den Prozess finden, zu dem wir wechseln müssen (ich werde den Prioritätsmechanismus nicht beschreiben: Es ist einfach, wie ein Stau). Als nächstes müssen wir alle Register und Flags des aktuellen Prozesses speichern. Kurz bevor wir den ESP in eine Variable einfügen, speichern wir alle Register (einschließlich der Segmentregister) auf dem Stapel. Im Handler selbst müssen wir sie sehr sorgfältig vom Stapel entfernen und gleichzeitig die Flags und die Rücksprungadresse beibehalten. Ich möchte darauf hinweisen, dass der Stapel größer wird (d. H. ESP nimmt ab), was bedeutet, dass das letzte Register, das Sie auf dem Stapel gespeichert haben, bei ESP ist, das vorletzte ist ESP +4 usw.:

Jetzt bleibt es uns überlassen, die Werte der Prozessregister in die Register zu setzen, auf die wir umgeschaltet haben, und IRET auszuführen. Gewinn!
Prozessstart
Wenn wir den Prozess starten, reicht es aus, den Stapel für den Prozess zuzuweisen und dann argc und argv darin abzulegen, die Adresse der Funktion, die nach Abschluss des Prozesses gesteuert wird. Sie müssen auch die Prozessor-Flags auf den Wert setzen, den Sie benötigen. Für mein Betriebssystem ist es beispielsweise 0x216. Sie können das Flag-Register auf Wikipedia lesen.
Am Ende möchte ich Ihnen viel Erfolg wünschen, bald werde ich über die Arbeit mit dem Gedächtnis und andere Artikel schreiben, die Sie interessieren.
Viel Glück und ethisches Hacken!