Einführung in Betriebssysteme
Hallo, Habr! Ich möchte euch eine Serie von Übersetzungen einer interessanten Literatur vorstellen — OSTEP. In diesem Material wird die Funktionsweise von unixähnlichen Betriebssystemen recht tiefgehend behandelt, insbesondere die Arbeit mit Prozessen, verschiedenen Planern, Speicher und anderen ähnlichen Komponenten, die ein modernes Betriebssystem ausmachen. Das Original aller Materialien könnt ihr hier ansehen . Bitte beachtet, dass die Übersetzung nicht professionell (recht frei) durchgeführt wurde, aber ich hoffe, dass ich die allgemeine Bedeutung erhalten habe.
Die Laborarbeiten zu diesem Thema findet ihr hier:
- Original:
- Original:
- Meine persönliche Anpassung:
Weitere Teile:
Ihr könnt auch mal auf meinen Kanal in =)
Lassen Sie uns die grundlegendste Abstraktion betrachten, die ein Betriebssystem den Benutzern bietet: den Prozess. Die Definition eines Prozesses ist recht einfach – es handelt sich um ein aktives Programm. Ein Programm ist für sich genommen eine leblose Entität, die sich auf der Festplatte befindet – es ist eine Reihe von Anweisungen und möglicherweise einige statische Daten, die auf den Moment der Ausführung warten. Das Betriebssystem nimmt diese Bytes und führt sie aus, verwandelt das Programm in etwas Nützliches.
Häufig möchten Benutzer mehrere Programme gleichzeitig ausführen; zum Beispiel können Sie auf Ihrem Laptop einen Browser, ein Spiel, einen Media Player, einen Texteditor usw. starten. Tatsächlich kann ein typisches System Dutzende oder Hunderte von Prozessen gleichzeitig ausführen. Diese Tatsache macht das System benutzerfreundlicher; Sie müssen sich nie fragen, ob die CPU frei ist, sondern können einfach die Programme starten.
Hieraus ergibt sich das Problem: Wie schafft man die Illusion von mehreren CPUs? Wie kann das Betriebssystem die Illusion von praktisch unbegrenzten CPUs erzeugen, selbst wenn Sie nur eine physische CPU haben?
Das Betriebssystem schafft diese Illusion durch die Virtualisierung der CPU. Indem es einen Prozess startet, dann stoppt, einen anderen Prozess startet und so weiter, kann das Betriebssystem den Anschein erwecken, dass es mehrere virtuelle CPUs gibt, obwohl tatsächlich nur ein oder mehrere physische Prozessoren vorhanden sind. Diese Technik wird genannt zeitliche CPU-Ressourcenteilung. Diese Technik ermöglicht es den Nutzern, so viele gleichzeitige Prozesse auszuführen, wie sie möchten. Der Preis für diese Lösung ist die Leistung – da, wenn die CPU von mehreren Prozessen geteilt wird, jeder Prozess langsamer bearbeitet wird.
Um die CPU-Virtualisierung zu verwirklichen, insbesondere um dies gut zu machen, benötigt das Betriebssystem sowohl eine niedrig- als auch hochrangige Unterstützung. Die niedrig-rangige Unterstützung wird genannt Mechanismen — das sind Low-Level-Methoden oder Protokolle, die den benötigten Teil der Funktionalität umsetzen. Ein Beispiel für eine solche Funktionalität ist das Kontexthandling, das dem Betriebssystem ermöglicht, ein Programm zu stoppen und ein anderes Programm auf dem Prozessor zu starten. Diese zeitliche Trennung wird in allen modernen Betriebssystemen realisiert.
Auf diesen Mechanismen basiert eine gewisse Logik, die im Betriebssystem in Form von "Richtlinien" implementiert ist. Richtlinie — das ist ein Algorithmus zur Entscheidungsfindung im Betriebssystem. Solche Richtlinien bestimmen beispielsweise, welches Programm (aus einer Befehlsliste) zuerst gestartet werden soll. Diese Aufgabe wird von der sogenannten Planungsrichtlinie (scheduling policy) gelöst, die bei der Entscheidungsfindung auf Daten wie die Laufhistorie (welches Programm in der letzten Minute am längsten lief), die Arbeitslast dieses Prozesses (welche Programmtypen gestartet wurden), Leistungskennzahlen (ob das System für interaktive Interaktion oder für Durchsatz optimiert ist) und so weiter zurückgreift.
Abstraktion: Prozess
Die Abstraktion eines laufenden Programms, das vom Betriebssystem ausgeführt wird, ist das, was wir nennen ein Prozess.. Wie bereits erwähnt, ist ein Prozess einfach ein laufendes Programm zu jedem gegebenen Zeitpunkt. Es handelt sich um das Programm, mit dem wir aggregierte Informationen aus verschiedenen Ressourcen des Systems erhalten können, auf die das Programm während seiner Ausführung zugreift oder die es beeinflusst.
Um die Bestandteile eines Prozesses zu verstehen, muss man die Zustände des Systems verstehen: Welche Informationen kann das Programm während seiner Ausführung lesen oder ändern? Zu jeder Zeit muss man verstehen, welche Systemelemente für die Ausführung des Programms wichtig sind.
Eines der offensichtlichen Elemente des Systemzustands, die durch den Prozess eingeschlossen sind, ist 2 GB SO-DIMM DDR4 (erweiterbar auf 8 GB). Die Anweisungen werden im Speicher abgelegt. Die Daten, die das Programm liest oder schreibt, befinden sich ebenfalls im Speicher. Somit ist der Speicher, den der Prozess adressieren kann (der sogenannte Adressraum), ein Teil des Prozesses.
Ein weiterer wesentlicher Bestandteil des Systemstatus sind die Register. Viele Befehle sind darauf ausgerichtet, den Inhalt von Registern zu verändern oder ihren aktuellen Wert auszulesen, was sie zu einem wichtigen Element der Prozessverarbeitung macht.
Es ist wichtig zu erwähnen, dass der Maschinenstatus auch von speziellen Registern geprägt wird. Beispielsweise, IP — der Befehlszeiger — der auf den aktuellen Befehl verweist, den das Programm gerade ausführt. Zudem gibt es Stack-Zeiger und den damit verbundenen Frame-Zeiger, die zur Verwaltung von: Funktionsparametern, lokalen Variablen und Rücksprungadressen verwendet werden.
Schließlich greifen Programme häufig auf den ROM (Nichtflüchtigen Speicher) zu. Solche Informationen über „I/O“ (Ein- und Ausgabe) sollten eine Liste von Dateien enthalten, die der Prozess gerade geöffnet hat.
Prozess-API
Um das Verständnis der Funktionsweise eines Prozesses zu vertiefen, betrachten wir Beispiele für Systemaufrufe, die in jede Benutzeroberfläche eines Betriebssystems integriert sein müssen. Diese APIs sind in irgendeiner Form auf jedem Betriebssystem verfügbar.
● Erstellen (Erstellung): Das Betriebssystem muss eine Methode zur Verfügung stellen, um neue Prozesse zu erstellen. Wenn Sie einen Befehl im Terminal eingeben oder eine Anwendung durch Doppelklick auf das Symbol starten, wird eine Anfrage an das Betriebssystem gesendet, um einen neuen Prozess zu erstellen und das angegebene Programm zu starten.
● Entfernen: Wenn es eine Schnittstelle zur Erstellung eines Prozesses gibt, sollte das Betriebssystem auch die Möglichkeit bieten, einen Prozess zwangsweise zu beenden. Die meisten Programme werden in der Regel von selbst gestartet und beendet, während sie ausgeführt werden. Andernfalls möchte der Benutzer sie möglicherweise beenden, weshalb eine Schnittstelle zum Stoppen von Prozessen nützlich wäre.
● Warten (Warten): Manchmal ist es sinnvoll, auf die Beendigung eines Prozesses zu warten, daher werden einige Schnittstellen bereitgestellt, die diese Möglichkeit bieten.
● Sonstiges Steuerung (Sonstiges Management): Neben dem Beenden und Warten auf Prozesse gibt es noch andere verschiedene Kontrollmethoden. Beispielsweise bieten die meisten Betriebssysteme die Möglichkeit, einen Prozess einzufrieren (seine Ausführung für einen bestimmten Zeitraum anzuhalten) und ihn dann wieder fortzusetzen (die Ausführung fortzusetzen).
● Status (Status): Es gibt verschiedene Schnittstellen, um Informationen über den Status eines Prozesses zu erhalten, wie z. B. die Dauer seiner Ausführung oder in welchem Zustand er sich derzeit befindet.

Prozess erstellen: Details
Eine der interessanten Fragen ist, wie Programme in Prozesse umgewandelt werden. Besonders spannend ist, wie das Betriebssystem eine Anwendung startet und ausführt. Wie wird ein Prozess konkret erstellt?
Zunächst muss das Betriebssystem den Programmkode und die statischen Daten in den Speicher (in den Adressraum des Prozesses) laden. Programme befinden sich normalerweise auf einer Festplatte oder einem Solid-State-Laufwerk in einem ausführbaren Format. Somit erfordert der Ladevorgang des Programms und der statischen Daten in den Speicher, dass das Betriebssystem die Bytes von der Festplatte lesen und sie irgendwo im Speicher ablegen kann.
In frühen Betriebssystemen wurde der Ladevorgang ungeduldig (eagerly) durchgeführt, was bedeutet, dass der Code vollständig in den Speicher geladen wurde, bevor das Programm ausgeführt wurde. Moderne Betriebssysteme tun dies faul (lazily), indem sie Teile des Codes oder Daten nur dann laden, wenn sie während der Ausführung des Programms benötigt werden.
Nachdem der Code und die statischen Daten in den Arbeitsspeicher des Betriebssystems geladen wurden, sind noch einige Schritte erforderlich, bevor der Prozess gestartet werden kann. Ein gewisser Speicher muss für den Stack reserviert werden. Programme nutzen den Stack für lokale Variablen, Funktionsparameter und Rücksprungadressen.. Das Betriebssystem reserviert diesen Speicher und stellt ihn dem Prozess zur Verfügung. Der Stack kann auch mit bestimmten Argumenten angelegt werden, insbesondere werden die Parameter der Funktion main(), wie das Array argc und argv, gefüllt.
Das Betriebssystem kann auch eine gewisse Menge an Speicher für den Heap (freier Speicher) der Programme reservieren. Der Heap wird von Programmen für ausdrücklich angeforderte dynamisch zugewiesene Daten genutzt.. Programme fordern diesen Speicher an, indem sie eine Funktion aufrufen, malloc() und sie reinigen ausdrücklich, indem sie die Funktion aufrufen, free(). Ein Heap ist für Datenstrukturen wie verkettete Listen, Hash-Tabellen, Bäume und andere erforderlich. Zu Beginn wird eine kleine Menge Speicher für den Heap reserviert, aber im Laufe der Programmausführung kann der Heap mehr Speicher anfordern, indem er den Bibliotheks-API-Aufruf malloc() verwendet. Das Betriebssystem ist in den Prozess der Zuweisung zusätzlichen Speichers integriert, um diese Anforderungen zu erfüllen.
Das Betriebssystem führt auch Initialisierungsaufgaben aus, insbesondere solche, die mit Ein-/Ausgabe zu tun haben. Zum Beispiel hat in UNIX-Systemen jeder Prozess standardmäßig 3 geöffnete Dateideskriptoren für den Standard-Eingabe-, Ausgabe- und Fehlerstrom. Diese Deskriptoren ermöglichen es Programmen, Eingaben vom Terminal zu lesen und Informationen auf den Bildschirm auszugeben.
Durch das Laden von Code und statischen Daten in den Speicher, das Erstellen und Initialisieren des Stacks sowie das Ausführen anderer Aufgaben im Zusammenhang mit der Eingabe- und Ausgabeoperation bereitet das Betriebssystem die Grundlage für die Ausführung des Prozesses vor. Schließlich bleibt die letzte Aufgabe: das Programm über seinen Einstiegspunkt, der als Funktion main() bekannt ist, zur Ausführung zu bringen. Wenn die Ausführung der Funktion main() beginnt, übergibt das Betriebssystem die Kontrolle an die neu erstellte Prozessinstanz, sodass das Programm ausgeführt wird.
Zustand des Prozesses
Jetzt, da wir ein gewisses Verständnis dafür haben, was ein Prozess ist und wie er erstellt wird, lassen Sie uns die Zustände auflisten, in denen sich ein Prozess befinden kann. In der einfachsten Form kann sich der Prozess in einem dieser Zustände befinden:
● Running. Im laufenden Zustand wird der Prozess auf dem Prozessor ausgeführt. Das bedeutet, dass Instruktionen ausgeführt werden.
● Bereit. Im bereiten Zustand ist der Prozess bereit zur Ausführung, wird jedoch aus bestimmten Gründen nicht zum angegebenen Zeitpunkt vom Betriebssystem ausgeführt.
● Blockiert. Im blockierten Zustand führt der Prozess bestimmte Operationen aus, die ihn daran hindern, für die Ausführung bereit zu sein, bis ein bestimmtes Ereignis eintritt. Ein häufiges Beispiel hierfür ist, wenn ein Prozess eine IO-Operation initiiert, wird er blockiert und ermöglicht es so einem anderen Prozess, die CPU zu nutzen.

Man kann sich diese Zustände als Graphen vorstellen. Wie auf dem Bild zu sehen ist, kann der Zustand eines Prozesses zwischen RUNNING und READY nach Ermessen des Betriebssystems wechseln. Wenn der Zustand eines Prozesses von READY nach RUNNING wechselt, bedeutet das, dass der Prozess geplant wurde. Umgekehrt wird er von der Planung genommen. In dem Moment, in dem der Prozess BLOCKED wird, beispielsweise durch die Initialisierung einer IO-Operation, hält das Betriebssystem ihn in diesem Zustand bis zum Eintreten eines bestimmten Ereignisses, wie dem Abschluss der IO. In diesem Moment wechselt er in den Zustand READY und möglicherweise sofort in den Zustand RUNNING, wenn das das Betriebssystem entscheidet.
Lassen Sie uns ein Beispiel betrachten, wie zwei Prozesse durch diese Zustände verlaufen. Zunächst nehmen wir an, dass beide Prozesse ausgeführt werden und jeweils nur die CPU nutzen. In diesem Fall würden ihre Zustände wie folgt aussehen.

Im nächsten Beispiel fordert der erste Prozess nach einer bestimmten Arbeitszeit IO an und wechselt in den Zustand BLOCKED, was dem anderen Prozess die Möglichkeit gibt, zu starten (siehe Abb. 1.4). Das Betriebssystem sieht, dass Prozess 0 die CPU nicht nutzt und startet Prozess 1. Während der Ausführung von Prozess 1 wird IO abgeschlossen und der Status von Prozess 0 wechselt auf READY. Schließlich beendet Prozess 1 seine Ausführung, und nach dessen Abschluss wird Prozess 0 gestartet, ausgeführt und beendet.

Datenstruktur
Das Betriebssystem selbst ist ein Programm und hat, wie jedes andere Programm, bestimmte Schlüssel-Datenstrukturen, die verschiedene relevante Informationen überwachen. Um den Zustand jedes Prozesses im Betriebssystem zu verfolgen, wird ein gewisser Prozessliste Für alle Prozesse im Zustand READY sowie einige zusätzliche Informationen zur Überwachung der derzeit laufenden Prozesse ist das Betriebssystem verantwortlich. Darüber hinaus muss das Betriebssystem auch die blockierten Prozesse überwachen. Nach Abschluss von E/S-Operationen ist das Betriebssystem verpflichtet, den entsprechenden Prozess zu wecken und ihn in den Zustand der Bereitschaft zur Ausführung zu versetzen.
So muss das Betriebssystem beispielsweise den Zustand der Prozessorregister speichern. Beim Anhalten eines Prozesses wird der Zustand der Register im Adressraum des Prozesses gespeichert, und beim Fortsetzen seiner Ausführung werden die Werte der Register wiederhergestellt, sodass der Prozess fortgesetzt werden kann.
Neben den Zuständen ready, blocked und running gibt es noch einige andere Zustände. Manchmal kann ein Prozess beim Erstellen den Zustand INIT haben. Schließlich kann ein Prozess in den Zustand FINAL versetzt werden, wenn er bereits abgeschlossen ist, aber die Informationen über ihn noch nicht gelöscht wurden. In UNIX-Systemen wird dieser Zustand als Zombie-Prozess. Dieser Zustand ist nützlich, wenn der übergeordnete Prozess den Rückgabewert des untergeordneten Prozesses ermitteln möchte. In der Regel signalisiert 0 einen erfolgreichen Abschluss und 1 einen Fehler. Programmierer können jedoch zusätzliche Ausgabecodes verwenden, um auf verschiedene Probleme hinzuweisen. Wenn der übergeordnete Prozess beendet wird, führt er den letzten Systemaufruf, z.B. wait(), aus, um auf den Abschluss des untergeordneten Prozesses zu warten und dem Betriebssystem zu signalisieren, dass die Daten, die mit dem abgeschlossenen Prozess verbunden sind, gelöscht werden können.

Wichtige Punkte der Vorlesung:
● Prozess — die Hauptabstraktion eines laufenden Programms im Betriebssystem. Zu jedem Zeitpunkt kann ein Prozess anhand seines Zustands beschrieben werden: der Inhalt des Speichers in seinem Adressraum, der Inhalt der CPU-Register, einschließlich des Instruction Pointers und des Stack Pointers, sowie Informationen über die Ein- und Ausgabe, wie z.B. geöffnete Dateien, die gelesen oder geschrieben werden.
● Prozess-API besteht aus Aufrufen, die Programme in Bezug auf Prozesse machen können. Normalerweise handelt es sich um Aufrufe zum Erstellen, Löschen oder andere.
● Der Prozess befindet sich in einem von vielen Zuständen, einschließlich running, ready, blocked. Verschiedene Ereignisse wie Planung, Exkludierung aus der Planung oder Warten können den Zustand des Prozesses von einem in einen anderen wechseln.
● Prozessliste enthält Informationen über alle Prozesse im System. Jeder Eintrag darin wird als Process Control Block bezeichnet, der tatsächlich eine Struktur ist, die alle notwendigen Informationen über einen bestimmten Prozess enthält.
Quelle: habr.com
