Einführung in Betriebssysteme
Hallo, Habr! Ich möchte Ihnen eine Serie von Übersetzungen eines interessanten Fachbuchs vorstellen: OSTEP. In diesem Material wird die Funktionsweise von Unix-ähnlichen Betriebssystemen ausführlich behandelt, insbesondere der Umgang mit Prozessen, verschiedenen Scheduling-Algorithmen, Speicher und ähnlichen Komponenten, die moderne Betriebssysteme ausmachen. Sie können das Original aller Materialien hier einsehen: . 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:
Ihr könnt auch mal auf meinen Kanal in =)
Programmablauf
Was passiert, wenn ein Programm läuft? Das gestartete Programm führt eine grundlegende Aufgabe aus – es verarbeitet Anweisungen. Jede Sekunde extrahiert der Prozessor Millionen und möglicherweise Milliarden von Anweisungen aus dem Arbeitsspeicher, dekodiert sie (zum Beispiel, indem er den Typ dieser Anweisungen erkennt) und führt sie aus. Dies kann das Addieren zweier Zahlen, den Zugriff auf den Speicher, die Überprüfung von Bedingungen, das Springen zu einer Funktion und so weiter umfassen. Nach der Ausführung einer Anweisung wechselt der Prozessor zur nächsten. Und so werden Anweisung für Anweisung abgearbeitet, bis das Programm abgeschlossen ist.
Dieses Beispiel ist natürlich vereinfacht dargestellt – tatsächlich ermöglicht moderne Hardware, um die Verarbeitungsgeschwindigkeit des Prozessors zu erhöhen, die Ausführung von Anweisungen außerhalb der Reihenfolge, das Berechnen möglicher Ergebnisse, das gleichzeitige Ausführen von Anweisungen und ähnliche Tricks.
Das von uns beschriebene vereinfachte Arbeitsmodell ähnelt dem von John von Neumann entwickelten Rechenmodell.
Das beschriebene vereinfachte Arbeitsprinzip ist analog zum von Neumann-Modell der Berechnungen. Von Neumann ist einer der Pioniere der Computersysteme und auch einer der Autoren der Spieltheorie.Während die Programme ausgeführt werden, passieren viele andere Ereignisse, viele andere Prozesse und externe Logik sind am Werk, deren Hauptziel die Vereinfachung des Startens, Betriebs und der Wartung des Systems ist.
Es gibt eine Sammlung von Software, die für die einfache Ausführung von Programmen verantwortlich ist (oder sogar das gleichzeitige Ausführen mehrerer Programme ermöglicht). Diese Software erlaubt es Programmen, denselben Speicher zu teilen und mit verschiedenen Geräten zu interagieren. Diese Art von Software nennt man Betriebssystem, und es obliegt ihm, sicherzustellen, dass das System korrekt und effizient arbeitet und eine einfache Verwaltung des Systems gewährleistet ist.
Das Betriebssystem
Das Betriebssystem, kurz OS, ist ein Komplex von miteinander verbundenen Programmen, die dazu dienen, die Ressourcen des Computers zu verwalten und die Interaktion des Benutzers mit dem Computer zu organisieren..
Das OS erreicht seine Effizienz in erster Linie durch die wichtigste Technik – die Technik der Virtualisierung.. Ein Betriebssystem interagiert mit physischen Ressourcen (wie Prozessor, Speicher, Festplatte usw.) und wandelt diese in eine allgemeinere, leistungsfähigere und benutzerfreundlichere Form um. Daher kann man das Betriebssystem grob mit einer virtuellen Maschine vergleichen.
Um den Benutzern zu ermöglichen, Befehle an das Betriebssystem zu geben und so die Funktionen der virtuellen Maschine zu nutzen (wie zum Beispiel: Programme ausführen, Speicher zuweisen, auf Dateien zugreifen usw.), stellt das Betriebssystem ein Interface zur Verfügung, das als API (application programming interface) bekannt ist und zu dem Aufrufe (calls) gemacht werden können. Ein typisches Betriebssystem ermöglicht Hunderte von Systemaufrufen.
Und schließlich, da die Virtualisierung es vielen Programmen ermöglicht, gleichzeitig zu arbeiten (so dass die CPU gemeinsam genutzt wird) und gleichzeitig auf ihre Anweisungen und Daten zuzugreifen (und somit den Speicher zu teilen), sowie auf Festplatten zuzugreifen (was die Nutzung von Eingabe-/Ausgabegeräten ermöglicht), wird das Betriebssystem auch als Ressourcenmanager bezeichnet. Jeder Prozessor, jeder Speicher und jede Festplatte ist eine Systemressource, und so wird eine der Aufgaben des Betriebssystems, diese Ressourcen effektiv und fair oder, je nach Aufgabe, für die diese Software entwickelt wurde, zu verwalten.
CPU-Virtualisierung
Lassen Sie uns das folgende Programm betrachten:
(https://www.youtube.com/watch?v=zDwT5fUcki4&feature=youtu.be)

Es führt keine besonderen Aktionen aus, im Grunde genommen ruft es nur die Funktion spin(), deren Aufgabe es ist, die Zeit zyklisch zu überprüfen und zurückzukehren, nachdem eine Sekunde vergangen ist. So wiederholt es endlos die Zeile, die der Benutzer als Argument übergeben hat.
Wir starten dieses Programm und übergeben als Argument das Zeichen „A“. Das Ergebnis ist eher uninteressant – das System führt einfach das Programm aus, das regelmäßig das Zeichen „A“ auf dem Bildschirm anzeigt.
Jetzt versuchen wir eine Variante, bei der mehrere Instanzen desselben Programms ausgeführt werden, die unterschiedliche Buchstaben ausgeben, um es verständlicher zu machen. In diesem Fall ergibt sich ein etwas anderes Ergebnis. Obwohl wir nur einen Prozessor haben, wird das Programm gleichzeitig ausgeführt. Wie kann das sein? Das liegt daran, dass das Betriebssystem, mit Hilfe der Hardwarefähigkeiten, eine Illusion schafft. Eine Illusion, dass es im System mehrere virtuelle Prozessoren gibt, indem es einen physischen Prozessor in theoretisch unendlich viele umwandelt und somit den Programmen ermöglicht, gleichzeitig ausgeführt zu werden. Diese Illusion nennt man CPU-Virtualisierung.
Ein solches Szenario wirft viele Fragen auf, zum Beispiel: Wenn mehrere Programme gleichzeitig gestartet werden wollen, welches Programm wird tatsächlich ausgeführt? Diese Frage beantworten die „Richtlinien“ des Betriebssystems. Richtlinien werden an vielen Stellen im Betriebssystem verwendet und beantworten solche Fragen, während sie auch die grundlegenden Mechanismen sind, die das Betriebssystem implementiert. Daher ist die Rolle des Betriebssystems als Ressourcenmanager entscheidend.
Speicher-Virtualisierung
Lassen Sie uns nun den Speicher näher betrachten. Das physische Modell des Speichers in modernen Systemen wird als Array von Bytes dargestellt.Um aus dem Speicher zu lesen, muss man die Adresse der Zelleangeben, um darauf zugreifen zu können. Zum Schreiben oder Aktualisieren von Daten muss man ebenfalls die Daten und die Adresse der Zelle angeben, in die sie geschrieben werden sollen.
Zugriffe auf den Speicher erfolgen kontinuierlich während der Programmausführung. Das Programm speichert seine gesamte Datenstruktur im Speicher und greift darauf zu, während es verschiedene Anweisungen ausführt. Die Anweisungen selbst werden ebenfalls im Speicher abgelegt, weshalb auch bei jedem Aufruf der nächsten Anweisung auf den Speicher zugegriffen wird.
Aufruf von malloc()
Betrachten wir das folgende Programm, das einen Speicherbereich unter Verwendung des Aufrufs malloc() (https://youtu.be/jnlKRnoT1m0):

Das Programm führt mehrere Aufgaben aus. Zunächst reserviert es einen bestimmten Speicherbereich (Zeile 7), anschliessend gibt es die Adresse des reservierten Speicherplatzes aus (Zeile 9) und schreibt eine Null in den ersten Slot des reservierten Speichers. Danach geht das Programm in eine Schleife, in der es den Wert, der an der in der Variablen „p“ gespeicherten Adresse steht, inkrementiert. Außerdem gibt es die Prozess-ID von sich selbst aus. Die Prozess-ID ist für jeden gestarteten Prozess einzigartig.Startet man mehrere Kopien, stößt man auf ein interessantes Ergebnis: Im ersten Fall, wenn man nichts unternimmt und einfach mehrere Kopien startet, werden die Adressen unterschiedlich sein. Doch das passt nicht zu unserer Theorie! Richtig, denn in modernen Distributionen ist standardmäßig eine Funktion zur Speicher-Randomisierung aktiviert. Wenn man diese deaktiviert, erhält man das erwartete Ergebnis — die Speicheradressen von zwei gleichzeitig laufenden Programmen werden übereinstimmen.

Letztendlich ergibt sich, dass zwei unabhängige Programme mit ihren eigenen privaten Adressräumen arbeiten, die von der Betriebssystem in den physischen Speicher abgebildet werden.. Daher haben Programme den Eindruck, dass sie ihren eigenen Bereich physischer Speicherressourcen vollständig zugewiesen bekommen, während die Adresse der Speichernutzung innerhalb eines Programms keinen Einfluss auf andere Programme hat. Die Realität ist jedoch, dass der physische Speicher eine gemeinsam genutzte Ressource ist, deren Verwaltung vom Betriebssystem übernommen wird.
Konsistenz
Ein weiteres wichtiges Thema im Bereich der Betriebssysteme ist Konsistenz. Dieser Begriff wird verwendet, wenn es um Probleme im System geht, die auftreten können, wenn mehrere Operationen innerhalb eines Programms gleichzeitig durchgeführt werden. Konsistenzprobleme können sogar im Betriebssystem selbst auftreten. In den vorherigen Beispielen zur Speicher- und Prozessvirtualisierung haben wir festgestellt, dass das Betriebssystem viele Dinge gleichzeitig verwaltet — es startet den ersten Prozess, dann den zweiten und so weiter. Diese Vorgehensweise kann, wie sich herausstellt, zu bestimmten Schwierigkeiten führen. So haben zum Beispiel moderne Multithreading-Anwendungen mit solchen Herausforderungen zu kämpfen.
Lassen Sie uns das folgende Programm betrachten:

Das Programm erstellt in der Hauptfunktion zwei Threads mit dem Aufruf Pthread_create(). In diesem Beispiel kann man von einem Thread als von einer Funktion denken, die im gleichen Speicherbereich neben anderen Funktionen ausgeführt wird, wobei die Anzahl der gleichzeitig ausgeführten Funktionen offensichtlich mehr als eins beträgt. In diesem Beispiel startet jeder Thread und führt die Funktion worker() aus, die wiederum einfach eine Variable inkrementiert.,.
Wir starten dieses Programm mit dem Argument 1000. Wie Sie sich vielleicht schon denken können, sollte das Ergebnis 2000 sein, da jeder Thread die Variable 1000 Mal inkrementiert hat. Doch so einfach ist es nicht. Lassen Sie uns das Programm mit einer deutlich höheren Wiederholungszahl starten.

Wenn wir als Eingabe die Zahl 100000 angeben, erwarten wir, als Ausgabe die Zahl 200000 zu sehen. Allerdings sehen wir, wenn wir 100000 mehrmals ausführen, nicht nur kein korrektes Ergebnis, sondern wir erhalten verschiedene falsche Antworten. Das Rätsel liegt darin, dass für die Erhöhung der Zahl drei Operationen notwendig sind – das Auslesen der Zahl aus dem Speicher, das Inkrementieren und dann das Zurückschreiben der Zahl. Da all diese Instruktionen nicht atomar (alle gleichzeitig) ausgeführt werden, können solche seltsamen Dinge passieren. Dieses Problem wird in der Programmierung als Race Condition — Zustand des Wettlaufs. Wenn unbekannte Kräfte zu einem unbekannten Zeitpunkt die Ausführung Ihrer Operationen beeinflussen können.
Quelle: habr.com
