Betriebssysteme: Drei einfache Teile. Teil 2: Abstraktion: Prozess (Übersetzung)

Einführung in Betriebssysteme

Hallo, Habr! Ich möchte Ihnen eine Serie von Übersetzungen einer interessanten Literatur — OSTEP — vorstellen. In diesem Material wird die Funktionsweise von Unix-ähnlichen Betriebssystemen ziemlich gründlich behandelt, insbesondere die Arbeit mit Prozessen, verschiedenen Planern, Speicher und anderen ähnlichen Komponenten, die ein modernes Betriebssystem ausmachen. Das Original aller Materialien können Sie hier ansehen hier. Bitte beachten Sie, dass die Übersetzung unprofessionell (relativ frei) durchgeführt wurde, aber ich hoffe, dass ich den allgemeinen Sinn bewahrt habe.

Die Laborarbeiten zu diesem Thema finden Sie hier:

Andere Teile:

Und Sie können auch meinen Kanal auf Telegramm =)

Schauen wir uns die grundlegendste Abstraktion an, die das Betriebssystem den Benutzern bietet: den Prozess. Die Definition eines Prozesses ist recht einfach — es ist eine ausgeführte Programm. Ein Programm an sich ist eine lebloses Ding, das sich auf der Festplatte befindet — es ist ein Satz von Anweisungen und möglicherweise einigen statischen Daten, die darauf warten, gestartet zu werden. Es ist das Betriebssystem, das diese Bytes nimmt und sie ausführt, wobei es das Programm in etwas Nützliches verwandelt.
Häufig möchten Benutzer mehr als ein Programm gleichzeitig ausführen, beispielsweise können Sie auf Ihrem Laptop einen Browser, ein Spiel, einen Media Player, einen Texteditor und Ähnliches starten. Tatsächlich kann ein typisches System Dutzende und Hunderte von Prozessen gleichzeitig ausführen. Diese Tatsache macht das System benutzerfreundlicher, man muss sich nie sorgen, ob die CPU frei ist, man startet einfach Programme.

Hieraus ergibt sich das Problem: Wie schafft man die Illusion vieler CPUs? Wie kann das Betriebssystem die Illusion praktischerweise unzähliger CPUs erzeugen, auch wenn man nur einen physischen CPU hat?

Das Betriebssystem erzeugt diese Illusion durch die Virtualisierung der CPU. Indem es einen Prozess startet, ihn dann stoppt, einen anderen Prozess startet und so weiter, kann das Betriebssystem die Illusion aufrechterhalten, dass es viele virtuelle CPUs gibt, obwohl es sich tatsächlich um einen oder mehrere physische Prozessoren handelt. Diese Technik wird genannt CPU-Ressourcenteilung nach Zeit. Diese Technik erlaubt es Benutzern, 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.
Für die Umsetzung der CPU-Virtualisierung, insbesondere um dies gut zu tun, benötigt das Betriebssystem sowohl niedrige als auch hohe Unterstützung. Niedriglevelunterstützung – das sind niedriglevel Methoden oder Protokolle, die den erforderlichen Funktionsbereich implementieren. Ein Beispiel für eine solche Funktionalität ist das Kontextwechseln, das dem Betriebssystem ermöglicht, ein Programm anzuhalten und ein anderes auf der CPU zu starten. Diese zeitliche Teilung ist in allen modernen Betriebssystemen implementiert.
Oben auf diesen Mechanismen liegt eine gewisse Logik, die im Betriebssystem in Form von „Politiken“ angelegt ist. Politik – das ist ein Algorithmus zur Entscheidungsfindung des Betriebssystems. Solche Politiken entscheiden beispielsweise, welches Programm (aus der Befehlsliste) zuerst gestartet werden soll. So wird beispielsweise diese Aufgabe durch eine Politik gelöst, die als Planungspolitik (scheduling policy) bezeichnet wird und bei der Auswahl der Lösung auf Daten wie folgt zurückgreift: Laufhistorie (welches Programm am längsten in der letzten Minute ausgeführt wurde), welche Last dieser Prozess verursacht (welche Programmarten ausgeführt wurden), Leistungsmetriken (ob das System für interaktive Interaktion oder für Durchsatz optimiert ist) usw.

Abstraktion: Prozess

Die Abstraktion eines laufenden Programms, das vom Betriebssystem ausgeführt wird, ist das, was wir Prozessnennen. Wie bereits früher erwähnt, ist ein Prozess einfach ein laufendes Programm zu jedem Zeitpunkt. Ein Programm, mithilfe dessen wir zusammenfassende Informationen von verschiedenen Systemressourcen abrufen können und auf die diese Programme während ihrer Ausführung zugreifen oder die sie betreffen.
Um die Bestandteile eines Prozesses zu verstehen, muss man die Zustände des Systems verstehen: was das Programm während seiner Ausführung lesen oder ändern kann. Zu jedem Zeitpunkt muss man verstehen, welche Elemente des Systems für die Ausführung des Programms wichtig sind.
Eines der offensichtlichen Elemente des Systemzustands, das den Prozess umfasst, ist der Speicher. Die Anweisungen befinden sich im Speicher. Die Daten, die das Programm liest oder schreibt, werden ebenfalls im Speicher gespeichert. Daher ist der Speicher, den der Prozess adressieren kann (der sogenannte Adressraum), Teil des Prozesses.
Auch die Register sind Teil des Systemzustands. Viele Anweisungen zielen darauf ab, den Wert von Registern zu ändern oder ihren Wert zu lesen, und somit werden die Register ebenfalls zu einem wichtigen Teil der Arbeitsweise des Prozesses.
Es ist zu beachten, dass der Maschinenzustand auch aus bestimmten speziellen Registern besteht. Zum Beispiel IP — der Befehlszeiger — ist der Zeiger auf den Befehl, den das Programm gerade ausführt. Es gibt auch einen Stackzeiger und den dazugehörigen Rahmenzeiger, die zur Verwaltung von Funktionen, lokalen Variablen und Rücksprungadressen verwendet werden.
Schließlich greifen Programme häufig auf den ROM (nichtflüchtiger Speicher) zu. Diese Informationen über "I/O" (Eingabe/Ausgabe) sollten eine Liste von Dateien enthalten, die der Prozess gerade geöffnet hat.

Process API

Um das Verständnis der Arbeitsweise eines Prozesses zu verbessern, betrachten wir Beispiele für Systemaufrufe, die in jedes Betriebssystem-Interface integriert werden sollten. Diese APIs sind in irgendeiner Form auf jedem OS 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.
Löschen: Da es eine Schnittstelle zur Erstellung von Prozessen gibt, muss das Betriebssystem auch die Möglichkeit bieten, Prozesse gewaltsam zu löschen. Die meisten Programme werden sich natürlich selbst starten und beenden, während sie ausgeführt werden. Andernfalls möchte der Benutzer in der Lage sein, sie zu beenden, und somit wäre eine Schnittstelle zum Stoppen von Prozessen nicht überflüssig.
Warten (Warten): Manchmal ist es nützlich, auf die Beendigung eines Prozesses zu warten, daher werden einige Schnittstellen bereitgestellt, die die Möglichkeit des Wartens bieten.
Sonstige Steuerung (verschiedene Steuerung): Neben dem Töten und Warten auf den Prozess gibt es noch andere verschiedene Kontrollmethoden. Zum Beispiel bieten die meisten Betriebssysteme die Möglichkeit, einen Prozess einzufrieren (seine Ausführung für eine gewisse Zeit zu stoppen) und anschließend fortzusetzen (die Ausführung wieder aufzunehmen).
Status (zustand): Es gibt verschiedene Schnittstellen, um Informationen über den Status des Prozesses zu erhalten, wie die Dauer seiner Ausführung oder in welchem Zustand er sich gerade befindet.

Betriebssysteme: Drei einfache Teile. Teil 2: Abstraktion: Prozess (Übersetzung)

Prozess erstellen: Details

Eine der interessanten Dinge ist, wie Programme genau in Prozesse umgewandelt werden. Insbesondere, wie das Betriebssystem ein Programm lädt und startet. Wie genau ein Prozess erstellt wird.
Zunächst muss das Betriebssystem den Programmcode und die statischen Daten in den Arbeitsspeicher (in den Adressraum des Prozesses) laden. Programme sind normalerweise auf einer Festplatte oder einem SSD in einem bestimmten ausführbaren Format gespeichert. Daher erfordert der Prozess des Ladens des Programmcodes und der statischen Daten in den Speicher vom Betriebssystem die Fähigkeit, diese Bytes von der Festplatte zu lesen und sie irgendwo im Speicher zu platzieren.

In frühen Betriebssystemen wurde der Ladeprozess ungeduldig (eagerly) durchgeführt, was bedeutet, dass der Code vollständig in den Speicher geladen wurde, bevor das Programm gestartet wurde. Moderne Betriebssysteme tun dies faul (lazily), indem sie Teile des Codes oder der Daten nur dann laden, wenn sie während der Programmausführung benötigt werden.

Nachdem der Code und die statischen Daten in den Speicher geladen sind, muss das Betriebssystem noch einige Dinge erledigen, bevor der Prozess gestartet wird. Ein gewisser Teil des Speichers muss für den Stack reserviert werden. Programme verwenden den Stack für lokale Variablen, Funktionparameter und Rücksprungadressen.. Das Betriebssystem weist diesen Speicher zu und gibt ihn dem Prozess. Der Stack kann auch mit bestimmten Argumenten reserviert werden, speziell füllt er die Parameter der Funktion main(), beispielsweise mit dem Array argc und argv.

Das Betriebssystem kann auch einen bestimmten Teil des Speichers für den Heap des Programms reservieren. Der Heap wird von Programmen für ausdrücklich angeforderte dynamisch zugewiesene Daten verwendet.. Programme beantragen diesen Speicher mit dem Aufruf der Funktion malloc() und geben ihn ausdrücklich frei, indem sie die Funktion free(). Der Heap ist für Datenstrukturen wie: verkettete Listen, Hash-Tabellen, Bäume und andere erforderlich. Zunächst wird ein kleines Stück Speicher für den Heap reserviert, aber im Laufe der Programmausführung kann der Heap über die bibliotheksseitige API-Funktion malloc() mehr Speicher anfordern. Das Betriebssystem ist in den Prozess der Zuweisung zusätzlichen Speichers eingebunden, um diesen Anforderungen nachzukommen.

Das Betriebssystem wird auch Initialisierungsaufgaben ausführen, insbesondere solche, die mit Ein- und Ausgabe zu tun haben. In UNIX-Systemen hat jeder Prozess standardmäßig 3 geöffnete Dateideskriptoren für den Standard-Eingangs-, Ausgangs- und Fehlerstrom. Diese Deskriptoren ermöglichen es Programmen, Eingaben vom Terminal zu lesen und Informationen auf dem Bildschirm auszugeben.

So bereitet das Betriebssystem den Raum für die Ausführung eines Prozesses vor, indem es Code und statische Daten in den Speicher lädt, einen Stapel erstellt und initialisiert sowie andere Aufgaben im Zusammenhang mit der Durchführung von Ein- und Ausgaben ausführt. Schließlich bleibt die letzte Aufgabe: das Programm über seinen Einstiegspunkt, die Funktion main(), zur Ausführung zu bringen. Wenn die Ausführung der Funktion main() übergeben wird, übergibt das Betriebssystem die Kontrolle an die neu erstellte Prozessinstanz, wodurch das Programm ausgeführt wird.

Zustand des Prozesses

Jetzt, da wir ein gewisses Verständnis davon haben, was ein Prozess ist und wie er erstellt wird, lassen Sie uns die Zustände des Prozesses auflisten, in denen er sich befinden kann. In seiner einfachsten Form kann sich ein Prozess in einem dieser Zustände befinden:
Laufend. Im laufenden Zustand wird der Prozess vom Prozessor ausgeführt. Das bedeutet, dass Anweisungen ausgeführt werden.
Bereit. Im bereiten Zustand ist der Prozess bereit zur Ausführung, aber aus bestimmten Gründen wird er im gegebenen Moment nicht vom Betriebssystem ausgeführt.
Blockiert. Im blockierten Zustand führt der Prozess bestimmte Operationen aus, die es ihm nicht ermöglichen, bereit zur Ausführung zu sein, bis ein bestimmtes Ereignis eintritt. Ein häufiges Beispiel ist, wenn der Prozess eine IO-Operation initialisiert, wird er blockiert, sodass ein anderer Prozess den Prozessor nutzen kann.

Betriebssysteme: Drei einfache Teile. Teil 2: Abstraktion: Prozess (Übersetzung)

Man kann sich diese Zustände als Graf 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 entfernt. In dem Moment, in dem ein Prozess BLOCKED wird, zum Beispiel wenn eine IO-Operation initiiert wird, wird das Betriebssystem ihn in diesem Zustand halten, bis ein Ereignis eintritt, beispielsweise das Ende der IO. In diesem Moment wechselt der Prozess zurück in den Zustand READY und möglicherweise sofort in den Zustand RUNNING, wenn das das Betriebssystem entscheidet.
Lassen Sie uns ein Beispiel anschauen, wie zwei Prozesse durch diese Zustände gehen. Zunächst nehmen wir an, dass beide Prozesse gestartet sind und jeder nur die CPU nutzt. In diesem Fall würden ihre Zustände wie folgt aussehen.

Betriebssysteme: Drei einfache Teile. Teil 2: Abstraktion: Prozess (Übersetzung)

Im nächsten Beispiel fordert der erste Prozess nach einer gewissen Zeit der Ausführung IO an und wechselt in den Zustand BLOCKED, wodurch dem anderen Prozess die Möglichkeit gegeben wird, zu starten (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 das IO abgeschlossen und der Status von Prozess 0 ändert sich in READY. Schließlich ist Prozess 1 abgeschlossen, und nach dessen Abschluss wird Prozess 0 gestartet, ausgeführt und endet seine Arbeit.

Betriebssysteme: Drei einfache Teile. Teil 2: Abstraktion: Prozess (Übersetzung)

Datenstruktur

Das Betriebssystem selbst ist ein Programm und hat wie jedes andere Programm einige wichtige Datenstrukturen, die verschiedene relevante Informationen verfolgen. Um den Zustand jedes Prozesses zu verfolgen, wird das Betriebssystem eine gewisse Prozessliste für alle Prozesse im Zustand READY und einige zusätzliche Informationen zur Verfolgung von Prozessen, die gerade laufen, führen. Zudem muss das Betriebssystem auch blockierte Prozesse überwachen. Nach Abschluss der IO ist das Betriebssystem verpflichtet, den entsprechenden Prozess zu wecken und ihn in den Zustand der Bereitschaft zu versetzen.

So muss das Betriebssystem beispielsweise den Zustand der CPU-Register speichern. Im Moment der Beendigung eines Prozesses wird der Zustand der Register im Adressraum des Prozesses gespeichert und beim Fortsetzen seiner Ausführung – die Werte der Register wiederhergestellt, um so die Ausführung dieses Prozesses fortzusetzen.

Neben den Zuständen ready, blocked und running gibt es noch einige andere Zustände. Manchmal kann ein Prozess im Moment seiner Erstellung den Zustand INIT haben. Schließlich kann ein Prozess in den Zustand FINAL versetzt werden, wenn er bereits beendet ist, aber die Informationen über ihn noch nicht bereinigt wurden. In UNIX-Systemen wird dieser Zustand genannt Zombie-Prozess. Dieser Zustand ist nützlich, wenn der Elternprozess den Rückgabecode des Kindes erfahren möchte, zum Beispiel signalisiert in der Regel 0 einen erfolgreichen Abschluss und 1 einen Fehler, jedoch können Programmierer zusätzliche Rückgabecodes erstellen, um verschiedene Probleme anzuzeigen. Beim Abschluss führt der Elternprozess den letzten Systemaufruf, wie wait(), aus, um auf die Beendigung des Kinderprozesses zu warten und das Betriebssystem darüber zu informieren, dass alle mit dem abgeschlossenen Prozess verbundenen Daten bereinigt werden können.

Betriebssysteme: Drei einfache Teile. Teil 2: Abstraktion: Prozess (Übersetzung)

Schlüsselpunkte der Vorlesung:

Prozess — die Hauptabstraktion eines laufenden Programms im Betriebssystem. Zu jedem Zeitpunkt kann ein Prozess durch seinen Zustand beschrieben werden: der Inhalt des Speichers in seinem Adressraum, der Inhalt der Prozessorregister, einschließlich des Instruction Pointers und des Stack Pointers, sowie Informationen über IO, wie beispielsweise geöffnete Dateien, die gelesen oder geschrieben werden.
Process 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 der vielen Zustände, einschließlich running, ready, blocked. Verschiedene Ereignisse, wie z.B. das Scheduling, die Ausnahme vom Scheduling oder das Warten, können den Zustand eines Prozesses von einem in einen anderen überführen.
Prozessliste enthält Informationen über alle Prozesse im System. Jeder Eintrag darin wird als Prozesskontrollblock bezeichnet, der tatsächlich eine Struktur darstellt, die alle notwendigen Informationen über einen bestimmten Prozess enthält. 

Quelle: habr.com

Zuverlässiges Hosting für Websites mit DDoS-Schutz kaufen, VPS VDS Server 🔥 Zuverlässiges Hosting für Websites mit DDoS-Schutz kaufen, VPS VDS Server - ProHoster