Kontrollfragen (Architekturen und Funktionsprinzipien)​
Wie lautet die Definition eines Betriebssystems gemäß der ISO/IEC 2383:2015?
Welches Beispiel wird im Text verwendet, um die Rolle des Betriebssystems als "Brücke" zwischen Hardware-Komplexität und Benutzeranforderung zu illustrieren?
In welche zwei groben Kategorien lassen sich die Aufgaben eines Betriebssystems einteilen?
Auf welche Weise passt ein Betriebssystem die Leistung der Hardware an die BedĂĽrfnisse der Benutzer an und schĂĽtzt sie gleichzeitig?
Nennen Sie die zwei Hauptformen von Benutzerschnittstellen, die im Text beschrieben werden, und einen wesentlichen Unterschied in ihrer Bedienung.
Was war das Hauptziel der Stapelverarbeitung (Batch Processing) ab Ende der 50er-Jahre?
Wie steigerte der Mehrprogramm-Betrieb (z.B. bei der IBM 360) die Effizienz der Prozessornutzung?
Wie unterscheidet sich Timesharing vom reinen Mehrprogramm-Betrieb, insbesondere im Hinblick auf die Nutzer?
Wer entwickelte UNIX und als Reaktion auf die Komplexität welches Vorgängersystems entstand es?
Was beschreibt das Entwurfskriterium "Portierbarkeit"?
Nennen Sie die zwei Aspekte, die das Entwurfskriterium "Skalierbarkeit" umfasst.
Welches Ziel verfolgt das Entwurfskriterium "Transparenz und Virtualisierung"?
Was ist die zentrale Aufgabe der Betriebssystemkomponente "Prozessverwaltung und -koordinierung"?
Warum wird die Hauptspeicherverwaltung laut Text oft als eigene Komponente behandelt, obwohl sie logisch zur Betriebsmittelverwaltung gehört?
Was definiert die Programmierschnittstelle (API) eines Betriebssystems?
Was ist der Hauptnachteil einer monolithischen Architektur, der zur Entwicklung der Kern-Schale-Struktur fĂĽhrte?
Wie ist die typische Aufgabenverteilung zwischen Kernel (Kern) und Shell (Schale) in der Kern-Schale-Architektur?
Erklären Sie den Unterschied zwischen einer "konsistenten Schichtung" und einer "quasi-konsistenten Schichtung".
Was ist die Kernidee der Mikrokern-Architektur und wie werden die meisten Betriebssystemdienste implementiert?
Welchen Vorteil bietet die Mikrokern-Architektur hinsichtlich der Robustheit (Fehlertoleranz) des Systems?
Laut ISO/IEC 2383:15 ist ein Betriebssystem eine Software, die die AusfĂĽhrung von Programmen steuert und Dienste wie Ressourcenzuweisung, Ablaufplanung, Ein-/Ausgabesteuerung und Datenverwaltung bereitstellen kann.
Das Beispiel der Dateiverwaltung: Die Hardware erlaubt nur das Lesen/Schreiben von rohen Datenblöcken (z.B. 4KiB), während das Betriebssystem dem Benutzer die einfache Verwaltung von logischen Dateien beliebiger Größe ermöglicht.
Die Aufgaben werden grob eingeteilt in: 1. Optimale Ausnutzung von Ressourcen (z.B. Stromverbrauch) und 2. ErfĂĽllung von Nutzeranforderungen (z.B. Echtzeit, Bedienbarkeit).
Es erweitert den Leistungsumfang der Hardware (z.B. durch Dateiverwaltung) und schĂĽtzt sie durch Kapselung und Abstraktion, indem der direkte Zugriff auf die Hardware unterbunden wird.
Kommandosprachen (textbasiert) und 2. Grafische Benutzungsoberflächen (GUI). Der Unterschied liegt in der Bedienung: Bei Kommandosprachen erfolgen Aufträge durch Texteingaben (z.B. "ls -al"), bei GUIs bevorzugt über grafische Eingabegeräte (z.B. Maus, "drag & drop").
Das Ziel war die automatisierte, sequenzielle Abarbeitung mehrerer Nutzeraufträge (Jobs) nacheinander auf Großrechnern.
Das Betriebssystem nutzte Prozessor-Wartezeiten (z.B. während langsamer Ein-/Ausgabeoperationen) eines Jobs, um in dieser Zeit einen anderen Job zu bearbeiten.
Timesharing ist eine Modifikation für Mehrbenutzersysteme. Es verteilt die Prozessorleistung durch zeitliche Verschachtelung aktiv auf verschiedene Jobs, um mehreren Nutzern quasi-gleichzeitig eine Bedienung (z.B. an Terminals) zu ermöglichen (während Multiprogramming primär die CPU-Auslastung optimiert).
UNIX wurde (ab 1969) von Dennis Ritchie und Ken Thompson als Reaktion auf die Komplexität des monolithischen Systems Multics entwickelt.
Portierbarkeit bezeichnet den Grad des Arbeitsaufwandes, der nötig ist, um ein Betriebssystem von einer Hardware-Plattform auf eine andere zu übertragen (zu "portieren").
Die Fähigkeit, das OS auf verschiedensten Rechnerplattformen (z.B. Smartphone bis Server) zu nutzen. 2. Die Fähigkeit, die Arbeitslast auf mehrere parallel arbeitende Prozessoren (Kerne) aufzuteilen.
Das Betriebssystem soll dem Anwender möglichst viele Details der Hardware und auch Ressourcenmängel (z.B. knapper Hauptspeicher) verbergen (transparent machen) oder ausgleichen (virtualisieren).
Sie verwaltet die laufenden Anwendungsprozesse und beeinflusst deren Ablauf so, dass sie ihre Aufgaben fehlerfrei erfüllen können, selbst wenn sie miteinander in Konflikt geraten.
Sie wird wegen ihrer hohen Komplexität oft als eigene Komponente realisiert.
Die API definiert verbindlich (in Syntax und Semantik) diejenigen Funktionen des Betriebssystems (Systemdienste/Systemaufrufe), die ein Programmierer in seinen Programmen verwenden kann.
Änderungen und Erweiterungen sind sehr aufwendig, da alle Bestandteile in einem großen Block gesammelt sind und jede Funktion auf jede andere zugreifen kann ("jede Funktion ist von außen offen sichtbar").
Der Kernel übernimmt die "lebenswichtigen" und hardwareabhängigen Komponenten (z.B. Prozessor- und Speicherverwaltung). Die Shell übernimmt alle anderen, hardwarefernen Komponenten (z.B. Benutzerkommunikation).
Bei "konsistenter Schichtung" sind Interaktionen nur zwischen direkt benachbarten Schichten erlaubt. Bei "quasi-konsistenter Schichtung" dürfen auch dazwischenliegende Schichten übersprungen werden (Dienste einer Schicht sind von allen höheren Schichten nutzbar).
Die Kernidee ist die Reduzierung des Kernels auf absolut nötige Basisfunktionen (hardwarenahe Teile, Datenaustausch). Alle weiteren Dienste werden als eigenständige Dienstprozesse (Server) außerhalb des Kerns implementiert und nutzen das Client-Server-Modell.
Da die Dienste als eigenständige Prozesse außerhalb des Kerns laufen, führt ein Fehler in einem einzelnen Dienstprozess (z.B. Dateisystem-Server) im Allgemeinen nicht zum Absturz des gesamten Systems (der Mikrokern läuft weiter).
Warum ist in Multitasking-Systemen ein Identifikator fĂĽr jeden Prozess notwendig?
Wozu sind Kernsperren nötig?
Benötigt man Kernsperren auch bei Multikernel-Betriebssystemen?
Nennen Sie einige konkrete Aktionen, die ein Betriebssystem beim Beenden eines Prozesses ausfĂĽhren muss.
Was versteht man unter einem Thread?
Worauf sollte bei der Programmierung einer Anwendung fĂĽr ein Multi-Threading-Betriebssystem geachtet werden?
Erklären Sie den fundamentalen Unterschied zwischen einem Programm und einem Prozess anhand der Begriffe "statisch" und "dynamisch".
Was ist der Unterschied zwischen echter Parallelität (Multiprocessing) und Quasi-Parallelität (Nebenläufigkeit), und welche Hardware-Voraussetzung ist für echte Parallelität nötig?
Beschreiben Sie den Zweck des Systemmodus (Kernel Mode) und wie ein Prozess aus dem Benutzermodus dorthin gelangt.
Nennen Sie die drei Grundzustände eines Prozesses (außer "nicht existent") und beschreiben Sie kurz, was sie bedeuten.
Ein Prozess wechselt vom Zustand "aktiv" in den Zustand "wartend". Was ist typischerweise der Auslöser dafür und wer veranlasst diesen Wechsel?
Wozu dient der Hardware-Kontext (Register-Kontext) eines Prozesses und wann muss er gesichert werden?
Erklären Sie die unterschiedlichen Aufgaben von Scheduler und Dispatcher bei einem Prozesswechsel.
Warum ist "Busy Waiting" (aktives Warten) ineffizient und welche Funktion hat der "Idle Task" (Leerlaufprozess) in diesem Zusammenhang?
Warum ist eine vollständige Kernsperre für die Leistung von Mehrkernprozessoren schädlich und welche Alternative wird im Text genannt?
Was ist das Hauptmerkmal einer NUMA-Architektur und warum muss das Betriebssystem die "Affinität" berücksichtigen?
Wie unterscheidet sich der Ansatz zur Prozesserzeugung unter UNIX/Linux (z.B. fork()/exec()) von dem unter Windows (z.B. CreateProcess())?
Welches spezifische Problem des "schwergewichtigen" Prozesswechsels wird durch das Thread-Konzept gelöst und wie?
Erklären Sie den Unterschied zwischen dem Zuordnungsmodell "Alle User-Level-Threads auf einen Kernel-Thread" und "Jeder User-Level-Thread auf einen Kernel-Thread".
Der Addressraum eines Prozesses umfasst alle dem Prozess gehörenden (von ihm belegten) Speicherbereiche zur Nutzung für Programm-Code, statische Daten und dynamische Daten (Stapelspeicher). Er bildet quasi ein Speicher-Abbild des Prozesses.
In Multitasking-Systemen ist für jeden Prozess ein Identifikator notwendig, weil das Betriebssystem jeden beliebigen Prozess individuell „ansprechen“ muss, um ihn beeinflussen zu können. Dafür muss es auch die entsprechenden „privaten“ Daten zu jedem Prozess in seiner Verwaltung finden. Übrigens müssen auch ein anderer Prozess sowie der Bediener den Identifikator eines Prozesses kennen, um auf ihn einwirken zu können (z. B. für eine vorzeitige Beendigung).
Kernsperren sind nötig, um die Datenkonsistenz im Betriebssystemkern zu sichern, indem sie verhindern, dass kritische Abläufe (wie die Manipulation von Prozesslisten) unterbrochen werden. Daher kann es erforderlich sein, für bestimmte Zeitabschnitte das Auftreten von Unterbrechungen zu ignorieren bzw. diese zu verbieten.
Ein Multikernel-Betriebssystem behandelt jeden Kern als eine unabhängige Einheit, die über Nachrichten mit den anderen Einheiten kommuniziert. Sieht man in diesem Modell eine feste Eins-zu-eins-Zuordnung zwischen CPU und Betriebssystemkern vor, bei der es keine Annahme von gemeinsamem Speicherbereichen gibt, bedeutet das zunächst einmal, dass jeder Kern auf jeder CPU seinen eigenen Adressraum hat. Folglich benötigt man hier keine Sperren für den konkurrierenden Zugriff von verschiedenen CPUs auf gemeinsame Datenstrukturen. Trotzdem könnte es weiterhin zu Unterbrechungen von Prozessen auf derselben CPU kommen, sodass die lokalen Datenstrukturen weiterhin gegen nebenläufige Zugriffe verschiedener Prozesse geschützt werden müssen (vgl.
Abschnitt 3.3.2. Systemdienste zur Prozessverwaltung)
Beim Beenden eines Prozesses sind unter anderem folgende Aktionen nötig:
Änderung des Zustandes,
Freigabe der ggf. noch belegten Ressourcen,
Freigabe des PCBs,
Freigabe des Identifikators (PID).
Ein Thread ist ein „leichtgewichtiger Prozess“, der sich im Allgemeinen mit anderen Threads den Adressraum eines Prozesses teilt und darin nur seinen Stackbereich und die Prozessor-Register privat benutzt. Alle anderen Teile stehen auch den anderen Threads dieses Prozesses zur Verfügung. Der Thread übernimmt damit die Rolle des Aktivitätsträgers im System, der Prozess dagegen bildet nur noch eine (passive) Ausführungsumgebung, eine Art Container für seine Threads.
Bei einem Multi-Threading-System sollte man eine Anwendung möglichst in mehrere parallele Threads zerlegen, damit der Vorteil des leichteren bzw. schnelleren Wechsels zwischen Threads ein und desselben Prozesses maximal ausgenutzt werden kann, denn Prozesse mit nur einem Thread profitieren davon nicht.
Programm vs. Prozess: Ein Programm ist eine statische Einheit – eine passive Datei, die eine Folge von Anweisungen und Daten enthält. Ein Prozess ist hingegen ein dynamisches Objekt – es ist das "Programm in Ausführung", der eigentliche "Aktivitätsträger", der einen zeitlichen Ablauf und einen sich ändernden Zustand besitzt.
Echte vs. Quasi-Parallelität: Echte Parallelität (Multiprocessing) ist die tatsächlich gleichzeitige Ausführung mehrerer Prozesse. Dies erfordert als Hardware-Voraussetzung mehrere Prozessorkerne. Quasi-Parallelität (Nebenläufigkeit) ist die Illusion von Gleichzeitigkeit auf einem einkernigen Prozessor. Das Betriebssystem erreicht dies durch eine extrem schnelle, zeitlich verschachtelte Abarbeitung (Kontextwechsel) der Prozesse.
Zweck des Kernel Mode:Der Systemmodus (Kernel Mode) ist ein privilegierter Zustand, der für die Ausführung von Betriebssystem-Kernfunktionen reserviert ist. Nur in diesem Modus dürfen kritische, privilegierte Befehle ausgeführt werden (z.B. direkte Hardware-Steuerung, Speicherverwaltung). Ein Prozess gelangt aus dem Benutzermodus dorthin, indem er einen Systemaufruf (Systemdienst, Trap) oder einen Interrupt (z.B. von der Hardware) auslöst.
Drei Grundzustände eines Prozesses:
Aktiv (running): Der Prozess wird genau jetzt von einem Prozessorkern ausgefĂĽhrt.
Bereit (ready): Der Prozess ist lauffähig und hat alle Ressourcen, die er braucht, wartet aber nur darauf, dass ihm ein Prozessorkern zugeteilt wird.
Wartend (waiting): Der Prozess kann nicht laufen, selbst wenn ein Kern frei wäre, weil er auf ein externes Ereignis wartet (z.B. das Ende einer E/A-Operation oder eine Benutzereingabe).
Übergang "aktiv" zu "wartend": Der Auslöser ist typischerweise, dass der Prozess selbst eine Operation anstößt, die Zeit benötigt (z.B. das Lesen einer Datei von der Festplatte oder eine Netzwerkanfrage). Der Prozess selbst veranlasst diesen Wechsel, indem er den entsprechenden Systemdienst aufruft, der ihn blockiert.
Zweck des Hardware-Kontexts: Der Hardware-Kontext (auch Register-Kontext) umfasst alle Informationen, die der Prozess zu einem Zeitpunkt im Prozessor gespeichert hat (z.B. Befehlszeiger, Stackpointer, alle Register). Er muss immer dann gesichert werden, wenn der Prozess unterbrochen wird (also bei jedem Kontextwechsel), damit er später exakt an derselben Stelle mit denselben Werten weiterarbeiten kann.
Scheduler vs. Dispatcher: Der Scheduler (Ablaufplanung) ist der "Stratege": Er entscheidet, welcher Prozess aus der Liste der "bereiten" Prozesse als Nächstes den Prozessor bekommen soll. Der Dispatcher (Prozessumschalter) ist der "Macher": Er führt die eigentliche Umschaltung (den Kontextwechsel) durch, den der Scheduler beschlossen hat.
Busy Waiting und der Idle Task: "Busy Waiting" (aktives Warten) ist ineffizient, weil ein Prozess dabei einen CPU-Kern zu 100% auslastet, während er in einer Schleife "spinnt" und auf ein Ereignis wartet, statt diese wertvolle Rechenzeit freizugeben. Der "Idle Task" (Leerlaufprozess) ist ein spezieller Prozess mit niedrigster Priorität, der genau dann aktiviert wird, wenn kein anderer Anwendungsprozess "bereit" ist. Er überbrückt die Wartezeit, ohne "busy waiting" zu betreiben, und stellt sicher, dass das System (im Kernel-Modus) nicht aktiv warten muss.
Vollständige Kernsperre und Mehrkernprozessoren: Eine vollständige Kernsperre ist schädlich für die Leistung, weil sie verhindert, dass mehrere Prozessoren gleichzeitig im Kernel-Modus arbeiten können. Selbst wenn 8 Kerne vorhanden sind, dürfte wegen der Sperre immer nur einer den Kernel-Code ausführen, was die echte Parallelität zunichtemacht. Die Alternative ist eine feingranulare Architektur an Sperren, bei der nur die spezifischen Datenstrukturen gesperrt werden, auf die zugegriffen wird, sodass andere Kerne gleichzeitig an anderen Stellen im Kernel arbeiten können.
NUMA und Affinität: Das Hauptmerkmal einer NUMA-Architektur (Non-Uniform Memory Access) ist, dass CPUs über eigenen lokalen Arbeitsspeicher verfügen. Der Zugriff auf diesen lokalen Speicher ist deutlich schneller als der "entfernte" Zugriff auf den Speicher einer anderen CPU. Das Betriebssystem muss die "Affinität" (Zuordnung) berücksichtigen, um Prozesse (und ihre Daten) möglichst auf der CPU auszuführen, die lokalen Zugriff auf die benötigten Daten hat, da dies die Gesamtleistung massiv steigert.
fork/exec vs. CreateProcess: Windows (CreateProcess) erledigt Erzeugung und das Laden des Programms in einem Schritt. Man sagt dem System: "Erzeuge einen neuen Prozess und fĂĽhre dieses Programm darin aus".UNIX/Linux (fork/exec) verwendet einen zweistufigen Ansatz: fork() erzeugt einen exakten Klon (eine Kopie) des aufrufenden Prozesses. Der neue (Kind-)Prozess muss dann exec() aufrufen, um sich selbst durch ein neues Programm zu ersetzen.
Das Thread-Problem (Prozesswechsel): "Schwergewichtige" Prozesswechsel sind teuer (langsam), weil der Kontext so groß ist. Der größte "Ballast" dabei ist der Adressraum, der bei jedem Wechsel komplett ausgetauscht werden muss. Threads lösen das Problem, indem sie sich einen gemeinsamen Adressraum teilen. Bei einer Umschaltung zwischen Threads desselben Prozesses muss der Adressraum nicht gewechselt werden, was den Kontextwechsel "leichtgewichtig" und viel schneller macht.
Thread-Zuordnungsmodelle: Alle User-Level-Threads auf einen Kernel-Thread: Das Betriebssystem sieht nur einen Kernel-Thread (den Prozess). Die gesamte Thread-Verwaltung (das Umschalten zwischen den User-Level-Threads) findet schnell im User-Space statt (z.B. durch eine Bibliothek). Nachteil: Wenn ein User-Level-Thread blockiert (z.B. auf E/A wartet), blockiert das Betriebssystem den gesamten Kernel-Thread und damit alle anderen User-Level-Threads in diesem Prozess. Jeder User-Level-Thread auf einen Kernel-Thread: Das Betriebssystem kennt und verwaltet jeden einzelnen Thread. Vorteil: Wenn ein Thread blockiert, kann das OS einfach einen anderen Thread (desselben oder eines anderen Prozesses) auf dem Kern einplanen. Nachteil: Jede Thread-Umschaltung erfordert einen Wechsel in den Kernel-Modus und ist langsamer als die reine User-Level-Verwaltung.