Prozesse und Systemaufrufe

Lerne, wie ein Betriebssystem eine CPU unter vielen laufenden Programmen aufteilt und dabei jedes Programm an seinem Platz hält. Du erfährst, was ein Prozess ist und wie er zwischen den Zuständen „laufend“, „bereit“ und „blockiert“ wechselt. Du erzeugst Prozesse in C mit fork, führst mit exec neue Programme aus und rufst mit wait ihre Ergebnisse ab. Daraus entwickelst du eine kleine Shell mit Umleitungen und Pipes. Anschließend blickst du hinter die Kulissen: auf Benutzermodus und Kernelmodus, wie ein Systemaufruf über einen Trap in den Kernel wechselt, worin sich Ausnahmen und Interrupts unterscheiden und wie das Betriebssystem durch eingeschränkte direkte Ausführung und den Timerinterrupt die CPU zurückerhält und von einem Prozess zu einem anderen wechselt.

MittelEinheit 1 von 9 in Kurs: Betriebssysteme – Prozesse, Speicher, Threads und Dateisysteme

Was du lernst

  1. Beschreibe die Hauptaufgaben des Betriebssystems sowie die Geschichte und Entwurfsideen, die erklären, warum es den Rechner auf diese Weise aufteilt und schützt.
    • Beschreibe die Hauptaufgaben eines Betriebssystems
      Erkläre, wie das Betriebssystem aus der zugrunde liegenden Hardware leichter nutzbare, gemeinsam genutzte und geschützte Ressourcen für Programme macht.
    • Erkläre, wie Mehrprogrammbetrieb und Zeitteilverfahren die Entwicklung des Betriebssystems geprägt haben
      Verfolge die Entwicklung von der Stapelverarbeitung über den Mehrprogrammbetrieb zum Zeitteilverfahren, erkenne, warum jeder Schritt neuen Schutz erforderte, und vergleiche die wichtigsten Organisationsformen eines Kernels.
  2. Erkläre, was ein Prozess ist, was das Betriebssystem über jeden Prozess speichert und wie Prozesse zwischen den Zuständen „laufend“, „bereit“ und „blockiert“ wechseln.
    • Erkläre, was ein Prozess ist und welche Informationen das Betriebssystem über ihn speichert
      Unterscheide ein Programm von einem Prozess, beschreibe den Maschinenzustand und den Adressraum eines Prozesses und erkläre, wie das Betriebssystem einen Prozess erzeugt und verwaltet.
    • Verfolge die Zustandswechsel eines Prozesses
      Verfolge anhand der Zustände „laufend“, „bereit“ und „blockiert“ und ihrer Übergänge, wie Prozesse eine CPU gemeinsam nutzen, und erkenne die zusätzlichen Zustände, die reale Systeme melden.
  3. Nutze die Unix-Prozessschnittstelle in C, um Kindprozesse zu erzeugen, in ihnen neue Programme auszuführen, ihre Ergebnisse abzurufen, sie zu beenden und diese Aufrufe zu einer einfachen Shell zu verbinden.
    • Erzeuge mit fork einen Kindprozess
      Rufe fork auf, unterscheide Eltern- und Kindprozess anhand des Rückgabewerts und sage voraus, was jeder Prozess sieht und ausgibt.
    • Führe mit exec ein anderes Programm aus
      Ersetze mit exec das Programm in einem Prozess, übergib Argumente korrekt und erkläre, warum Unix das Erzeugen eines Prozesses vom Laden eines Programms trennt.
    • Warte auf Kindprozesse, lies ihren Beendigungsstatus aus und beende Prozesse
      Beende Prozesse mit exit und Signalen, rufe das Ergebnis eines Kindprozesses mit wait und waitpid ab, dekodiere seinen Status und verhindere Probleme durch Zombieprozesse und verwaiste Prozesse.
    • Entwickle eine einfache Shell mit fork, exec, wait und Umleitungen
      Verbinde die Prozessaufrufe zu einer Shell-Schleife, die Befehle ausführt, Ein- und Ausgabe umleitet, Befehle mit Pipes verbindet und Aufträge im Hintergrund ausführt.
  4. Erkläre, wie die Hardware privilegierten Kernelcode von Benutzerprogrammen trennt und wie Programme diese Grenze über Systemaufrufe, Ausnahmen und Interrupts kontrolliert überschreiten.
    • Unterscheide Benutzermodus und Kernelmodus
      Erkläre, was jeder CPU-Modus erlaubt, welche Operationen privilegiert sind und was geschieht, wenn ein Programm die Grenzen seines Modus überschreitet.
    • Erkläre Schritt für Schritt den Ablauf eines Systemaufrufs
      Verfolge einen Systemaufruf vom Bibliothekswrapper über den Trap in den Kernel und zurück und deute seinen Rückgabewert und seine Fehler.
    • Unterscheide Traps, Ausnahmen und Interrupts
      Ordne die Ereignisse ein, die die CPU in den Kernel wechseln lassen, erkläre die Funktion der Tabelle der Kernelbehandlungsroutinen und sage voraus, was nach jeder Art von Ereignis geschieht.
  5. Erkläre, wie das Betriebssystem Programme direkt und mit voller Geschwindigkeit auf der CPU ausführt und dabei die Kontrolle behält, und wie es die CPU von einem Prozess an einen anderen übergibt.
    • Erkläre die eingeschränkte direkte Ausführung
      Erkläre die zwei Probleme bei der direkten Ausführung von Programmen auf der CPU und wie Benutzermodus, Traps und Timerinterrupt sie lösen, Schritt für Schritt vom Systemstart bis zum Prozessende.
    • Verfolge einen Kontextwechsel und schätze seinen Aufwand ab
      Verfolge jeden Schritt beim Wechsel der CPU von einem Prozess zu einem anderen, unterscheide einen Kontextwechsel von einem Moduswechsel und schätze ab, wie viel CPU-Zeit ein Wechsel kostet.

Prozesse und Systemaufrufe einfach erklärt

5 Kapitel

1

Erkläre, was ein Betriebssystem macht

Das Betriebssystem bietet gewöhnlichen Programmen nützliche Möglichkeiten, Berechnungen auszuführen, Daten zu speichern und Geräte zu nutzen, während sie sich den Rechner sicher teilen. Seine Aufgaben erklären, warum eine Desktopanwendung keinen eigenen Festplattentreiber braucht und warum ein fehlerhaftes Programm nicht alles zum Stillstand bringen sollte. Verfolge die Entwicklung von Rechnern mit nur einem Auftrag zu interaktiven Systemen und vergleiche dann, wie verschiedene Kernelentwürfe privilegierte Aufgaben aufteilen.

1.1 Beschreibe die Hauptaufgaben eines Betriebssystems

Ein Browser, ein Compiler und ein Musikplayer können auf einem Computer alle Fortschritte machen. Jeder braucht CPU-Zeit, Speicher und Zugriff auf Geräte. Das Betriebssystem stellt diese Ressourcen über Dienste bereit, die Programme nutzen können, ohne den ganzen Rechner zu kontrollieren.

Stelle Programmen nützliche Ressourcen bereit

Virtualisierung macht aus einer physischen Ressource eine bequem nutzbare virtuelle Ressource. Durch CPU-Virtualisierung können sich viele Prozesse so verhalten, als hätte jeder einen eigenen Prozessor. Das Betriebssystem lässt jeden eine Zeit lang laufen und bewahrt seinen Zustand zwischen den Abschnitten. Die Speichervirtualisierung gibt jedem Prozess einen eigenen Adressraum: die Adressen, die seine Befehle nutzen können.

Eine Abstraktion verbirgt Details hinter einer nützlichen Schnittstelle. Eine Datei ist eine Abstraktion für gespeicherte Bytes. Ein Programm kann write aufrufen, ohne zu wissen, ob diese Bytes auf eine SSD oder ein Netzlaufwerk gehen. Ein Systemaufruf bietet eine kontrollierte Möglichkeit, einen Kerneldienst anzufordern. Die gerätespezifische Arbeit bleibt hinter der Schnittstelle.

Teile Ressourcen auf und schütze Programme

Als Ressourcenverwalter entscheidet das Betriebssystem, wer CPU-Zeit, Speicher und Gerätedienste erhält. Es versucht, die Hardware sinnvoll auszulasten und Programmen zugleich angemessenen Zugriff zu geben. Die gemeinsame Nutzung braucht auch Schutz: Ein Programm darf weder den privaten Speicher eines anderen überschreiben noch den Timer deaktivieren, um die CPU zu behalten.

Die Hardware hilft, diese Grenze durchzusetzen. Anwendungsbefehle laufen im Benutzermodus mit eingeschränkten Berechtigungen. Kernelcode läuft mit den Privilegien, die zur Verwaltung des Rechners nötig sind. Ein Timer kann ein Programm auch dann unterbrechen, wenn es nie etwas vom Betriebssystem anfordert.

Betrachte bei einem Laptop, der eine Datei herunterlädt und gleichzeitig Code kompiliert, drei getrennte Aufgaben:

  1. Das Netzwerkgerät meldet eintreffende Daten. Der Kernel stellt sie dem herunterladenden Prozess zur Verfügung.
  2. Das Betriebssystem teilt die CPU-Zeit zwischen Browser, Compiler und anderen bereiten Prozessen auf.
  3. Der Compiler fordert Dateioperationen an, um seine Ausgabe zu speichern. Sein Code steuert den Speichercontroller nicht direkt an.
Trenne den Kernel von der Oberfläche

Der Kernel ist der privilegierte Kern. Shell, Desktop und die meisten Systembibliotheken laufen als gewöhnliche Programme, selbst wenn sie mit dem Betriebssystem ausgeliefert werden. Das Schließen eines Desktopfensters entfernt den Kernel nicht. Ein Server ohne grafische Oberfläche braucht weiterhin Prozessverwaltung, Speicherschutz und Gerätedienste.

Auf einer logischen CPU wechseln sich Benutzercode und Kernelcode ab. Während der Compiler gewöhnliche Benutzerbefehle ausführt, läuft der Kernel nicht gleichzeitig auf dieser CPU. Systemaufrufe, Ausnahmen und Hardwareinterrupts geben ihm die Kontrolle zurück.

Wähle erst das Wie, dann das Wer

Ein Mechanismus stellt eine Operation bereit; eine Strategie wählt, wie sie genutzt wird. Die Register eines Prozesses zu sichern und die eines anderen wiederherzustellen, ist ein Mechanismus. Zu wählen, welcher bereite Prozess den nächsten Abschnitt erhält, ist eine Strategie. Diese Trennung erlaubt es dem Betriebssystem, seine Schedulingentscheidungen zu ändern, ohne den Wechselmechanismus zu ersetzen.

Beim Betriebssystementwurf werden nützliche Abstraktionen, Leistung, Schutz, Zuverlässigkeit, Sicherheit und Energieverbrauch gegeneinander abgewogen. Die zusätzliche CPU-Zeit und der zusätzliche Speicher für die Verwaltung sind Verwaltungsaufwand. Drei wiederkehrende Themen des Fachgebiets sind Virtualisierung, Nebenläufigkeit (gleichzeitig laufende Aktivitäten) und Persistenz, durch die gespeicherte Daten das Ende eines Programms oder einen Stromausfall überdauern.

Wichtige Erkenntnisse

  • Virtualisierung gibt Programmen bequem nutzbare Sichten auf gemeinsam genutzte Hardware.
  • Abstraktionen wie Dateien verbergen Gerätedetails hinter nützlichen Operationen.
  • Der Kernel ist privilegiert; Shell und Desktop sind gewöhnliche Programme.
  • Ein Mechanismus stellt eine Operation bereit; eine Strategie wählt, wann oder für wen sie genutzt wird.
  • Schutz und geringer Verwaltungsaufwand sind beides Ziele beim Entwurf von Betriebssystemen.

1.2 Erkläre, wie Mehrprogrammbetrieb und Zeitteilverfahren die Entwicklung des Betriebssystems geprägt haben

Betriebssysteme entwickelten sich weiter, als Computer von der Ausführung einzelner Aufträge dazu übergingen, viele Aktivitäten gleichzeitig zu bedienen. Jede Änderung löste ein praktisches Problem: untätige Hardware, langsame Interaktion oder Fehler, die sich zwischen Programmen ausbreiten.

Von Stapeln zu überlappender Arbeit

Bei der Stapelverarbeitung laufen eingereichte Aufträge ohne Interaktion mit der einreichenden Person nacheinander ab. Frühe Unterstützungssoftware half hauptsächlich beim Laden von Aufträgen und bei üblichen Ein-/Ausgabeoperationen. Musste der aktuelle Auftrag auf ein Gerät warten, konnte die CPU untätig bleiben.

Der Mehrprogrammbetrieb hält mehrere Aufträge im Speicher. Blockiert einer wegen Ein-/Ausgabe, kann ein anderer rechnen. Angenommen, ein Auftrag rechnet wiederholt 20 ms und wartet 80 ms auf die Festplatten-Ein-/Ausgabe. Allein beträgt seine CPU-Auslastung 20 / 100 = 20 %. Bei zwei solchen Aufträgen und geeigneter Überlappung können nützliche Berechnungen 40 % der verstrichenen Zeit ausfüllen. Die Verzögerung des Geräts ist nicht kürzer geworden; stattdessen wurde CPU-Zeit gefüllt, die sonst ungenutzt bliebe.

Der Gewinn hängt davon ab, dass Arbeit bereitsteht. Zusätzliche Aufträge können die CPU-Auslastung nicht über 100 % erhöhen, und bei Aufträgen, die auf dasselbe Gerät warten, kann weiterhin ein Engpass entstehen. Mehrere Aufträge im Speicher brauchen auch Schutz: Keiner darf einen anderen überschreiben oder die eigenen Daten des Betriebssystems beschädigen.

Sorge für schnelle Reaktionen

Das Zeitteilverfahren gibt Prozessen kurze Abschnitte auf der CPU. Ein Terminal oder Desktop kann reagieren, während andere Programme weiterarbeiten. Müsste man warten, bis ein Stapelauftrag endet, bevor der Tastendruck eines anderen Benutzers bedient wird, würde sich das ganz anders anfühlen.

AufteilungsverfahrenWas aufgeteilt wirdBeispiel
ZeitteilverfahrenNutzung einer Ressource über aufeinanderfolgende ZeitabschnitteCPU-Abschnitte
RaumteilverfahrenKapazität, die verschiedene Benutzer gleichzeitig belegenSpeicherbereiche oder Festplattenplatz

Nebenläufigkeit bedeutet, dass mehrere Aktivitäten im selben Zeitraum ablaufen. Ihre Befehle können auf einer logischen CPU verzahnt werden. Parallelität bedeutet, dass Befehle tatsächlich gleichzeitig auf unterschiedlichen Verarbeitungsressourcen ausgeführt werden. Mehrprogrammbetrieb setzt keine parallele Hardware voraus.

Platziere Dienste innerhalb oder außerhalb des Kernels
EntwurfWo die meisten Dienste laufenWichtigster Zielkonflikt
Monolithischer KernelDateisysteme, Treiber und Netzwerkfunktionen laufen im KernelmodusDirekte Aufrufe sind effizient; ein Treiberfehler kann den Kernel beschädigen
MikrokernelEin kleiner privilegierter Kern unterstützt Dienstprozesse im BenutzermodusDie Isolation der Dienste hilft, Fehler einzugrenzen; Kommunikation verursacht zusätzlichen Aufwand
Hybrider KernelVerbindet Elemente beider AnsätzeDie genaue Grenze zwischen den Diensten hängt vom Entwurf ab

Linux ist monolithisch und kann Kernelmodule laden. MINIX 3, QNX und seL4 veranschaulichen Mikrokernelentwürfe. Windows NT und Apples XNU werden üblicherweise als hybride Entwürfe beschrieben. Ein Treiber im Benutzermodus lässt sich nach einem Absturz oft neu starten, während ein privilegierter Treiber Zugriff hat, durch den sein Ausfall das ganze System betreffen kann.

Frühen Personal-Computer-Systemen wie MS-DOS und dem klassischen Mac OS fehlte der heute erwartete Speicherschutz für Prozesse. Moderne Systeme verbinden Betriebssystemregeln mit ihrer Durchsetzung durch Hardware. Kurze CPU-Abschnitte allein würden ein Programm nicht daran hindern, den Speicher eines anderen Programms zu verändern. Aufteilung und Schutz lösen unterschiedliche Teile des Problems.

Wichtige Erkenntnisse

  • Der Mehrprogrammbetrieb füllt Ein-/Ausgabewartezeiten mit Berechnungen eines anderen Auftrags.
  • Das Zeitteilverfahren unterstützt die interaktive Nutzung durch kurze CPU-Abschnitte.
  • Nebenläufige Arbeit kann auf einer CPU verzahnt werden; parallele Arbeit wird gleichzeitig ausgeführt.
  • Das Zeitteilverfahren teilt Nutzungsabschnitte auf; das Raumteilverfahren teilt Kapazität auf.
  • Bei der Kernelorganisation stehen direkte Dienstaufrufe und eine stärkere Isolation der Dienste in einem Zielkonflikt.
  • Mehrere im Speicher befindliche Aufträge erfordern Speicher- und Privilegienschutz.
2

Beschreibe Prozesse und ihre Zustände

Eine Programmdatei ist nur der Ausgangspunkt. Um sie auszuführen, erstellt und verwaltet das Betriebssystem einen aktiven Prozess mit Speicher, Registern, offenen Deskriptoren und einem Platz in der Prozessliste. Lerne, diesen Zustand zu lesen, und verfolge dann zwei Prozesse, während sich CPU-Arbeit und Wartezeiten auf Geräte überlappen. Der Unterschied zwischen dem Warten auf eine CPU und dem Warten auf ein Ereignis erklärt, welchen Prozess der Scheduler auswählen kann.

2.1 Erkläre, was ein Prozess ist und welche Informationen das Betriebssystem über ihn speichert

Eine ausführbare Datei auf einem Datenträger kann wiederholt gestartet werden. Jeder Start braucht einen eigenen veränderlichen Zustand, damit eine Instanz unabhängig pausieren, fortgesetzt werden oder ausfallen kann. Diese aktive Instanz ist ein Prozess.

Erkenne die laufende Instanz

Ein Programm besteht aus gespeicherten Befehlen und statischen Daten. Ein Prozess verbindet ein Programm mit seinem aktuellen Speicher, seinen CPU-Registern und seinem Ein-/Ausgabezustand. Drei Shells können dieselbe ausführbare Datei ausführen und dabei unterschiedliche Variablen, aktuelle Verzeichnisse und Befehlsverläufe haben.

Der Befehlszähler (PC) bezeichnet den nächsten Befehl. Der Stapelzeiger gibt die Position des aktiven Stacks an. Allgemeine Register enthalten Zwischenwerte. Wenn ein Prozess nicht mehr läuft, muss genug Zustand erhalten bleiben, damit er beim richtigen Befehl mit den richtigen Werten weitermachen kann.

Lies die Darstellung des Adressraums

Der Adressraum eines Prozesses ist der Speicher, den seine Befehle adressieren können. Eine übliche Darstellung für Lehrzwecke platziert Code und statische Daten bei niedrigeren Adressen, darüber einen Heap und bei höheren Adressen einen Stack. Der Heap dient dynamisch reservierten Objekten; der Stack unterstützt Funktionsaufrufe und lokalen Zustand. In dieser Darstellung wächst der Heap zu höheren und der Stack zu niedrigeren Adressen hin. Der tatsächliche Aufbau hängt vom System ab, und Implementierungen von malloc können Speicher auf mehr als eine Weise erhalten.

Zwei Prozesse können für eine Variable dieselbe virtuelle Adresse ausgeben und dort trotzdem unterschiedliche Werte halten. Virtuelle Adressen gehören zur Speichersicht eines Prozesses. Übereinstimmende Adresszahlen bedeuten keinen gemeinsam genutzten beschreibbaren Speicher.

Starte ein Programm

Um ein neues Programmabbild zu starten, richtet das Betriebssystem diese Teile ein:

  1. Den ausführbaren Code und die Daten, wobei auf modernen Systemen Teile bei Bedarf geladen werden.
  2. Einen anfänglichen Benutzerstack und Startinformationen, darunter die Argumentzeichenketten und den Argumentvektor, mit denen die Laufzeitumgebung main aufruft.
  3. Speicher für dynamische Zuweisungen und den anfänglichen Registerzustand.
  4. Eine Ein-/Ausgabeumgebung einschließlich geerbter Dateideskriptoren.
  5. Einen Einstiegspunkt, an dem der Programmstartcode beginnt.

Unter Unix sind Dateideskriptoren kleine ganze Zahlen, die offene Ein-/Ausgaberessourcen bezeichnen. Nach Shell-Konvention steht 0 für die Standardeingabe, 1 für die Standardausgabe und 2 für die Standardfehlerausgabe. Sie sind oft mit einem Terminal verbunden. Ein Prozess kann weitere Deskriptoren erben; die drei vertrauten Nummern sind weder eine Obergrenze noch eine Zusage, dass jeder Prozess sie immer geöffnet hat.

Untersuche den Datensatz des Kernels

Der Prozesskontrollblock (PCB) speichert Informationen, die der Kernel zur Verwaltung eines Prozesses braucht: PID, Zustand, gesicherten Ausführungskontext, Speicherinformationen, offene Deskriptoren, Elternprozess, Eigentümer, aktuelles Verzeichnis und Abrechnungsinformationen. Linux verwendet task_struct; xv6 verwendet struct proc. Eine Prozesstabelle oder Prozessliste organisiert diese Datensätze.

Während ein Prozess läuft, befinden sich seine aktuellen Register in der CPU. Beim Eintritt in den Kernel werden seine Benutzerregister auf seinem Kernelstack gesichert; ein Kontextwechsel sichert außerdem den Kernelausführungskontext. Der PCB ist kein fortlaufend aktualisiertes Spiegelbild jedes Benutzerregisters.

Eine Linux-Ausgabe von ps könnte zum Beispiel 8240 8200 S bash und 8291 8240 R ps enthalten. Der zweite Prozess hat die PID 8291 und die Eltern-PID 8240, ist also ein Kindprozess der Shell. R umfasst sowohl laufende als auch bereite Prozesse. Eine PID bezeichnet einen existierenden Prozess innerhalb seines PID-Namensraums, aber nach der Bereinigung kann diese Nummer erneut vergeben werden.

Wichtige Erkenntnisse

  • Ein Prozess ist eine Programminstanz mit Speicher-, Register- und Ein-/Ausgabezustand.
  • Gleiche virtuelle Adressen in unterschiedlichen Prozessen können unterschiedliche Daten bezeichnen.
  • Der PCB enthält den Verwaltungsdatensatz des Kernels und nicht die fortlaufend aktuellen CPU-Register.
  • Dateideskriptoren werden geerbt; 0, 1 und 2 haben konventionelle Rollen.
  • PIDs können erneut vergeben werden, nachdem ein Prozess beendet und bereinigt wurde.

2.2 Verfolge die Zustandswechsel eines Prozesses

Ein Prozess kann existieren, ohne gerade einen Befehl auszuführen. Sein Zustand zeigt dem Betriebssystem, ob er eine CPU nutzen kann oder erst auf etwas anderes warten muss. Dieser Unterschied erklärt, warum ein ausgelasteter Computer viele Prozesse haben kann, aber nur wenige in einem Augenblick ausgeführt werden.

Bestimme, was der Prozess braucht
ZustandBedeutungBeispiel
LaufendWird auf einer CPU ausgeführtEin Compiler erzeugt Befehle
BereitKann ausgeführt werden und wartet auf CPU-ZeitEine Berechnung, deren Abschnitt gerade endete
BlockiertWartet auf ein Ereignis, bevor er fortfahren kannEin Lesevorgang am Terminal wartet auf Eingabe

Ein blockierter Prozess verbraucht keine CPU-Zeit, solange er blockiert ist. Sein Gerät kann trotzdem weiterarbeiten oder das Ereignis eintreten. Ein bereiter Prozess hat bereits alles, was er braucht, außer einer CPU.

Die grundlegenden Übergänge sind bereit → laufend bei Auswahl, laufend → bereit, wenn der Prozess von der CPU genommen wird, laufend → blockiert, wenn er warten muss, und blockiert → bereit, wenn sein Ereignis eintritt. Der Abschluss eines Ereignisses macht einen Prozess ausführungsbereit; er gibt ihm nicht sofort die CPU. Zwischen ausführungsbereiten Prozessen auszuwählen, ist die Aufgabe des Schedulers.

Verfolge überlappende Arbeit

Nutze eine logische CPU und vernachlässige den Kontextwechselaufwand. Prozess A rechnet 2 ms, wartet 3 ms auf Festplatten-Ein-/Ausgabe und rechnet dann 1 ms. Prozess B braucht 4 ms CPU-Zeit. A beginnt zuerst.

IntervallCPUZustand von AZustand von B
0–2 msALaufendBereit
2–5 msBBlockiertLaufend
5–6 msBBereitLaufend
6–7 msALaufendBeendet

Bei 2 ms startet A die Ein-/Ausgabe und blockiert. B rechnet, während die Festplatte arbeitet. Bei 5 ms endet die Ein-/Ausgabe von A, aber das Betriebssystem lässt B seine vierte CPU-Millisekunde beenden, bevor es A ausführt. Das ist eine Strategieentscheidung und kein zusätzlicher Zustandsübergang direkt von blockiert zu laufend.

Die gesamte CPU-Arbeit beträgt 2 + 1 + 4 = 7 ms, und beide sind nach 7 ms fertig. Liefe A bis zum Ende, bevor B beginnt, betrüge die verstrichene Zeit 2 + 3 + 1 + 4 = 10 ms. Die CPU-Auslastung läge bei 7 / 10 = 70 %, weil keine andere Arbeit die Ein-/Ausgabewartezeit ausfüllen würde.

Lies Zustände auf einem realen System

Linux ps fasst laufend und bereit als R zusammen. Es zeigt unterbrechbaren Schlaf als S, nicht unterbrechbaren Schlaf, oft wegen Ein-/Ausgabe, als D, einen angehaltenen Prozess als T und einen beendeten Prozess, dessen Status noch nicht abgerufen wurde, als Z. Ein angehaltener Prozess wartet nicht einfach auf den Abschluss einer gewöhnlichen Ein-/Ausgabe. Ein Zombieprozess hat seine Ausführung beendet und behält nur den Datensatz, der zum Melden seines Ergebnisses nötig ist.

Ein neuer Prozess durchläuft außerdem seine Erzeugung, bevor er bereit ist. Auf einem System mit vier logischen CPUs können höchstens vier Prozesse mit je einem Thread gleichzeitig ausgeführt werden. Bereitschaftswarteschlangen ordnen ausführungsbereite Arbeit; Listen blockierter Prozesse erfassen die Ereignisse, die Prozesse benötigen. Ein Kontextwechsel sichert den Ausführungszustand eines Prozesses und stellt den eines anderen wieder her, wenn das Betriebssystem wechselt, welcher Prozess läuft.

Wichtige Erkenntnisse

  • Bereit bedeutet, auf eine CPU zu warten; blockiert bedeutet, auf ein Ereignis zu warten.
  • Der Abschluss einer Ein-/Ausgabe versetzt einen blockierten Prozess in den Zustand „bereit“.
  • Die Überlappung von Gerätewartezeiten mit nützlicher CPU-Arbeit kann die insgesamt verstrichene Zeit verkürzen.
  • Linux meldet „bereit“ und „laufend“ mit demselben Kürzel R.
  • Ein Zombieprozess ist beendet, aber sein Status wurde noch nicht abgerufen.
  • Die Anzahl logischer CPUs begrenzt, wie viele Prozesse mit je einem Thread gleichzeitig ausgeführt werden können.
3

Erzeuge und steuere Prozesse mit fork, exec und wait

Die Unix-Prozessschnittstelle trennt das Erzeugen eines Kindprozesses, das Ersetzen seines Programms und das Abrufen seines Ergebnisses. Verfolge, was jeder Aufruf ändert und was er beibehält, darunter private Variablen und den gemeinsam genutzten Zustand offener Dateien. Setze die Aufrufe dann zum Verhalten einer Shell zusammen: Vordergrundbefehle, Umleitungen, Pipelines und Hintergrundaufträge. Jeder Schritt hängt davon ab, welcher Prozess ihn ausführt.

3.1 Erzeuge mit fork einen Kindprozess

fork() erzeugt aus dem aufrufenden Elternprozess einen Kindprozess. Beide setzen ihre Ausführung unmittelbar nach dem Aufruf fort, sodass aus einem Quellcodepfad zwei Ausführungen werden. Der Rückgabewert zeigt jedem Prozess, welche Rolle er hat.

Verzweige anhand aller drei Ergebnisse
pid_t rc = fork();
if (rc < 0) {
    perror("fork");
    exit(1);
} else if (rc == 0) {
    printf("child %ld\n", (long) getpid());
} else {
    printf("parent of %ld\n", (long) rc);
}

Verwende <unistd.h> für fork und getpid, <stdio.h> für Ausgaben und <stdlib.h> für exit. Bei Erfolg erhält der Elternprozess die positive PID des Kindprozesses; der Kindprozess erhält 0. Bei einem Fehler erhält der Aufrufer -1, errno gibt den Fehler an, und es existiert kein Kindprozess.

Hat der Elternprozess die PID 5000 und der Kindprozess die PID 5001, gibt der Elternprozess „parent of 5001“ und der Kindprozess „child 5001“ aus. Es ist nicht garantiert, welcher Prozess zuerst läuft. getpid() gibt die eigene PID des Aufrufers zurück; getppid() gibt die PID seines Elternprozesses zurück. Der Kindprozess startet main nicht neu und wiederholt keine Befehle vor dem fork.

Verfolge privaten Speicher und gemeinsame Ein-/Ausgabe

Eltern- und Kindprozess beginnen mit übereinstimmenden gewöhnlichen Speicherinhalten in getrennten Adressräumen. Ist x vor dem Aufruf 10 und setzt der Kindprozess seine Kopie auf 20, bleibt die Kopie des Elternprozesses 10. Selbst die ausgegebene Adresse von x kann übereinstimmen: Diese gleichen virtuellen Adressen gehören zu unterschiedlichen Prozessadressräumen.

Copy-on-Write vermeidet das sofortige Kopieren jeder Speicherseite. Eltern- und Kindprozess nutzen zunächst schreibgeschützte physische Seiten gemeinsam. Schreibt einer auf eine private Seite, stellt der Kernel eine private Kopie bereit, bevor er den Schreibvorgang erlaubt. So bleibt das Verhalten getrennter Speicher erhalten, während der Aufwand für einen Kindprozess sinkt, der bald exec aufruft.

Dateideskriptoren verhalten sich anders. Ihre kopierten Einträge verweisen auf dieselbe zugrunde liegende offene Dateibeschreibung, die den aktuellen Dateioffset enthält. Aufeinanderfolgende Lesevorgänge über einen geerbten Deskriptor setzen diesen gemeinsamen Offset weiter. Die Deskriptortabelle wird kopiert, aber der zugrunde liegende Zustand der offenen Datei im Kernel kann gemeinsam genutzt bleiben.

Zähle nur fork-Aufrufe, die ein Prozess erreicht

Sind alle Aufrufe erfolgreich, gibt es nach fork(); fork(); vier Prozesse: Beide durch den ersten Aufruf entstandenen Prozesse führen den zweiten aus. Eine Schleife mit drei Durchläufen, die alle Prozesse erreichen, hinterlässt 23=8 Prozesse, einschließlich des ursprünglichen. Ein fork in einem Zweig, den nur der Elternprozess ausführt, wird von weniger Prozessen erreicht. Wiederholtes Verdoppeln beschreibt dann nicht mehr das ganze Programm.

Gib gepufferten Text bewusst aus

printf kann Text in einem Puffer im Benutzerraum zurücklassen. fork kopiert diesen Puffer mit dem übrigen Speicher. Wird die Ausgabe auf einen vollständig gepufferten Datenstrom umgeleitet, kann Text, der vor dem fork ausgegeben wurde, beim normalen Beenden von beiden Prozessen ausgegeben werden.

Bei printf("start\n"); fork(); gibt das üblicherweise zeilengepufferte Terminal den Zeilenumbruch vor dem fork aus. Bei der Umleitung in eine Datei kann der Text im Puffer bleiben, sodass die Datei zwei Kopien erhält. fflush(stdout) vor fork() gibt den ausstehenden Text einmal aus. Die doppelte Ausgabe entsteht durch kopierte gepufferte Daten, nicht dadurch, dass das frühere printf zweimal ausgeführt wird.

Wichtige Erkenntnisse

  • fork() gibt im Elternprozess die PID des Kindprozesses, im Kindprozess 0 und bei einem Fehler -1 zurück.
  • Beide erfolgreichen Ausführungspfade laufen direkt nach fork() weiter.
  • Änderungen am gewöhnlichen privaten Speicher des Kindprozesses ändern die Werte des Elternprozesses nicht.
  • Geerbte Deskriptoren können dieselbe offene Dateibeschreibung und denselben Offset gemeinsam nutzen.
  • Die Anzahl der Prozesse hängt davon ab, welche Prozesse den jeweiligen fork-Aufruf erreichen.
  • Gib ausstehende Ausgaben vor einem fork aus, wenn eine doppelte Ausgabe unerwünscht wäre.

3.2 Führe mit exec ein anderes Programm aus

Ein Prozess kann seine Identität behalten und zugleich das Programm wechseln, das er ausführt. Die exec-Familie nimmt diesen Austausch vor. In Verbindung mit fork kann ein Elternprozess seine Arbeit fortsetzen, während im Kindprozess ein anderes Programm gestartet wird.

Ersetze das Abbild und behalte den Prozess

Ein erfolgreiches exec ersetzt den alten Code, die statischen Daten, den Heap und den Stack. Der Kernel bereitet die Argumente und den Anfangszustand des neuen Programms vor und beginnt dann dessen Startcode. Die alte Aufrufstelle existiert nicht mehr, sodass ein erfolgreiches exec nie dorthin zurückkehrt.

PID, Elternbeziehung und aktuelles Verzeichnis bleiben erhalten. Offene Deskriptoren bleiben erhalten, ausgenommen die mit Close-on-Exec markierten. Für abgefangene Signale gilt wieder die Standardaktion; ignorierte Signale bleiben im Allgemeinen ignoriert. Die Berechtigungsdaten bleiben gewöhnlich gleich, aber die Set-User-ID- oder Set-Group-ID-Berechtigung einer ausführbaren Datei kann die effektiven Berechtigungsdaten ändern, wenn das System es erlaubt.

OperationNeuer Prozess?Hauptwirkung
fork()JaErzeugt einen Kindprozess mit kopiertem Ausführungszustand
Erfolgreiches execNeinErsetzt das Programmabbild des aufrufenden Prozesses
Erstelle einen gültigen Argumentvektor

Um wc -l notes.txt auszuführen, kann der Kindprozess Folgendes verwenden:

char *args[] = {"wc", "-l", "notes.txt", NULL};
execvp(args[0], args);
perror("execvp");
_exit(127);

Die Zeichenketten sind getrennte Argumente und keine zusammengefasste Befehlszeile. Üblicherweise bezeichnet args[0] das Programm. Das abschließende NULL ist ein Zeiger, der das Ende des Arrays markiert; es ist nicht die Zeichenkette „NULL“. Ohne einen gültigen Abschluss kann der Aufruf über das Array hinauslesen.

Die Zeilen nach execvp behandeln einen Fehler. Kann das Programm nicht gefunden werden, gibt exec -1 zurück und setzt errno, zum Beispiel auf ENOENT. _exit beendet dann den Kindprozess, ohne geerbte C-Ausgabepuffer auszugeben. Würde nach einem Fehler in die Shell-Schleife zurückgekehrt, entstünde versehentlich ein weiterer Shell-Ausführungspfad.

Lies den Variantennamen

Das v in execv bedeutet, dass Argumente als Vektor ankommen; l-Varianten erhalten eine Liste. Eine p-Variante sucht einen Programmnamen in PATH. Eine e-Variante übergibt eine ausdrücklich angegebene Umgebung. Der zugrunde liegende Linux-Systemaufruf ist execve; die Bibliothek stellt die bequem nutzbaren Varianten bereit.

execvp("wc", args) durchsucht PATH. execv("wc", args) behandelt „wc“ als relativen Pfadnamen im aktuellen Verzeichnis. Ein absoluter Pfadname wie /usr/bin/wc kann dort verwendet werden, wo diese ausführbare Datei installiert ist. Exec wertet keine Shell-Syntax aus: Eine Zeichenkette > in args ist nur ein Argument, sofern ein Programm sie nicht selbst interpretiert.

Nutze die Zeit vor dem Austausch

Im Beispiel mit Eltern-PID 6000 und Kind-PID 6001 führt der Kindprozess nun wc aus, behält aber die PID 6001. Sein Elternprozess kann auf diese PID warten. Vor exec kann der Kindprozess Deskriptoren umleiten, eine Pipe verbinden oder das Verzeichnis wechseln; das neue Programm erbt die vorbereitete Umgebung.

Diese Trennung ist zentral für die Unix-Shell. Das Windows-Verfahren CreateProcess verbindet Erzeugung und Programmladen und konfiguriert den neuen Prozess über Parameter. POSIX bietet für eine kombinierte Operation außerdem posix_spawn.

Wichtige Erkenntnisse

  • Exec ersetzt ein Programmabbild, ohne einen Prozess zu erzeugen oder seine PID zu ändern.
  • Bei Erfolg kehrt exec nie zurück; nachfolgende Anweisungen behandeln Fehler.
  • Schließe Argumentvektoren mit einem NULL-Zeiger ab.
  • p-Varianten durchsuchen PATH; eine Variante, die nur einen Pfadnamen nutzt, tut das nicht.
  • Durch das Einrichten von Deskriptoren vor exec kann ein unverändertes Programm an Umleitungen und Pipes teilnehmen.

3.3 Warte auf Kindprozesse, lies ihren Beendigungsstatus aus und beende Prozesse

Ein Elternprozess muss erfahren, ob und wie ein Kindprozess beendet wurde. Prozessende und Ergebnisabruf sind getrennte Schritte: Der Kindprozess endet, anschließend ruft sein Elternprozess den verbleibenden Datensatz des Kernels ab.

Beende einen Prozess

Die Rückkehr aus main oder der Aufruf von exit(status) beendet die Ausführung normal. exit gibt C-Ausgabepuffer aus und führt registrierte Behandlungsroutinen für die Beendigung aus. _exit(status) beendet den Prozess ohne diese Bereinigung durch die Bibliothek, was nach einem fehlgeschlagenen exec in einem mit fork erzeugten Kindprozess nützlich ist. Üblicherweise bedeutet 0 Erfolg und ein Wert ungleich null einen Fehler.

Auch ein Signal kann einen Prozess beenden. SIGTERM fordert die Beendigung an und kann behandelt werden; SIGKILL kann weder abgefangen noch ignoriert werden. SIGSTOP hält die Ausführung an und kann ebenfalls weder abgefangen noch ignoriert werden; SIGCONT setzt einen angehaltenen Prozess fort. Ein Terminal sendet normalerweise SIGINT bei Strg-C und SIGTSTP bei Strg-Z an den Vordergrundauftrag. Ein Signal ist eine Softwarebenachrichtigung an einen Prozess, kein Hardwareinterrupt.

Warte auf ein Ergebnis

wait(&status) wartet auf einen Kindprozess und gibt dessen PID zurück. waitpid(pid, &status, 0) wählt eine bestimmte positive Kind-PID aus. Ein beendeter Kindprozess, dessen Status noch nicht abgerufen wurde, kann sofort abgerufen werden. Andernfalls hält ein gewöhnliches blockierendes Warten den Elternprozess an, bis ein passendes Ergebnis verfügbar ist.

Mit WNOHANG gibt waitpid 0 zurück, wenn passende Kindprozesse existieren, aber keiner ein Ergebnis bereit hat. Ein positives Ergebnis bezeichnet den Kindprozess, dessen Status abgerufen wurde. Ein Ergebnis von -1 ist ein Fehler: ECHILD bedeutet, dass kein passender Kindprozess mehr vorhanden ist; EINTR bedeutet, dass eine Signalbehandlungsroutine das Warten unterbrochen hat.

Dekodiere den Status vor der Verwendung
int status;
pid_t r;
do {
    r = waitpid(child, &status, 0);
} while (r == -1 && errno == EINTR);
if (r == -1) {
    perror("waitpid");
} else if (WIFEXITED(status)) {
    printf("exit %d\n", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
    printf("signal %d\n", WTERMSIG(status));
}

Dies verwendet <sys/wait.h>, <errno.h> und <stdio.h>. Der gespeicherte Status kodiert sowohl die Art der Beendigung als auch ein Ergebnis. WEXITSTATUS ist aussagekräftig, nachdem WIFEXITED wahr ist; WTERMSIG folgt auf WIFSIGNALED.

Nur die unteren acht Bits eines normalen Beendigungswerts sind verfügbar. exit(300) ergibt daher 44, während exit(-1) 255 ergibt. Unter Linux erzeugt exit(3) gewöhnlich den rohen Wartestatus 768; die Ausgabe dieser rohen ganzen Zahl dekodiert den Beendigungscode nicht.

Rufe den Status von Zombieprozessen ab und erkenne verwaiste Prozesse

Ein Zombieprozess ist beendet, behält aber seine PID und sein Ergebnis, bis ein Warteaufruf sie abruft. Er führt keinen Code aus, sodass das Senden von SIGKILL ihn nicht entfernen kann. Ein langlebiger Elternprozess muss den Status jedes Kindprozesses abrufen. Um alle Kindprozesse abzurufen, warte nach jeder positiven PID weiter, versuche es bei EINTR erneut und höre bei ECHILD auf; melde andere Fehler.

Ein verwaister Prozess hat seinen Elternprozess verloren und kann weiterlaufen. Linux gibt ihm einen geeigneten Subreaper oder den Init-Prozess des Namensraums als neuen Elternprozess, der sein Ergebnis später abrufen kann. Gewöhnliche Signalberechtigungen hängen von Benutzeridentitäten und Privilegien ab. Die Ausführung als root gewährt Berechtigungen, bedeutet aber keine Ausführung im Kernelmodus.

Wichtige Erkenntnisse

  • Jeder erfolgreiche Warteaufruf ruft das Ergebnis eines Kindprozesses ab.
  • Dekodiere normale Beendigung und Beendigung durch ein Signal mit den jeweils passenden Statusmakros.
  • WNOHANG gibt 0 zurück, wenn ein passender Kindprozess existiert, aber noch kein Ergebnis bereithält.
  • Wiederhole ein bei EINTR unterbrochenes Warten; ECHILD bedeutet, dass kein passender Kindprozess mehr vorhanden ist.
  • Ein Zombieprozess ist beendet; ein verwaister Prozess hat seinen Elternprozess verloren und kann noch laufen.
  • SIGKILL und SIGSTOP können weder abgefangen noch ignoriert werden.
  • exit gibt C-Puffer aus; _exit tut das nicht.

3.4 Entwickle eine einfache Shell mit fork, exec, wait und Umleitungen

Eine Shell setzt Befehlstext in Prozesserzeugung, Deskriptoreinrichtung und Ergebnisabruf um. Sie ist ein gewöhnliches Benutzerprogramm und nutzt dieselben geschützten Betriebssystemdienste, die anderen Programmen zur Verfügung stehen.

Starte einen externen Befehl

Für wc -l notes.txt erstellt die Shell den Argumentvektor und startet den Befehl. Die zerlegten Wörter bilden einen mit einem Nullzeiger abgeschlossenen Argumentvektor args:

pid_t child = fork();
if (child == -1) {
    perror("fork");
} else if (child == 0) {
    execvp(args[0], args);
    perror("execvp");
    _exit(127);
} else {
    int status;
    pid_t result;
    do {
        result = waitpid(child, &status, 0);
    } while (result == -1 && errno == EINTR);
    if (result == -1) perror("waitpid");
    else if (WIFEXITED(status)) {
        printf("exit %d\n", WEXITSTATUS(status));
    } else if (WIFSIGNALED(status)) {
        printf("signal %d\n", WTERMSIG(status));
    }
}

Dies verwendet <unistd.h>, <sys/wait.h>, <errno.h> und <stdio.h>. Die umgebende Shell liest und zerlegt jeden Befehl, führt diesen Startpfad aus und zeigt dann ihre nächste Eingabeaufforderung an.

Nur der Kindprozess ersetzt sein Programm; die Shell kann einen Befehlsabsturz als Beendigungsergebnis abrufen. Eingebaute Befehle wie cd müssen den eigenen Zustand der Shell ändern: Eine Änderung des Verzeichnisses eines Kindprozesses ließe die Eltern-Shell in ihrem bisherigen Verzeichnis.

Leite vor exec einen Deskriptor um

Für ls -l > out.txt öffnet der Kindprozess das Ziel zum Schreiben und erzeugt oder leert es dabei. Dann lässt er die Standardausgabe auf diese Datei verweisen:

int fd = open("out.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd == -1) { perror("open"); _exit(1); }
if (dup2(fd, STDOUT_FILENO) == -1) {
    perror("dup2"); _exit(1);
}
if (fd != STDOUT_FILENO && close(fd) == -1) {
    perror("close"); _exit(1);
}

open braucht <fcntl.h>; Deskriptoraufrufe und _exit verwenden <unistd.h>. Der Erstellungsmodus wird durch die Dateierstellungsmaske des Prozesses gefiltert. Die Prüfung fd != STDOUT_FILENO hält Deskriptor 1 offen, selbst wenn open selbst 1 zurückgegeben hat. Nach erfolgreicher Einrichtung behält exec Deskriptor 1 bei, und ls schreibt normal, ohne von der Umleitung zu wissen.

Ein älterer Ansatz schließt Deskriptor 1 vor dem Öffnen der Datei: open gibt den unbenutzten Deskriptor mit der niedrigsten Nummer zurück. dup2 gibt das Ziel ausdrücklich an. Die Eingabeumleitung verbindet entsprechend eine lesbare Datei mit Deskriptor 0.

Verbinde zwei Befehle

Für ls | wc -l erzeugt pipe(fds) ein Leseende fds[0] und ein Schreibende fds[1]. Der erzeugende Kindprozess verbindet das Schreibende mit Deskriptor 1. Der verbrauchende Kindprozess verbindet das Leseende mit Deskriptor 0. Nach dem Duplizieren schließt jeder die ursprünglichen Pipe-Deskriptoren, die er nicht benötigt. Der Elternprozess schließt seine Kopien und wartet auf beide Kindprozesse.

Erzeuge beide Kindprozesse, bevor du auf einen von ihnen wartest. Ein Erzeuger kann die Pipe füllen und blockieren; der Leser muss existieren, um sie zu leeren. Der Leser erreicht das Dateiende erst, wenn alle Schreibdeskriptoren geschlossen und gepufferte Pipe-Daten verbraucht sind. Ein vom Elternprozess offengehaltenes ungenutztes Schreibende kann wc unbegrenzt warten lassen.

Halte die Eingabeaufforderung verfügbar

Bei sleep 60 & startet die Shell den Kindprozess und zeigt die nächste Eingabeaufforderung, ohne sofort zu warten. Später nutzt sie nicht blockierende Warteaufrufe oder reagiert auf SIGCHLD, um den Status beendeter Kindprozesse abzurufen.

Der Beendigungsstatus steuert auch Befehlskombinationen. make && ./app startet den zweiten Befehl nur nach Erfolg; make || echo failed startet den zweiten nur nach einem Fehler. Diese Entscheidungen nutzen das dekodierte Ergebnis, nicht den rohen kodierten Wartestatus.

Wichtige Erkenntnisse

  • Ein externer Befehl läuft in einem Kindprozess; ein eingebauter Befehl, der den Zustand ändert, läuft in der Shell.
  • Eine Umleitung ändert Deskriptoren im Kindprozess vor exec.
  • Eine Pipe verbindet die Standardausgabe eines Erzeugers mit der Standardeingabe eines Verbrauchers.
  • Starte beide Seiten, bevor du wartest, und schließe ungenutzte Pipe-Enden.
  • Das Dateiende setzt voraus, dass nach dem Verbrauch gepufferter Daten jedes Pipe-Schreibende geschlossen ist.
  • Die Ausführung im Hintergrund verschiebt das Warten; die Shell muss den Status des Kindprozesses später dennoch abrufen.
4

Erkläre Benutzermodus, Kernelmodus und Systemaufrufe

Benutzerprogramme brauchen Betriebssystemdienste, ohne die Kontrolle über den Rechner zu erhalten. Privilegienmodi der Hardware schaffen diese Grenze, und kontrollierte Eintrittspfade ermöglichen dem Kernel, auf Anfragen und Ereignisse zu reagieren. Verfolge einen Systemaufruf durch seinen Bibliothekswrapper und seine Kernelbehandlungsroutine und unterscheide dann absichtliche Traps, Befehlsausnahmen und Geräteinterrupts anhand ihrer Ursachen und möglichen Folgen.

4.1 Unterscheide Benutzermodus und Kernelmodus

Der Kernel muss Programme schnell rechnen lassen und zugleich Operationen verhindern, die den Rechner übernehmen könnten. Der Privilegienmodus der CPU ist die Hardwaregrenze, die beides ermöglicht.

Trenne Berechnung von privilegierter Steuerung

Im Benutzermodus können Befehle auf erlaubten Prozessspeicher zugreifen und gewöhnliche Berechnungen ausführen. Sie können die Hardwarekonfiguration, die andere Programme schützt, nicht beliebig ändern. Im Kernelmodus, auch Supervisormodus oder privilegierter Modus genannt, kann das Betriebssystem die geschützten Operationen zur Verwaltung des Rechners ausführen.

Zu den privilegierten Operationen gehören das Programmieren des Timers, das Deaktivieren von Interrupts, das Einrichten von Interruptbehandlungsroutinen, das Ändern von Steuerregistern der Speicherübersetzung, die direkte Steuerung geschützter Geräte und das Anhalten der CPU. Könnte jede Anwendung den Timer deaktivieren, wären kurze CPU-Abschnitte von der Kooperation dieser Anwendung abhängig.

PlattformAnwendungsmodusKernelmodus
x86-64 unter Linux oder WindowsRing 3Ring 0
AArch64EL0EL1

AArch64 bietet außerdem EL2 für Hypervisoren und EL3 für sichere Firmware. Diese Namen bezeichnen Privilegienstufen der Hardware, keine unterschiedlichen Arten von Benutzerkonten.

Tritt an einer kontrollierten Adresse ein

Benutzercode kann seine eigenen Privilegien nicht erhöhen und keine beliebige Kerneladresse zur Ausführung wählen. Der Eintritt erfolgt durch einen Systemaufruf, eine durch einen Befehl verursachte Ausnahme oder einen Hardwareinterrupt. Die CPU überträgt die Kontrolle an einen vom Betriebssystem konfigurierten Einstiegspunkt. Der Rückkehrcode des Kernels stellt den gesicherten Benutzerzustand wieder her und senkt die Privilegienstufe, um den Anwendungscode fortzusetzen.

Der Kernel schützt seine Daten auch durch Speicherberechtigungen. Ein Prozess kann Kerneladressen in seinem virtuellen Adressraum haben, ohne im Benutzermodus die Berechtigung zum Zugriff darauf zu besitzen. Die Kenntnis einer Adresse gewährt keinen Zugriff auf die Bytes dort.

Betrachte eine x86-64-Linux-Anwendung, die hlt ausführt, einen Befehl, der eine CPU bis zu einem Ereignis anhalten soll. Im Benutzermodus verursacht der Befehl eine Ausnahme. Linux sendet normalerweise SIGSEGV, dessen Standardaktion diesen Prozess beendet. Die Anwendung hält nicht den ganzen Computer an; die Schutzgrenze macht aus der versuchten privilegierten Operation ein Ereignis, das der Kernel behandelt.

Trenne Identität und Modus

Ein Root- oder Administratorprozess ist weiterhin ein Programm im Benutzermodus, während seine eigenen Befehle laufen. Seine Benutzeridentität gibt ihm umfassendere Berechtigungen, Dienste anzufordern. Er tritt weiterhin über kontrollierte Pfade in den Kernel ein, und der Kernel prüft die Anfrage.

Ein privilegiertes Wartungsprogramm darf zum Beispiel möglicherweise eine geschützte Datei öffnen. Das Programm fragt über einen Systemaufruf an; es wird nicht allein deshalb zu Kernelcode, weil die Anfrage erlaubt wird. Umgekehrt wird Kernelcode, der ein read eines gewöhnlichen Benutzers bedient, im Kernelmodus ausgeführt, obwohl der Aufrufer keine Administratoridentität hat.

Auf einer logischen CPU ist dies eine Abfolge: Benutzerbefehle, Kerneleintritt, Kernelarbeit, Rückkehr zu Benutzerbefehlen. Ein Programm und eine zweite Betriebssystemaufgabe werden nicht gleichzeitig auf derselben CPU ausgeführt. Getrennte logische CPUs können im selben Augenblick unterschiedliche Modi ausführen.

Wichtige Erkenntnisse

  • Der Benutzermodus erlaubt gewöhnliche Berechnungen und schränkt zugleich Operationen zur Steuerung des Rechners ein.
  • Der Kerneleintritt nutzt vom Betriebssystem konfigurierte Einstiegspunkte.
  • Root ist eine Benutzeridentität; der Kernelmodus ist ein Privilegienzustand der CPU.
  • Ein verbotener Benutzerbefehl löst eine Ausnahme aus, sodass der Kernel reagieren kann.

4.2 Erkläre Schritt für Schritt den Ablauf eines Systemaufrufs

Ein C-Programm scheint häufig eine gewöhnliche Funktion aufzurufen, wenn es einen Betriebssystemdienst braucht. Unterhalb eines Systemaufruf-Wrappers ermöglicht ein kontrollierter CPU-Übergang dem Kernelcode, diese Anfrage zu prüfen und auszuführen.

Unterscheide einen Bibliotheksaufruf von einer Kernelanfrage

Die C-Bibliothek implementiert strlen ohne Systemaufruf im Benutzerraum. printf formatiert Daten und kann sie puffern; ein späteres write überträgt Bytes über den Kernel. Ein schlanker read-Wrapper ordnet Argumente nach der Systemaufrufkonvention der Plattform an und führt den Eintrittsbefehl aus.

POSIX beschreibt Schnittstellen für Unix-ähnliche Systeme. Linux implementiert viele Dienste, die grob in Prozesssteuerung, Datei- und Geräteoperationen, Information, Kommunikation und Schutz gegliedert werden. Eine Systemaufrufnummer wählt einen Dienst aus der Zuordnungstabelle des Kernels. Die Nummern sind plattformabhängig: Linux auf x86-64 verwendet 1 für write und 39 für getpid; AArch64 verwendet 64 für write.

Verfolge eine Schreibanfrage

Verfolge bei write(1, "hi\n", 3) unter Linux auf x86-64 die Anfrage der Reihe nach:

  1. Der Wrapper legt die Dienstnummer 1 in rax, Deskriptor 1 in rdi, die Pufferadresse in rsi und die Anzahl 3 in rdx ab.
  2. syscall sichert die Rücksprungadresse und die Flags in Registern, wechselt in den Kernelmodus und springt zum konfigurierten Einstiegspunkt. Dabei wird der Stack nicht automatisch gewechselt.
  3. Der Kerneleinstiegscode richtet den Kernelstack des Prozesses ein und sichert den übrigen Benutzerzustand.
  4. Die Zuordnung im Kernel wählt write; die Implementierung prüft den Deskriptor, den Zugriff auf den Benutzerpuffer und die Anfrage, bevor sie Bytes an die Ausgaberessource weiterleitet.
  5. Bei einem vollständig erfolgreichen Schreibvorgang beträgt das Ergebnis 3 Bytes. Der Rückkehrcode des Kernels stellt den Benutzerzustand wieder her und setzt den Wrapper fort, der zum Aufrufer zurückkehrt.

Deskriptor 1 kann ein Terminal, eine Datei oder eine Pipe bezeichnen. Die Nummer allein bestimmt das Ausgabegerät nicht. Ein Schreibvorgang kann auch erfolgreich eine kleinere Byteanzahl zurückgeben; Aufrufer, die jedes Byte übertragen müssen, verfolgen die tatsächlich geschriebene Menge.

Prüfe Fehler an der Schnittstelle

Der Kernel behandelt Benutzerzeiger und andere Argumente als nicht vertrauenswürdig. Eine Anfrage, aus einem nicht zugänglichen Puffer zu kopieren, kann mit EFAULT fehlschlagen; kontrolliertes Kopieren verhindert, dass der Aufrufer Zugriff auf Kernelspeicher erhält.

Unter Linux ist ein Kernelfehler ein negativer Fehlercode. Der übliche C-Wrapper meldet -1 und setzt errno. Schlägt zum Beispiel open("missing.txt", O_RDONLY) fehl, weil die Datei nicht existiert, ist errno gleich ENOENT. perror("missing.txt") kann „missing.txt: No such file or directory“ anzeigen. Prüfe errno nach einem gemeldeten Fehler, nicht als primären Erfolgstest: Ein erfolgreicher Aufruf muss einen früheren Fehler nicht zurücksetzen.

Berücksichtige die Zeit im Kernel

Ein Aufruf, der sofort abgeschlossen werden kann, kehrt möglicherweise zum selben Prozess zurück. Ein blockierender Lesevorgang am Terminal kann diesen Prozess dagegen in den Zustand „blockiert“ versetzen und einen anderen laufen lassen. Das Eingabeereignis macht den Leser bereit; sobald er eingeplant wird, schließt er den Rückweg ab.

Der Kerneleintritt verursacht gegenüber einem normalen Funktionsaufruf zusätzliche Arbeit: Zustand sichern, Privilegien ändern, Argumente prüfen und die Ausführung wiederherstellen. Pufferung durch die Bibliothek kann kleine Ausgaben zu weniger Schreibvorgängen zusammenfassen. Unter Linux führt strace ls den Befehl ls aus und meldet dabei dessen Systemaufrufe, Argumente und Ergebnisse.

Wichtige Erkenntnisse

  • Eine Bibliotheksfunktion kann ihre gesamte Arbeit im Benutzerraum erledigen oder Systemaufrufe ausführen.
  • Eine Dienstnummer wählt eine Kerneloperation; der Aufrufer wählt keine beliebige Kerneladresse.
  • Kernelcode prüft Deskriptoren, Zeiger und andere Argumente.
  • Erkenne einen Fehler am Rückgabewert und prüfe dann errno.
  • Ein blockierender Systemaufruf kann einen Kontextwechsel verursachen; der Kerneleintritt allein muss das nicht.
  • Systemaufrufnummern und Registerkonventionen hängen vom Betriebssystem und von der Architektur ab.

4.3 Unterscheide Traps, Ausnahmen und Interrupts

Ein Kerneleintritt kann eine Dienstanfrage, ein Befehlsproblem oder ein Geräteereignis bedeuten. Wenn du die Ursache einordnest, kannst du vorhersagen, was das Betriebssystem tun wird und ob derselbe Befehl erneut ausgeführt werden soll.

Frage nach der Ursache des Ereignisses
EreignisUrsacheZeitpunkt in Bezug auf den aktuellen Befehl
Systemaufruf-TrapDas Programm fordert absichtlich einen Dienst anSynchron
Ausnahme oder FehlerDer aktuelle Befehl braucht eine besondere BehandlungSynchron
HardwareinterruptEin Timer oder Gerät signalisiert der CPU ein EreignisAsynchron

Synchron bedeutet, dass das Ereignis an den ausgeführten Befehl gebunden ist. Es bedeutet nicht, dass dieselbe Eingabe bei jedem Durchlauf genau dieselben Ausnahmen verursacht. Ein Ladebefehl kann eine Ausnahme auslösen, wenn seine Seite fehlt, und später erfolgreich sein, wenn diese Seite im Speicher liegt. Asynchron bedeutet, dass die Ursache des Ereignisses vom aktuellen Befehl unabhängig ist, etwa wenn eine Festplatte eine frühere Arbeit abschließt.

CPU-Handbücher verwenden diese Begriffe unterschiedlich. Achte auf die Ursache: absichtliche Dienstanfrage, durch einen Befehl verursachtes Ereignis oder Hardwarebenachrichtigung.

Leite an die richtige Behandlungsroutine weiter

Beim Systemstart richtet das Betriebssystem geschützte Adressen für Behandlungsroutinen ein. Eine Trap- oder Interrupttabelle ordnet Ereignisarten Kernelroutinen zu. Auf x86-64 dient die Interruptdeskriptortabelle (IDT) für Interrupts und Ausnahmen; syscall nutzt eine Einstiegsadresse in einem speziellen Prozessorregister. Nur privilegierter Code kann diese Ziele konfigurieren.

Bei einem Interrupt aus dem Benutzermodus sichert die x86-64-Hardware einen Rückkehrrahmen auf einem Kernelstack. Der Einstiegscode sichert zusätzliche Register, bevor er die Arbeit erledigt. Der syscall-Pfad sichert seinen anfänglichen Rückkehrzustand in Registern und ist darauf angewiesen, dass der Einstiegscode den Kernelstack einrichtet. Alle Eintrittspfade als gleiche Hardwareaktionen zu behandeln, verdeckt einen wichtigen Implementierungsunterschied.

Wähle die Folge

Eine gültige Seite, die nicht im Speicher liegt, kann geladen werden. Danach wird der Befehl, der die Ausnahme auslöste, erneut versucht. Ein Zugriff auf Speicher, den der Prozess nicht nutzen darf, führt unter Linux normalerweise dazu, dass SIGSEGV gesendet wird. Eine Ausnahme ist daher nicht immer fatal und auch nicht immer eine Aufforderung, den aktuellen Befehl zu überspringen.

Bei einem tatsächlichen x86-64-Ganzzahlbefehl idiv mit Divisor null löst die CPU eine Division-Error-Ausnahme aus. Linux übermittelt normalerweise SIGFPE; die bekannte Meldung erwähnt Gleitkommazahlen, selbst bei diesem Ganzzahlbefehl. Die Ganzzahldivision von AArch64 liefert dagegen bei einem Divisor von null das Ergebnis null. Eine Division durch null in C hat undefiniertes Verhalten, sodass portabler C-Code sich auf keines der beiden Maschinenergebnisse verlassen kann.

Wenn ein Festplattenlesevorgang endet, vermerkt die Interruptbehandlungsroutine den Abschluss und macht den wartenden Prozess bereit. Dieser Prozess kann später laufen. Ein Timerinterrupt gibt dem Betriebssystem die Gelegenheit, die aktuelle CPU-Belegung zu überdenken, aber die Behandlungsroutine kann auch zu demselben Prozess zurückkehren.

Unterscheide ein Signal von einem Interrupt

Ein Hardwareinterrupt benachrichtigt die CPU; ein Signal benachrichtigt einen Prozess über das Betriebssystem. Eine Ausnahme kann den Kernel veranlassen, ein Signal zu senden, und ein Prozess kann über kill ein Signal für einen anderen anfordern. Normale Geräteabschlüsse sind Interrupts und bedeuten keine Programmfehler.

Kernelcode muss auch gemeinsam genutzte Daten schützen, während Ereignisse auftreten. Er kann Interrupts auf einer CPU kurz deaktivieren und bei Bedarf Sperren nutzen. Lange Zeiträume mit deaktivierten Interrupts verzögern die Gerätebehandlung; das Deaktivieren lokaler Interrupts allein hindert eine andere CPU nicht am Zugriff auf gemeinsame Daten.

Wichtige Erkenntnisse

  • Traps und Ausnahmen entstehen durch den aktuellen Befehl; Hardwareinterrupts haben eine externe Ursache.
  • Eine behebbare Ausnahme kann behoben und ihr Befehl erneut ausgeführt werden.
  • Geschützte Eintrittstabellen verhindern, dass Benutzerprogramme Kernelbehandlungsroutinen ersetzen.
  • Ein Signal richtet sich an einen Prozess; ein Hardwareinterrupt führt zur Behandlung durch die CPU.
  • Ein Geräteabschluss macht einen blockierten Prozess bereit, und ein Timerinterrupt muss keinen Prozesswechsel auslösen.
5

Erkläre, wie das Betriebssystem die CPU durch eingeschränkte direkte Ausführung aufteilt

Schnelle Ausführung und Kontrolle durch das Betriebssystem müssen zusammenwirken. Die eingeschränkte direkte Ausführung lässt gewöhnliche Befehle auf der Hardware laufen, während Modi, geschützte Einstiegspunkte und ein Timer dafür sorgen, dass der Kernel die Kontrolle behält. Verfolge, was geschieht, wenn das Betriebssystem zum selben Prozess zurückkehrt und wenn es einen anderen fortsetzt. Der gesicherte Zustand und der Wechselaufwand erklären, wie kurze CPU-Abschnitte interaktives Arbeiten unterstützen können.

5.1 Erkläre die eingeschränkte direkte Ausführung

Jeden Anwendungsbefehl in einem Softwaresimulator auszuführen, würde die gemeinsame CPU-Nutzung aufwendig machen. Einer Anwendung unbegrenzte Kontrolle über den Rechner zu geben, würde die gemeinsame Nutzung unsicher machen. Die eingeschränkte direkte Ausführung (LDE) nutzt gewöhnliche Hardwareausführung mit einem geschützten Rückweg zum Betriebssystem.

Löse zwei unterschiedliche Steuerungsprobleme

Direkte Ausführung bedeutet, dass die gewöhnlichen Befehle eines Prozesses auf der CPU selbst ausgeführt werden. Das Betriebssystem untersucht und genehmigt nicht jeden arithmetischen Befehl. Zwei Probleme bleiben: Operationen einzuschränken, die den Rechner beschädigen könnten, und die Kontrolle zurückzugewinnen, wenn ein Programm nicht freiwillig anhält.

Der Benutzermodus löst das Problem der Einschränkung. Systemaufruf-Traps erlauben einem Programm, Dienste anzufordern, ohne beliebigen privilegierten Code zu wählen. Ein Hardwaretimer löst das Steuerungsproblem: Er kann eine Anwendung unterbrechen, die überhaupt keine Aufrufe ausführt. Zusammen unterstützen diese Mechanismen die CPU-Virtualisierung mit geringem Verwaltungsaufwand.

Bereite alles vor, bevor Anwendungen laufen

Beim Systemstart konfiguriert der Kernel Ereignisbehandlungsroutinen und initialisiert den Timer. Diese Einrichtungsoperationen sind privilegiert. Spätere Benutzerprogramme können weder die Behandlungsroutinen umlenken noch den Timer deaktivieren, um ihre CPU-Abschnitte zu behalten.

Um einen Prozess zu starten, erzeugt das Betriebssystem seinen Verwaltungsdatensatz und sein Speicherabbild, bereitet Startargumente und einen Benutzerstack vor und legt anfängliche Registerwerte fest. Es richtet einen anfänglich gesicherten Kontext so ein, dass der normale Rückweg vom Kernel zum Benutzer das Programm starten kann. Vom Konzept her beginnt der Prozess mit der Rückkehr aus einem Trap, den er nie ausgelöst hat.

Der genaue Aufbau des gesicherten Rahmens hängt von CPU und Betriebssystem ab. Die nützliche Entwurfsidee ist die Wiederverwendung: Derselbe Wiederherstellungsmechanismus kann einen angehaltenen Prozess fortsetzen oder einen neu vorbereiteten starten.

Kehre freiwillig zum Kernel zurück

Bei einem Systemaufruf sichert die Hardware den anfänglichen Rückkehrzustand und überträgt die Kontrolle. Der Kerneleinstiegscode sichert die zusätzlichen Register auf dem Kernelstack des Prozesses. Nach Abschluss einer nicht blockierenden Anfrage stellt der Kernel den Prozess wieder her und kehrt in den Benutzermodus zurück. Blockiert die Anfrage, kann zunächst ein anderer bereiter Prozess laufen.

Wenn main zurückkehrt, fordert die Beendigungsverarbeitung der Laufzeitumgebung schließlich den Kernel auf, den Prozess zu beenden. Sein Speicher kann freigegeben werden, während sein Beendigungsdatensatz erhalten bleibt, bis ein Elternprozess ihn abruft.

Kehre ohne Kooperation zurück

Ein kooperatives System ist darauf angewiesen, dass Programme Aufrufe ausführen, die CPU freiwillig abgeben oder Ausnahmen verursachen. Eine Endlosschleife, die nichts davon tut, kann eine CPU für sich beanspruchen. Das klassische Mac OS veranschaulicht diese Schwäche.

Ein nicht kooperativer Entwurf nutzt einen Timerinterrupt, um den Kerneleintritt zu erzwingen. Betrachte ein Lehrmodell mit einer logischen CPU und einem periodischen Timer alle 4 ms. Der Timer erzeugt 1000 / 4 = 250 Taktimpulse pro Sekunde. Ein Benutzerprozess, der in while (1) { } feststeckt, wird beim nächsten Taktimpuls unterbrochen. Der Kernel kann dann einen anderen bereiten Prozess ausführen; die Schleife muss nicht erst einen Systemaufruf erreichen.

Der Taktimpuls ist eine Gelegenheit zu handeln, keine Pflicht zum Prozesswechsel. Der Scheduler kann den unterbrochenen Prozess weiterlaufen lassen. Timerbehandlung und Wiederherstellung des gesicherten Zustands sind Mechanismen; die Wahl des Schedulers ist eine Strategie. Die tatsächliche Antwortzeit hängt auch von der Kernelarbeit und davon ab, wann Interrupts behandelt werden können. Die Timerperiode allein garantiert daher keine bestimmte Antwortzeit einer Anwendung.

Wichtige Erkenntnisse

  • LDE führt gewöhnliche Benutzerbefehle direkt mit durch Hardware durchgesetzten Einschränkungen aus.
  • Der Benutzermodus schränkt Operationen ein; Systemaufrufe stellen kontrollierte privilegierte Dienste bereit.
  • Ein Timer lässt das Betriebssystem ohne Kooperation des Benutzercodes die Kontrolle zurückgewinnen.
  • Die Einrichtung beim Systemstart schützt die Adressen der Behandlungsroutinen und die Timerkonfiguration.
  • Ein Timerimpuls gibt dem Scheduler eine Wahl; er verlangt keinen Prozesswechsel.

5.2 Verfolge einen Kontextwechsel und schätze seinen Aufwand ab

Der Eintritt in den Kernelmodus ändert nicht zwangsläufig den laufenden Prozess. Ein Kontextwechsel findet statt, wenn der Kernel den Ausführungszustand eines Prozesses sichert und einen anderen fortsetzt. Dazu muss sowohl der Benutzerzustand gesichert als auch der eigene Ausführungskontext des Kernels gewechselt werden.

Verfolge den Wechsel von A zu B

Verfolge bei einem timergesteuerten Wechsel auf x86-64 die Ebenen:

  1. A führt Benutzerbefehle aus. Der Timer unterbricht den Prozess.
  2. Die Hardware wechselt in den Kernelmodus und sichert den Interruptrückkehrrahmen von A auf dem Kernelstack von A. Der Einstiegscode sichert dort die übrigen Benutzerregister von A.
  3. Der Scheduler wählt B. Die Wechselroutine sichert den Kernelausführungskontext von A und stellt den gesicherten Kernelkontext von B wieder her, einschließlich dessen Kernelstapelzeigers.
  4. Der Kernel wechselt bei Bedarf zum Adressraumkontext von B. Er läuft nun im Auftrag von B und nutzt dessen Kernelstack.
  5. Der Kernelaustrittscode stellt die Benutzerregister von B wieder her. Der Rückkehrbefehl stellt den Rückkehrzustand von B wieder her und wechselt in den Benutzermodus. B läuft dort weiter, wo der Prozess zuvor angehalten wurde.

Die erste Sicherung bewahrt die unterbrochene Benutzerausführung von A. Die zweite bewahrt die Stelle des Kernels bei der Behandlung von A. Es handelt sich nicht um zwei identische vollständige Kopien desselben Registersatzes. Ein PCB oder eine zugehörige Kontextstruktur und der prozessbezogene Kernelstack wirken zusammen, um den Zustand zu sichern.

Erkenne das Ereignis und die Entscheidung

Ein blockierender Aufruf, ein Prozessende oder eine Timerentscheidung kann zu einem Wechsel führen. Ein Geräteinterrupt kann einen anderen Prozess bereit machen, den das Scheduling dann auswählen kann. Der gesamte Wechselmechanismus läuft im Kernel; eine Anwendung sichert ihre eigenen Register nicht, um sich freiwillig unterbrechen zu lassen.

SituationModusübergang?Prozesswechsel?
Nicht blockierender Dienst kehrt zum Aufrufer zurückBenutzer → Kernel → BenutzerNein
Timer wird behandelt, derselbe Prozess läuft weiterBenutzer → Kernel → BenutzerNein
A blockiert und B wird fortgesetztKerneleintritt, dann Rückweg von BJa

Ein Kontextwechsel sichert den für die Fortsetzung nötigen Ausführungszustand. Zeit vergeht und die Außenwelt kann sich ändern: Gemeinsam genutzte Dateioffsets und externe Eingaben werden nicht eingefroren, während ein Prozess pausiert.

Berücksichtige direkten und indirekten Aufwand

Zur direkten Arbeit gehören das Ausführen des Schedulers, das Sichern und Wiederherstellen von Registern und die Auswahl eines Adressraums. Übersetzungscaches müssen möglicherweise angepasst werden; gekennzeichnete Übersetzungen können verhindern, dass alle alten Einträge verworfen werden. Indirekter Aufwand folgt, wenn B CPU-Caches oder zwischengespeicherte Adressübersetzungen neu füllen muss, die A verdrängt hat. Der Aufwand hängt von Hardware und Arbeitslast ab. Verwende daher bei der Berechnung des Verwaltungsaufwands gemessene oder angegebene Werte.

Angenommen, ein Wechsel kostet einschließlich seiner relevanten Auswirkungen 5 µs und nutzbare CPU-Abschnitte dauern 10 ms. Rechne 10 ms in 10.000 µs um. Der Wechselanteil beträgt 5 / (10.000 + 5), etwa 0,05 %. Bei nutzbaren Abschnitten von 1 ms sind es 5 / 1005, etwa 0,50 %; bei 0,1 ms sind es 5 / 105, etwa 4,8 %.

Der Nenner umfasst die nutzbare Arbeit plus die Wechselzeit für einen wiederholten Zyklus. Ein kürzerer Abschnitt kann die Möglichkeiten für interaktive Arbeit verbessern, beansprucht aber einen größeren Anteil für wiederholte Wechsel. Timer und Wechselcode ermöglichen kurze Abschnitte; die Schedulingstrategie muss diesen Aufwand gegen die Reaktionsfähigkeit abwägen.

Wichtige Erkenntnisse

  • Nach einem Kerneleintritt kann die Ausführung ohne Kontextwechsel zum selben Prozess zurückkehren.
  • Das Sichern des Benutzerzustands beim Eintritt und des Kernelkontexts während eines Wechsels dient unterschiedlichen Zwecken.
  • Die Fortsetzung von B braucht den Kernelstack, die gesicherten Register und den Adressraumkontext von B.
  • Der Wechselaufwand umfasst direkte Verwaltungsarbeit und das spätere Neufüllen von Caches oder Adressübersetzungen.
  • Der Verwaltungsanteil ist der Wechselaufwand geteilt durch die Summe aus nutzbarem Abschnitt und Wechselaufwand.
  • Kürzere Abschnitte erhöhen bei festen Kosten pro Wechsel den Wechselanteil.

Prozesse und Systemaufrufe: Beispiele

5 Interaktive Lektionen

1

Erkenne den Beitrag des Betriebssystems

12 Schritte
Verfolge gemeinsam genutzte Ressourcen von einer Anwendungsanfrage bis zu Kerneldiensten und vergleiche dann die Entwürfe, die die gemeinsame Nutzung ermöglichen.
  1. 1
    EinführungBeginne mit mehreren Programmen

    Stell dir einen Laptop vor, der ein Projekt kompiliert, während er Musik abspielt und Netzwerkdaten empfängt. Jede Anwendung braucht CPU-Zeit, Speicher und Ein-/Ausgabe, aber keine kontrolliert den ganzen Rechner.

    Verfolge, wer diese Ressourcen bereitstellt, wie mehrere Aufträge Fortschritte machen und wo die privilegierten Dienste laufen. Die wiederkehrende Frage ist konkret: Welche Arbeit fordert die Anwendung an und welche muss das Betriebssystem verwalten?

  2. 2
    ErklärungVerbirg die Gerätedetails

    Das Betriebssystem bietet Abstraktionen: nützliche Schnittstellen, die darunterliegende Mechanismen verbergen. Ein Programm öffnet eine Datei und liest Bytes, ohne Befehle für jedes Speichergerät zu implementieren.

    Ein Systemaufruf fordert über eine kontrollierte Schnittstelle einen Kerneldienst an. Der Kernel prüft und erledigt die geschützte Arbeit. Die Anwendung entscheidet weiterhin, welche Daten sie möchte; sie erhält keine Berechtigung, das Gerät direkt anzusteuern.

  3. 3
    BeispielVerfolge eine Ausgabedatei des Compilers

    Ein Compiler erzeugt Bytes für eine ausführbare Datei und fordert einen Schreibvorgang an. Der Kernel prüft die Ausgaberessource und behandelt die Speicherarbeit hinter der Dateischnittstelle.

    Gleichzeitig verwaltet das Betriebssystem den Speicher des Compilers und teilt CPU-Zeit mit dem Musikplayer. Eine Handlung, das Speichern einer Datei, liegt somit innerhalb mehrerer Aufgaben des Betriebssystems: Abstraktion, Ressourcenverwaltung und Schutz der anderen Programme.

  4. 4
    KernaussageGib jedem Prozess einen nutzbaren Rechner

    Virtualisierung stellt eine physische Ressource über eine nützliche private Sicht dar. Ein Prozess kann Fortschritte machen, als hätte er einen eigenen Prozessor, obwohl seine Befehle sich mit denen anderer Prozesse abwechseln.

    Auch seine Speichersicht ist privat: Eine von ihm genutzte Adresse muss nicht dieselben physischen Bytes wie in einem anderen Prozess bezeichnen. CPU- und Speichervirtualisierung geben Programmen bequeme Modelle, während das Betriebssystem die tatsächlich gemeinsam genutzte Hardware verwaltet.

  5. 5
    HerausforderungFinde den privilegierten Teil

    Auf einem Computer ist kein grafischer Desktop installiert, aber er führt Dienste aus, speichert Dateien und teilt CPU-Zeit auf. Hat er den wesentlichen Kern des Betriebssystems verloren? Erkläre.

    ---reveal---

    Nein. Der Kernel verwaltet weiterhin Prozesse, Speicher und Gerätedienste. Ein grafischer Desktop ist ein Benutzerprogramm, das auf diesen Diensten aufbaut. Sein Fehlen ändert die Oberfläche, die ein Mensch sieht, nicht aber den Bedarf an der Ressourcenverwaltung des Kernels.

  6. 6
    ErklärungTrenne die Operation von der Wahl

    Ein Mechanismus ermöglicht eine Handlung. Das Sichern der Register eines Prozesses und das Wiederherstellen der Register eines anderen stellt den Mechanismus für einen Wechsel bereit.

    Eine Strategie wählt, wie er genutzt wird, indem sie zum Beispiel entscheidet, welcher bereite Prozess den nächsten CPU-Abschnitt erhält. Diese Unterscheidung ist bei einer Entwurfsänderung wichtig: Eine neue Entscheidungsregel kann denselben zuverlässigen Wechselcode wiederverwenden. Effiziente Mechanismen allein entscheiden nicht, was fair ist.

  7. 7
    ErklärungFülle eine sonst ungenutzte Wartezeit

    Ein Stapelverarbeitungssystem kann die CPU ungenutzt lassen, während sein aktueller Auftrag auf Ein-/Ausgabe wartet. Der Mehrprogrammbetrieb hält andere Aufträge verfügbar und führt während dieser Wartezeit einen davon aus.

    Der wartende Auftrag rechnet nicht und das Gerät ist nicht schneller geworden. Der Gewinn entsteht durch die Überlappung unabhängiger Arbeit: Ein Prozess wartet, während ein anderer die CPU nutzt. Mehrere im Speicher befindliche Programme brauchen außerdem Schutz, damit keines den Speicher eines anderen überschreiben kann.

  8. 8
    BeispielBerechne den Auslastungsgewinn

    Ein Auftrag rechnet 20 ms und wartet dann 80 ms auf Ein-/Ausgabe. Allein enthält ein Zyklus von 100 ms nur 20 ms CPU-Arbeit: Die Auslastung beträgt 20 %.

    Mit einem zweiten solchen Auftrag und geeigneter Überlappung können weitere 20 ms Berechnung sonst ungenutzte Zeit füllen. Das ergibt in diesem Beispiel bis zu 40 %. Die Verbesserung nutzt Wartezeit produktiv; zusätzliche Aufträge können die CPU-Auslastung nicht über 100 % erhöhen.

  9. 9
    VerbindungGib interaktiver Arbeit kurze Abschnitte

    Das Zeitteilverfahren gibt Prozessen kurze CPU-Abschnitte, sodass interaktive Arbeit nicht auf einen ganzen Stapelauftrag warten muss. Das ist selbst auf einer logischen CPU Nebenläufigkeit: Mehrere Aktivitäten kommen durch Verzahnung voran.

    Parallelität braucht tatsächliche gleichzeitige Ausführung auf getrennten Verarbeitungsressourcen. Arbeits- und Festplattenspeicher veranschaulichen eine andere Aufteilung, das Raumteilverfahren, bei dem verschiedene Benutzer gleichzeitig getrennte Teile belegen.

  10. 10
    ErklärungWähle eine Dienstgrenze

    Ein monolithischer Kernel wie Linux führt die meisten Dienste gemeinsam und privilegiert aus. Direkte Aufrufe sind effizient, aber ein fehlerhafter Treiber kann den Kernelzustand beschädigen.

    Ein Mikrokernel wie seL4 belässt mehr Dienste in getrennten Benutzerprozessen. Isolation kann den Ausfall eines Dienstes eingrenzen, verursacht aber Aufwand für Kommunikation und Moduswechsel. Windows NT und XNU verbinden Ansätze. Die Grenze bestimmt, welche Fehler sich ausbreiten und welche Dienste neu gestartet werden können.

  11. 11
    HerausforderungVerbinde Aufteilung und Schutz

    Ein System mit einer CPU führt mehrere Aufträge aus und wechselt zu einem anderen, sobald einer auf Festplatten-Ein-/Ausgabe wartet. Ein Vorschlag würde jedem Auftrag erlauben, jede Speicheradresse zu überschreiben. Erkläre den Auslastungsvorteil und den Fehler dieses Vorschlags.

    ---reveal---

    Der Mehrprogrammbetrieb füllt Ein-/Ausgabewartezeiten mit Berechnungen eines anderen Auftrags. Uneingeschränktes Schreiben würde einem Auftrag erlauben, einen anderen oder das Betriebssystem zu beschädigen. Überlappende Arbeit verbessert die Ressourcennutzung, aber gemeinsame Nutzung braucht weiterhin durchgesetzten Speicher- und Privilegienschutz.

  12. 12
    ZusammenfassungBetrachte einen Entwurf mit drei Fragen

    Frag dich bei einem Betriebssystementwurf, welche Ressource aufgeteilt wird, welche Schnittstelle ein Programm sieht und welche Grenze andere Arbeit schützt.

    Diese Fragen verbinden CPU-Virtualisierung mit Prozessabschnitten, Dateien mit verborgenen Gerätedetails und Kernelorganisation mit Fehlerisolation. Mechanismen führen geschützte Operationen aus; Strategien entscheiden, wie ihre Nutzung aufgeteilt wird. Nebenläufigkeit kann durch Verzahnung auf einer CPU entstehen.

2

Verfolge einen Prozess bei CPU- und Ein-/Ausgabearbeit

13 Schritte
Lies einen Prozessdatensatz und erstelle dann einen Zeitablauf, der Ausführung, Warten auf eine CPU und Warten auf ein Ereignis trennt.

Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.

  1. 1
    EinführungVerfolge eine aktive Instanz
  2. 2
    ErklärungMache aus einer Datei einen Prozess
  3. 3
    ErklärungSichere den nächsten Befehl
  4. 4
    BeispielLies eine Prozessliste
  5. 5
    HerausforderungDeute gleiche Adressen
  6. 6
    TippLies Deskriptoren als geerbte Ressourcen
  7. 7
    ErklärungFrag dich, was fehlt
  8. 8
    BeispielBeginne den Zeitablauf
  9. 9
    BeispielVervollständige den Zeitablauf
  10. 10
    HerausforderungÄndere die Entscheidung für einen bereiten Prozess
  11. 11
    VerbindungErkenne reale Zustandsnamen
  12. 12
    HerausforderungVerbinde Datensätze mit CPU-Kapazität
  13. 13
    ZusammenfassungLies den Zustand als Ausführungsbereitschaft
3

Setze einen Befehl aus Prozessaufrufen zusammen

23 Schritte
Verfolge einen Kindprozess von fork über Programmersetzung, Ergebnisabruf und Umleitung bis zu einer verbundenen Pipeline.

Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.

  1. 1
    EinführungGib jedem Aufruf eine Aufgabe
  2. 2
    ErklärungTeile eine Ausführung in zwei
  3. 3
    BeispielVerfolge einen privaten Wert
  4. 4
    BeispielZähle einen Zweig, nicht die Quellcodeaufrufe
  5. 5
    HerausforderungZähle die Ausgabepfade
  6. 6
    TippPrüfe, was kopiert wurde
  7. 7
    ErklärungErsetze das Programm des Kindprozesses
  8. 8
    BeispielÜbergib getrennte Argumentzeichenketten
  9. 9
    HerausforderungSage einen erfolgreichen Austausch voraus
  10. 10
    KernaussageNutze die Zeit vor exec
  11. 11
    ErklärungRufe das Ergebnis eines Kindprozesses ab
  12. 12
    BeispielDekodiere eine normale Beendigung
  13. 13
    ErklärungLass keine Ergebnisse unabgerufen
  14. 14
    HerausforderungBehandle eine Unterbrechung richtig
  15. 15
    TippWähle eine Beendigungsanfrage
  16. 16
    ErklärungKehre zur Vordergrund-Eingabeaufforderung zurück
  17. 17
    BeispielLeite die Ausgabe vor dem Austausch um
  18. 18
    HerausforderungErhalte das Ausgabeziel
  19. 19
    BeispielVerbinde die beiden Pipe-Enden
  20. 20
    KernaussageStarte vor dem Warten
  21. 21
    VerbindungBehalte Hintergrundarbeit im Blick
  22. 22
    HerausforderungVerfolge eine leere Pipeline
  23. 23
    ZusammenfassungVerfolge Identität, Abbild und Ergebnis
4

Verfolge eine Anfrage über die Privilegiengrenze

15 Schritte
Verfolge eine Dienstanfrage in den Kernel und vergleiche sie dann mit Befehlsausnahmen und externen Geräteereignissen.

Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.

  1. 1
    EinführungFrag dich, warum der Kernel betreten wurde
  2. 2
    ErklärungBegrenze, was Anwendungen steuern können
  3. 3
    BeispielTrenne Identität und Privilegien
  4. 4
    HerausforderungSage die Folge eines verbotenen Befehls voraus
  5. 5
    ErklärungFinde den eigentlichen Systemaufruf
  6. 6
    BeispielBereite einen Schreibvorgang vor
  7. 7
    BeispielTritt ein und prüfe
  8. 8
    BeispielDeute die Rückkehr
  9. 9
    HerausforderungVerfolge einen blockierten Lesevorgang
  10. 10
    ErklärungOrdne nach der Ursache ein
  11. 11
    BeispielBehebe oder beende
  12. 12
    TippOrdne Signale der Prozessebene zu
  13. 13
    HerausforderungOrdne einen Geräteabschluss ein
  14. 14
    HerausforderungUnterscheide zwei Arten des Eintritts
  15. 15
    ZusammenfassungVerfolge Ursache, Prüfungen und Ergebnis
5

Behalte die Kontrolle bei direkter Programmausführung

15 Schritte
Verfolge die timergesteuerte Kontrolle bis zu einem Wechsel zwischen Prozessen und schätze dann den Zeitanteil ab, den das Wechseln beansprucht.

Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.

  1. 1
    EinführungVerbinde Geschwindigkeit und Kontrolle
  2. 2
    ErklärungLöse zwei Steuerungsprobleme
  3. 3
    BeispielBereite Rechner und Prozess vor
  4. 4
    VerbindungErkenne die Schwäche der Kooperation
  5. 5
    BeispielUnterbrich eine Endlosschleife
  6. 6
    HerausforderungBestimme den fehlenden Mechanismus
  7. 7
    ErklärungSichere den unterbrochenen Benutzerpfad
  8. 8
    BeispielSichere auch den Kernelpfad
  9. 9
    BeispielKehre über den gesicherten Zustand von B zurück
  10. 10
    HerausforderungErkenne einen reinen Moduswechsel
  11. 11
    ErklärungErfasse den Aufwand nach einem Wechsel
  12. 12
    BeispielBilde den Nenner für den vollständigen Zyklus
  13. 13
    HerausforderungSchätze eine veränderte Abschnittsdauer ab
  14. 14
    HerausforderungVerbinde einen blockierten Aufruf mit seiner Fortsetzung
  15. 15
    ZusammenfassungVerfolge, zu wem der Rückweg gehört

Prozesse und Systemaufrufe: Übungsaufgaben

50 Übungsfragen in 5 Sets

Set 1: Ressourcen, Prozesse und Schutz10 Übungsfragen

Frage 1 von 10

Ein Programm schreibt eine Datei, ohne zu wissen, ob ihre Daten auf eine SSD oder ein Netzlaufwerk gelangen. Welche Idee des Betriebssystems macht das möglich?
  1. Das Zeitteilverfahren für das Speichergerät
  2. Eine Abstraktion für Dateioperationen
  3. Die parallele Ausführung von Geräteanfragen
  4. Eine Schedulingstrategie für das Programm
Antwort zeigenAntwort ausblenden
Antwort: B
Eine Dateischnittstelle verbirgt die gerätespezifische Arbeit hinter Operationen, die das Programm anfordern kann. Das ist eine Abstraktion: Das Programm arbeitet mit Bytes und Dateien, während das Betriebssystem die Gerätedetails behandelt. Das Zeitteilverfahren betrifft den Zeitpunkt der Ressourcennutzung, nicht die Schnittstelle, die diese Details verbirgt.

Frage 2 von 10

Ein Betriebssystem ändert, welcher bereite Prozess den nächsten Abschnitt erhält, lässt aber seine Routine zum Sichern der Register unverändert. Was hat sich geändert?
  1. Der Aufbau des Adressraums
  2. Der Privilegienmechanismus
  3. Das Registerformat
  4. Die Schedulingstrategie
Antwort zeigenAntwort ausblenden
Antwort: D
Die Auswahlregel ist eine Strategie; die unveränderte Routine zum Sichern und Wiederherstellen ist ein Mechanismus. Ein Wechselmechanismus kann mehrere Verfahren zur Wahl des nächsten Prozesses unterstützen. Eine Änderung der Reihenfolge der CPU-Abschnitte ändert für sich nicht, wie der Rechner den Registerzustand sichert.

Frage 3 von 10

Ein Auftrag rechnet wiederholt 15 ms und wartet dann 45 ms auf Ein-/Ausgabe. Welche CPU-Auslastung erreicht er, wenn er der einzige Auftrag ist und der Wechselaufwand vernachlässigt wird?
  1. 25%
  2. 33,3%
  3. 75%
  4. 15%
Antwort zeigenAntwort ausblenden
Antwort: A
Jeder Zyklus dauert 15 + 45 = 60 ms, und nur 15 ms enthalten Berechnungen. Die CPU-Auslastung beträgt daher 15 / 60 = 25 %. Teilt man 15 durch die 45 ms Wartezeit, erhält man 33,3 %, aber für die Auslastung zählt der gesamte verstrichene Zyklus einschließlich Berechnung und Warten.

Frage 4 von 10

Zwei Programme wechseln sich auf einer logischen CPU wiederholt ab, bevor eines von ihnen endet. Wie wird ihre Ausführung beschrieben?
  1. Parallel, mit gleichzeitiger Ausführung
  2. Sequenziell, wobei jedes zuerst endet
  3. Nebenläufig, mit verzahnter Ausführung
  4. Raumgeteilt, mit dauerhafter CPU-Aufteilung
Antwort zeigenAntwort ausblenden
Antwort: C
Beide Aktivitäten laufen im selben Zeitraum ab, ihre Ausführung ist also nebenläufig. Eine einzelne logische CPU verzahnt ihre Befehle. Parallele Ausführung würde gleichzeitig arbeitende Verarbeitungsressourcen voraussetzen; ein schneller Wechsel der Abschnitte lässt die Befehle nicht im selben Augenblick laufen.

Frage 5 von 10

Eine Prozessliste zeigt PID 9641, PPID 9600 und den Befehlsnamen „sort“. Welcher Prozess ist sein Elternprozess?
  1. Der Prozess mit der PID 9600
  2. Der Prozess mit der PID 9641
  3. Jeder Prozess namens „sort“
  4. Der nach 9641 erzeugte Prozess
Antwort zeigenAntwort ausblenden
Antwort: A
Die PPID bezeichnet den Elternprozess, der also die PID 9600 hat. PID 9641 bezeichnet den sort-Prozess selbst. Ein Befehlsname kann von vielen Prozessinstanzen geteilt werden und ersetzt nicht die vom Kernel erfasste numerische Elternbeziehung.

Frage 6 von 10

Zwei getrennt gestartete Kopien eines Programms geben für eine private Variable dieselbe virtuelle Adresse aus. Eine ändert deren Wert. Was folgt für die andere Kopie?
  1. Sie sieht denselben Schreibvorgang, weil die virtuellen Adressen übereinstimmen.
  2. Sie sieht den Schreibvorgang erst, wenn beide Prozesse wieder in den Kernel eintreten.
  3. Sie behält ihren Wert nur, bis ihr nächster gesicherter Kontext wiederhergestellt wird.
  4. Sie behält ihren Wert, weil ihr Adressraum getrennt ist.
Antwort zeigenAntwort ausblenden
Antwort: D
Jeder Prozess hat seinen eigenen privaten Adressraum, sodass gleiche virtuelle Adresszahlen unterschiedliche Daten bezeichnen können. Ein Schreibvorgang auf eine private Variable ändert die Variable des anderen Prozesses nicht. Übereinstimmende ausgegebene Adressen allein bedeuten keine gemeinsam genutzte beschreibbare Speicherabbildung.

Frage 7 von 10

Ein Prozess kann weiterrechnen, aber der Timer hat seinen CPU-Abschnitt beendet. In welchem Grundzustand ist er jetzt?
  1. Blockiert, wartet auf den Abschluss des Timers
  2. Bereit, wartet auf einen weiteren CPU-Abschnitt
  3. Angehalten, wartet auf ein Fortsetzungssignal
  4. Zombie, wartet auf den Abruf durch den Elternprozess
Antwort zeigenAntwort ausblenden
Antwort: B
Der Prozess hat Arbeit und braucht nur eine CPU, ist also bereit. Der Verlust eines Abschnitts macht ihn nicht blockiert, weil keine Ein-/Ausgabe und kein anderes erforderliches Ereignis seinen Fortschritt verhindert. Die Zustände „angehalten“ und „Zombie“ beschreiben andere Situationen: ausgesetzte Ausführung und abgeschlossene Ausführung, deren Ergebnis noch abgerufen werden muss.

Frage 8 von 10

Auf einer logischen CPU läuft M, während N wegen eines Lesevorgangs blockiert ist. Das Gerät schließt den Lesevorgang von N ab, und der Kernel kehrt zu M zurück. Welchen Grundzustand haben die beiden unmittelbar danach?
  1. M bereit; N laufend
  2. M laufend; N blockiert
  3. M laufend; N bereit
  4. M blockiert; N bereit
Antwort zeigenAntwort ausblenden
Antwort: C
Der abgeschlossene Lesevorgang beseitigt den Grund für die Blockierung von N, sodass N bereit wird. M läuft weiter, weil der Kernel zu M zurückgekehrt ist. Der Geräteabschluss macht N ausführungsbereit, verlangt aber nicht, dass der Scheduler N sofort die CPU gibt. Wer den Abschluss als automatischen Wechsel behandelt, überspringt diese Entscheidung.

Frage 9 von 10

Ein Wartungsprogramm läuft mit Root-Identität und fordert eine geschützte Dateioperation an. In welchem Modus werden seine eigenen Anwendungsbefehle ausgeführt?
  1. Im Kernelmodus, weil root Eintrittsprüfungen umgeht
  2. Im Supervisormodus, bis die Datei geschlossen ist
  3. Im Benutzermodus, nur wenn die angeforderte Datei öffentlich ist
  4. Im Benutzermodus, wobei der Kernel die Berechtigungen prüft
Antwort zeigenAntwort ausblenden
Antwort: D
Root ist eine Identität mit umfassenderen Dienstberechtigungen, kein CPU-Ausführungsmodus. Die Anwendungsbefehle des Programms laufen weiterhin im Benutzermodus und nutzen für Kernelarbeit einen kontrollierten Eintrittspfad. Die Berechtigung, eine geschützte Datei zu öffnen, macht das Programm selbst nicht zu Kernelcode.

Frage 10 von 10

Ein neues Betriebssystem setzt Einschränkungen im Benutzermodus und einen geschützten Systemaufrufeintritt durch, erhält die CPU aber nur zurück, wenn ein Programm einen Aufruf ausführt oder eine Ausnahme auslöst. Ein Benutzerprogramm gerät in eine endlose Rechenschleife. Warum kann dieser Entwurf daran scheitern, eine CPU aufzuteilen?
  1. Die Schleife kann jeden eingerichteten Rückweg zum Betriebssystem vermeiden.
  2. Die Schleife gibt bei jedem folgenden Sprung automatisch die CPU ab.
  3. Der Kernel läuft neben der Schleife auf derselben logischen CPU.
  4. Der Benutzermodusschutz erzwingt nach jedem Befehl die Auswahl eines bereiten Prozesses.
Antwort zeigenAntwort ausblenden
Antwort: A
Das Programm kann weiter erlaubte Befehle ausführen, ohne einen Aufruf auszuführen oder eine Ausnahme zu verursachen. Dann hat das Betriebssystem kein unabhängiges Ereignis, das den Eintritt erzwingt, damit es einen anderen Prozess ausführen kann. Privilegieneinschränkungen schützen Operationen, aber ein Timerinterrupt ist nötig, um ohne Kooperation die Kontrolle zurückzugewinnen.
Set 2: Einen Prozess erzeugen, ersetzen und sein Ergebnis abrufen10 Übungsfragen

Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.

Frage 1 von 10

Ein Elternprozess erhält von einem erfolgreichen fork den Wert 7421. Welchen Wert gibt derselbe fork im Kindprozess zurück?
  1. 7421
  2. -1
  3. 0
  4. Die PID des Elternprozesses
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 2 von 10

Jeder fork-Aufruf ist erfolgreich. Wie viele Prozesse erreichen puts("done") in diesem Code?

if (fork() == 0) {
    fork();
}
puts("done");
  1. 2
  2. 3
  3. 4
  4. 5
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 3 von 10

Ein Programm mit einem Thread lässt „ready“ in einem vollständig gepufferten Ausgabedatenstrom zur Ausgabe ausstehen und führt dann erfolgreich fork aus. Beide Prozesse rufen exit auf, ohne weitere Ausgaben zu erzeugen. Was kann am Ziel ankommen?
  1. Keine Kopie, weil fork ausstehende Ausgaben löscht
  2. Eine Kopie, weil der Datenstrom einen Deskriptor hat
  3. Eine Kopie, aber nur wenn der Kindprozess zuerst endet
  4. Zwei Kopien, weil jeder Prozess einen kopierten Puffer ausgibt
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 4 von 10

Ein Prozess mit PID 8502 führt erfolgreich exec für ein neues Programm aus. Welche Prozesskennung hat das neue Programm?
  1. 8502
  2. 8503
  3. 0
  4. Die PID des Elternprozesses
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 5 von 10

Ein Programm ist unter /opt/course/bin/checker installiert, und PATH enthält /opt/course/bin. Im aktuellen Verzeichnis gibt es keine Datei namens checker. Warum findet execv("checker", args) das Programm nicht?
  1. Execv durchsucht PATH nur, wenn args[0] ein absoluter Pfadname ist.
  2. Execv durchsucht nur das Verzeichnis der aufrufenden ausführbaren Datei.
  3. Execv behandelt den Namen als Pfadnamen und durchsucht PATH nicht.
  4. Execv durchsucht PATH erst nach einem Fehlversuch im aktuellen Verzeichnis.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 6 von 10

Ein Kindprozess ruft exit(515) auf. Was meldet WEXITSTATUS nach einem erfolgreichen Warteaufruf und einem wahren WIFEXITED-Test?
  1. 515
  2. 512
  3. 255
  4. 3
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 7 von 10

Ein Elternprozess hat einen laufenden Kindprozess. Sein Aufruf waitpid(child, &status, WNOHANG) gibt 0 zurück. Was ist geschehen?
  1. Der Kindprozess wurde erfolgreich beendet.
  2. Kein Ergebnis des Kindprozesses ist bereit, daher läuft der Elternprozess weiter.
  3. Der Kindprozess gehört nicht mehr zum Elternprozess.
  4. Der Elternprozess hat den Status eines Kindprozesses mit PID 0 abgerufen.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 8 von 10

Ein Kindprozess eines Servers hat den Zustand Z. Nach dem Senden von SIGKILL bleibt der Eintrag sichtbar. Welche Handlung gibt den aufbewahrten Ergebnisdatensatz dieses Kindprozesses frei?
  1. Der Elternprozess ruft ihn mit einer Warteoperation ab.
  2. Der Kindprozess behandelt SIGTERM und ruft exit auf.
  3. Der Scheduler gibt dem Kindprozess einen weiteren CPU-Abschnitt.
  4. Der Elternprozess sendet SIGCONT und dann SIGSTOP.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 9 von 10

Eine Shell leitet die Standardausgabe eines Kindprozesses in eine Datei um und führt mit exec einen Befehl aus. Der Befehl schreibt normal, endet erfolgreich, und die Shell zeigt erneut eine Eingabeaufforderung. Warum brauchte der Befehl keinen Umleitungscode?
  1. Der Elternprozess leitet die Ausgabe des Kindprozesses nach der Rückkehr von wait weiter.
  2. Exec interpretiert den Umleitungstext im Argumentvektor.
  3. Die Deskriptoreinrichtung des Kindprozesses bleibt beim Programmaustausch erhalten.
  4. Exec gibt dem Befehl eine neue Kopie der Deskriptoren des Elternprozesses.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 10 von 10

Eine Shell startet producer | consumer. Der Erzeuger endet, aber der Verbraucher wartet unbegrenzt, nachdem er alle verfügbaren Bytes gelesen hat. Die Shell hat ihre Pipe-Kopien geschlossen; der Verbraucher hält noch seinen geerbten ursprünglichen Schreibende-Deskriptor offen. Was behebt das Hängen?
  1. Warte auf den Verbraucher, bevor du das Leseende der Shell schließt.
  2. Schließe das ungenutzte Schreibende im Verbraucher vor exec.
  3. Schließe das Leseende des Verbrauchers, wenn der Erzeuger endet.
  4. Lass die Shell den Status des Erzeugers abrufen, bevor sie zu lesen beginnt.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Set 3: Verfolge Kerneleintritt und CPU-Wechsel10 Übungsfragen

Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.

Frage 1 von 10

Warum muss das Ändern der Tabelle der Interruptbehandlungsroutinen privilegiert sein?
  1. Damit Benutzercode den geschützten Kerneleintritt nicht umlenken kann
  2. Damit jedes Programm eine andere Systemaufrufnummer verwendet
  3. Damit alle Hardwareereignisse synchron zu Benutzerbefehlen sind
  4. Damit jeder Eintritt in eine Behandlungsroutine einen neuen Prozess verlangt
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 2 von 10

Eine C-Funktion formatiert mehrere kurze Nachrichten in einem Ausgabepuffer und überträgt sie später mit einem Schreibvorgang. Welchen Aufwand verringert diese Bündelung?
  1. Die Anzahl der Zeichen, die die Anwendung formatieren muss
  2. Die für die ausführbare Datei benötigte Speichermenge
  3. Die Anzahl der Prozesse, die die Nachrichten halten müssen
  4. Wiederholte Verwaltungsarbeit für Kerneleintritt und Systemaufrufe
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 3 von 10

Ein C-Wrapper gibt von open den Wert -1 zurück und errno ist ENOENT. Was sollte der Aufrufer daraus schließen?
  1. Die Datei wurde mit einem ungewöhnlichen Deskriptor erfolgreich geöffnet.
  2. Die Anfrage schlug fehl, weil die benannte Datei nicht gefunden wurde.
  3. Die Anfrage erzeugte einen Kindprozess, der noch nicht beendet ist.
  4. Die Datei wurde geöffnet, aber die Byteanzahl wurde abgeschnitten.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 4 von 10

Ein Netzwerkcontroller meldet, dass ein Paket eingetroffen ist, während ein Prozess ganze Zahlen addierte. Welche Art von Kerneleintrittsereignis ist das?
  1. Eine synchrone arithmetische Ausnahme
  2. Ein absichtlicher Systemaufruf-Trap
  3. Ein asynchroner Hardwareinterrupt
  4. Ein vom Additionsbefehl gesendetes Softwaresignal
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 5 von 10

Ein Befehl greift auf eine gültige Seite zu, die sich derzeit nicht im Speicher befindet. Der Kernel lädt die Seite erfolgreich. Wie kann die Ausführung fortgesetzt werden?
  1. Den Befehl überspringen, weil sein Ergebnis bereits gespeichert wurde.
  2. Den Prozess beenden, weil jede Ausnahme fatal ist.
  3. Einen neuen Prozess für die übrigen Befehle erzeugen.
  4. Den Befehl mit der nun verfügbaren benötigten Seite erneut ausführen.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 6 von 10

Wie viele Timerimpulse treten in einem periodischen Timermodell mit einem Impuls alle 5 ms pro Sekunde auf?
  1. 200
  2. 50
  3. 500
  4. 5000
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 7 von 10

Ein Prozess tritt für einen nicht blockierenden Dienst in den Kernel ein. Die Behandlungsroutine endet und kehrt zum selben Prozess zurück. Was ist geschehen?
  1. Ein Kontextwechsel ohne Änderung der Privilegienstufe
  2. Ein Moduswechsel ohne Prozesskontextwechsel
  3. Ein Prozesswechsel unter Beibehaltung der ursprünglichen PID
  4. Ein Austausch des Prozessprogramms durch exec
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 8 von 10

Auf einer logischen CPU führt A einen nicht blockierenden Schreibvorgang aus, der zu A zurückkehrt; dann führt ein Timer in den Kernel und kehrt zu A zurück. Anschließend führt A einen blockierenden Lesevorgang aus und B wird fortgesetzt; B endet später durch einen Systemaufruf und C wird fortgesetzt. Es gibt keine weiteren Kerneleintritte. Wie viele Eintritte vom Benutzer- in den Kernelmodus und wie viele Prozesskontextwechsel finden statt?
  1. 4 Kerneleintritte; 4 Prozesswechsel
  2. 3 Kerneleintritte; 2 Prozesswechsel
  3. 4 Kerneleintritte; 2 Prozesswechsel
  4. 4 Kerneleintritte; 3 Prozesswechsel
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 9 von 10

Eine Anwendung trägt die Adresse von geschütztem Kernelspeicher in ein Schreibpufferargument ein und führt einen Systemaufruf aus. Warum berechtigt der kontrollierte Eintritt allein nicht zum angeforderten Lesen von dieser Adresse?
  1. Der Kernel muss den Pufferzugriff des Aufrufers weiterhin prüfen.
  2. Die Dienstnummer allein berechtigt zu jeder übergebenen Pufferadresse.
  3. Die Registereinrichtung des Wrappers beweist bereits, dass der Zeiger zugänglich ist.
  4. Der Moduswechsel macht geschützten Speicher zum Teil des Benutzerpuffers.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 10 von 10

Ein wiederholter CPU-Zyklus bietet 2 ms nutzbare Ausführung, gefolgt von 8 µs Kontextwechselaufwand. Welcher Anteil des Zyklus entfällt ungefähr auf den Wechsel?
  1. 4,00%
  2. 0,004%
  3. 0,80%
  4. 0,398%
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Set 4: Verfolge Prozessverhalten und Ressourcenaufteilung10 Übungsfragen

Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.

Frage 1 von 10

Eine logische CPU führt J 3 ms lang aus. J wartet dann 5 ms auf Ein-/Ausgabe und braucht weitere 2 ms CPU-Zeit. K ist anfangs bereit und braucht 6 ms CPU-Zeit. K läuft immer dann, wenn J blockiert, und läuft bis zu seinem Ende weiter; der Wechselaufwand wird vernachlässigt. Wann sind beide fertig?
  1. 10 ms
  2. 11 ms
  3. 14 ms
  4. 16 ms
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 2 von 10

Welcher gespeicherte Wert ermöglicht dem Kernel, den nächsten Befehl zu bestimmen, wenn er einen pausierten Ausführungskontext fortsetzt?
  1. Das aktuelle Verzeichnis
  2. Der Dateioffset
  3. Die Eltern-PID
  4. Der Befehlszähler
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 3 von 10

Ein Prozess mit einem Thread hat ein privates x mit dem Wert 6. Jeder fork-Aufruf ist erfolgreich. Alle Prozesse erreichen puts. Wie viele Zeilen enthalten „9“?

pid_t p = fork();
if (p == 0) x += 3;
fork();
if (x == 9) puts("9");
  1. 2
  2. 1
  3. 3
  4. 4
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 4 von 10

Vor einem erfolgreichen fork verweist ein Dateideskriptor auf Offset 0 in einer offenen Datei, die „ABCDEFGH“ enthält. Der Elternprozess liest genau drei Bytes, danach liest der Kindprozess über den geerbten Deskriptor genau zwei Bytes. Es finden keine weiteren Operationen statt. Was liest der Kindprozess?
  1. „AB“
  2. „BC“
  3. „DE“
  4. „FG“
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 5 von 10

Ein Kindprozess ruft execvp für ein Programm auf, das nicht existiert. Seine nächste Anweisung gibt einen Fehler aus. Warum kann diese Anweisung ausgeführt werden?
  1. Exec startet das neue Programm neben dem alten Aufrufpfad.
  2. Jedes erfolgreiche exec kehrt einmal zurück, bevor es den Speicher ersetzt.
  3. Der Elternprozess führt automatisch alle Fehleranweisungen des Kindprozesses aus.
  4. Das fehlgeschlagene exec kehrt zurück, ohne das alte Programm zu ersetzen.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 6 von 10

Ein Elternprozess mit zwei noch existierenden Kindprozessen ruft wait auf. Eine Signalbehandlungsroutine unterbricht den Aufruf, der -1 und errno EINTR liefert. Was sollte der Elternprozess tun, um weiterhin den Status der Kindprozesse abzurufen?
  1. Aufhören, weil der Status beider Kindprozesse abgerufen wurde.
  2. Das Warten wiederholen, statt EINTR als Abschluss zu behandeln.
  3. Den unveränderten Status mit WEXITSTATUS dekodieren.
  4. SIGKILL an den eigenen Prozesstabelleneintrag senden.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 7 von 10

Eine Shell ruft zur Implementierung von cd chdir nur in einem mit fork erzeugten Kindprozess auf, wartet auf diesen und startet dann einen weiteren Befehl. Warum beginnt der nächste Befehl im alten Verzeichnis?
  1. Wait setzt das Verzeichnis des Elternprozesses immer auf den Startwert zurück.
  2. Exec ignoriert jede geerbte Einstellung des aktuellen Verzeichnisses.
  3. Der Verzeichniswechsel des Kindprozesses änderte die Eltern-Shell nicht.
  4. Chdir verlangt, dass die Shell selbst im Kernelmodus läuft.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 8 von 10

Eine Shell erzeugt eine Pipe, erzeugt mit fork nur den Erzeuger und wartet auf ihn, bevor sie den Verbraucher erzeugt. Der Erzeuger schreibt mehr, als die Pipe aufnehmen kann. Warum kann der Startablauf zum Stillstand kommen?
  1. Der Erzeuger blockiert an einer vollen Pipe, während kein Verbraucher sie leeren kann.
  2. Die Leseendekopie des Elternprozesses verbraucht Bytes erst nach der Rückkehr von wait.
  3. Der Erzeuger erreicht das Dateiende, sobald die Pipe erstmals voll ist.
  4. Der Erzeuger kann seine Ausgabe erst schließen, wenn der Status des Elternprozesses abgerufen wurde.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 9 von 10

Ein Gerätedienst läuft in einem getrennten Server im Benutzermodus und stürzt ab. Das Betriebssystem startet diesen Dienst neu, während unabhängige Anwendungen weiterlaufen. Welche Eigenschaft des Kernelentwurfs erklärt dieses Ergebnis?
  1. Alle Dienste nutzen denselben privilegierten Adressraum.
  2. Jede Geräteanfrage umgeht die Schutzregeln des Kernels.
  3. Der Scheduler garantiert die parallele Ausführung jeder Anwendung.
  4. Der Dienst ist vom kleinen privilegierten Kern isoliert.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 10 von 10

Dasselbe Programm und dieselbe Eingabe verursachen bei einem Durchlauf einen Seitenfehler, bei einem anderen aber nicht, weil die benötigte Seite bereits im Speicher liegt. Wie ist der Fehler des ersten Durchlaufs einzuordnen?
  1. Asynchron, weil sich die beiden Durchläufe unterschieden
  2. Synchron, weil der Speicherbefehl ihn verursachte
  3. Als Hardwareinterrupt, der nur durch verstrichene Zeit entsteht
  4. Als Signal, das direkt vom Puffer der C-Bibliothek erzeugt wird
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Set 5: Diagnostiziere Wechselwirkungen zwischen Prozess und Kernel10 Übungsfragen

Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.

Frage 1 von 10

Das Ergebnis eines Kindprozesses erfüllt nach einem erfolgreichen waitpid den Test WIFSIGNALED. Welches Makro liefert die Nummer des beendenden Signals?
  1. WEXITSTATUS(status)
  2. WIFEXITED(status)
  3. WTERMSIG(status)
  4. WNOHANG
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 2 von 10

Ein write-Wrapper meldet, dass von einem angeforderten Puffer mit 9 Bytes 5 Bytes geschrieben wurden. Wie viele der angeforderten Bytes müssen noch übertragen werden?
  1. 4 Bytes
  2. 5 Bytes
  3. 9 Bytes
  4. 14 Bytes
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 3 von 10

Ein Prozess liest unter Linux eine ungültige Adresse, und der Kernel übermittelt SIGSEGV. Welche Reihenfolge unterscheidet die beiden Ereignisse?
  1. Auf ein Prozesssignal folgt eine Speicherausnahme der CPU.
  2. Ein asynchroner Geräteinterrupt wird direkt als SIGSEGV übermittelt.
  3. Ein absichtlicher Systemaufruf-Trap fordert die Übermittlung von SIGSEGV an.
  4. Auf eine Befehlsausnahme folgt ein Signal des Betriebssystems an den Prozess.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 4 von 10

Ein Betriebssystem setzt Einschränkungen im Benutzermodus durch und erhält Timerinterrupts. Seine Timerbehandlungsroutine sichert die Benutzerregister, kehrt aber immer zum selben Prozess zurück. Ein Lösungsvorschlag ändert nur, wie häufig der Timer auslöst. Andere Prozesse bleiben bereit. Warum kann diese Lösung trotzdem daran scheitern, ihnen CPU-Abschnitte zu geben?
  1. Der Timereintritt selbst garantiert faire Abschnitte vor der Rückkehr der Behandlungsroutine.
  2. Das Zurückgewinnen der Kontrolle liefert einen Mechanismus, keine faire Auswahlstrategie.
  3. Die Einschränkungen im Benutzermodus lassen jeden bereiten Prozess beim nächsten Timerimpuls laufen.
  4. Das Sichern der Benutzerregister führt automatisch zum nächsten bereiten Prozess.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 5 von 10

Eine Arbeitslast wiederholt nutzbare CPU-Abschnitte von 600 µs mit 6 µs Wechselaufwand. Sie wechselt zu Abschnitten von 1200 µs bei gleichem Aufwand. Wie ändert sich der Wechselanteil?
  1. Er steigt von etwa 0,50 % auf etwa 0,99 %.
  2. Er sinkt von etwa 0,99 % auf etwa 0,50 %.
  3. Er bleibt bei etwa 1 %, weil der Wechselaufwand unverändert ist.
  4. Er sinkt von etwa 9,9 % auf etwa 5,0 %.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 6 von 10

Eine Shell öffnet eine Datei für die Eingabeumleitung und erhält Deskriptor 0, weil die Standardeingabe geschlossen war. Sie ruft dup2(fd, 0) auf, schließt fd bedingungslos und führt den Befehl mit exec aus. Warum kann die Standardeingabe des Befehls geschlossen sein?
  1. Das Schließen von fd schließt nur einen alten Eintrag; Deskriptor 0 bleibt eine getrennte Kopie.
  2. Exec schließt Deskriptor 0, weil dup2 dessen Close-on-Exec-Flag gesetzt hat.
  3. dup2 weist gleiche Deskriptornummern zurück und entfernt den Eingabeeintrag.
  4. Fd ist bereits gleich 0, sodass das Schließen von fd die vorgesehene Eingabe schließt.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 7 von 10

Ein Programm hat ein privates x mit dem Wert 2. Jeder fork-Aufruf ist erfolgreich. Wie viele Prozesse geben „hit“ aus?

pid_t p = fork();
if (p > 0) {
    x = 7;
    fork();
}
if (x == 7) puts("hit");
  1. 2
  2. 3
  3. 4
  4. 1
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 8 von 10

Eine Shell startet zwei Kindprozesse. Einer endet normal mit Status 0; der andere wird durch ein Signal beendet. Die Shell muss beide Ergebnisse melden. Welches Vorgehen zur Ergebnisbehandlung ist nötig?
  1. Beide Kindprozesse je einmal abrufen und dann beide als normale Beendigungen dekodieren.
  2. Ein Ergebnis abrufen und es einmal als Beendigung und einmal als Signal dekodieren.
  3. Beide Ergebnisse abrufen und für jedes getrennt Dekodiermakros auswählen.
  4. Beide Ergebnisse abrufen, aber ihre rohen ganzen Zahlen als Beendigungscodes dekodieren.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 9 von 10

A blockiert bei einem Lesevorgang am Terminal und B läuft. Nachdem eine Eingabe A bereit macht, wählt der Scheduler A. Ein vorgeschlagener Wechsel stellt den Benutzer-Befehlszähler von A wieder her, behält aber den Kernelstack und den Adressraumkontext von B bei. Warum kann er den ausstehenden Lesevorgang von A so nicht korrekt fortsetzen?
  1. Der Benutzerrückkehrrahmen von A stellt den Rest seines Kernelpfads automatisch wieder her.
  2. Das Ändern des Benutzer-Befehlszählers wählt auch den passenden Kernelstack.
  3. Der Lesevorgang kann auf dem Kernelstack von B allein mit den Benutzerregistern von A enden.
  4. Der eigene Kernelkontext, Stack und Adressraumkontext von A müssen ausgewählt werden.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Frage 10 von 10

Ein Benutzerprogramm kennt die Adresse einer Kernelroutine und die Nummer eines gültigen Systemaufrufs. Warum kann es den Dienst anfordern, aber diese privilegierte Adresse nicht einfach wie eine gewöhnliche Funktion aufrufen?
  1. Ein Trap führt in einen konfigurierten Kernelpfad, der die Anfrage prüft und weiterleitet.
  2. Ein direkter Aufruf erhält Privilegien, wenn sein Ziel einer gültigen Dienstnummer entspricht.
  3. Eine Dienstnummer gewährt dem Aufrufer Kernelzugriff, bevor seine Argumente geprüft werden.
  4. Ein direkter Aufruf und ein Trap erhalten ihre Privilegien allein von der Zieladresse.
Antwort zeigenAntwort ausblenden

Speichere diese Einheit, um diese Antwort zu sehen.

Hängst du bei deiner eigenen Aufgabe fest? Hausaufgabenhilfe

Schlüsselbegriffe

75 Schlüsselbegriffe, jeweils mit Definition

A

Abrufen des Beendigungsstatus
Das Abrufen des Beendigungsstatus holt mit einer Warteoperation das Ergebnis eines beendeten Kindprozesses ab, sodass der Kernel dessen verbleibenden Prozessdatensatz freigeben kann.
Ein erfolgreicher Warteaufruf ruft den Status eines Kindprozesses ab; ein Elternprozess mit mehreren Kindprozessen muss weitere Ergebnisse abrufen.
Abstraktion
Eine Abstraktion ist eine nützliche Schnittstelle oder ein Modell, das darunterliegende Implementierungsdetails verbirgt und dabei die von Programmen benötigten Operationen erhält.
Die Dateiabstraktion lässt ein Programm Bytes lesen, ohne das Befehlsformat des Speichergeräts zu kennen.
Adressraum
Ein Adressraum umfasst die einem Prozess verfügbaren Speicheradressen und den in diese Sicht abgebildeten Code, die Daten, den Heap und den Stack.
Gleiche virtuelle Adressen in zwei privaten Adressräumen können auf unterschiedliche Werte im physischen Speicher verweisen.
Argumentvektor
Ein Argumentvektor ist ein mit einem Nullzeiger abgeschlossenes Array von Zeichenkettenzeigern, das einem neuen Programm als seine Kommandozeilenargumente übergeben wird.
Ein Argumentvektor für einen Zählbefehl hat getrennte Einträge für den Programmnamen, die Option und den Dateinamen.
Ausgabepuffer
Ein Ausgabepuffer ist Speicher im Benutzerraum, der formatierte Ausgabe aufnimmt, bevor eine Bibliothek diese Bytes an den Kernel überträgt.
Ausstehender Text in einem Ausgabepuffer kann durch fork kopiert und später von jedem Prozess einmal ausgegeben werden.
Ausnahme
Eine Ausnahme ist ein durch den aktuellen Befehl entstehendes Ereignis, das eine besondere Behandlung durch CPU und Kernel erfordert, etwa ein Seitenfehler.
Ein verbotener Speicherzugriff löst eine Ausnahme aus, bevor die Anwendung normal fortfahren kann.
Ausnahmeebene
Eine Ausnahmeebene ist eine Ausführungsprivilegienstufe von AArch64, darunter EL0 für Anwendungen und normalerweise EL1 für den Betriebssystemkernel.
AArch64 benennt Privilegienstufen anders als die x86-Ringe, trennt aber ebenfalls Anwendungen vom Kernel.

B

Beendigungsstatus
Ein Beendigungsstatus ist das kleine Ergebnis, das ein Prozess bei normaler Beendigung meldet, üblicherweise 0 für Erfolg und ein Wert ungleich null für einen Fehler.
Nach der Prüfung auf normale Beendigung liest der Elternprozess den Beendigungsstatus mit dem passenden Warte-Makro aus.
Befehlszähler
Der Befehlszähler ist ein CPU-Register, das die Adresse des nächsten Befehls in einem Ausführungskontext enthält.
Das Wiederherstellen des Befehlszählers lässt einen pausierten Prozess nach einem Wechsel beim richtigen Befehl fortfahren.
Benutzermodus
Der Benutzermodus ist die Privilegienstufe der CPU, in der Anwendungsbefehle mit eingeschränktem Zugriff auf geschützten Speicher und geschützte Operationen ausgeführt werden.
Auch der Editor eines Administrators läuft im Benutzermodus und fordert geschützte Dienste über Systemaufrufe an.
Betriebssystem
Ein Betriebssystem verwaltet Hardwareressourcen und stellt Dienste bereit, mit denen Programme sicher ausgeführt werden, kommunizieren und Daten speichern können.
Das Betriebssystem teilt CPU-Zeit zwischen einem Browser und einem Compiler auf und verwaltet dabei deren Dateianfragen.
Bibliothekswrapper
Ein Bibliothekswrapper ist Code im Benutzerraum, der eine aufrufbare Schnittstelle bereitstellt und Argumente und Eintritt für einen Kerneldienst vorbereitet.
Der Bibliothekswrapper für write folgt der Registerkonvention des Systemaufrufs, bevor er in den Kernel eintritt.

C

Close-on-Exec
Close-on-Exec ist ein Deskriptor-Flag, das die automatische Schließung dieses Deskriptors anfordert, wenn ein Prozess sein Programm erfolgreich ersetzt.
Eine Shell kann verhindern, dass ein interner Deskriptor den Befehl erreicht, indem sie ihn mit Close-on-Exec markiert.
Copy-on-Write
Copy-on-Write nutzt zunächst geschützte physische Speicherseiten gemeinsam und erstellt private Kopien, wenn Schreibvorgänge getrennte Inhalte erfordern.
Ein Kindprozess, der sofort exec aufruft, kann geerbte Seiten verwerfen, ohne zuvor jede Seite zu kopieren.
CPU-Auslastung
Die CPU-Auslastung ist der Anteil eines verstrichenen Zeitintervalls, während dessen die CPU beschäftigt ist, ausgedrückt als Verhältnis oder Prozentsatz.
Wenn nutzbare Berechnungen in einem Ablauf von 12 ms ohne Verwaltungsaufwand 9 ms beanspruchen, beträgt die CPU-Auslastung 75 %.

D

Dateideskriptor
Ein Dateideskriptor ist eine kleine ganze Zahl in der Deskriptortabelle eines Prozesses, die auf eine offene Datei oder eine andere Ein-/Ausgaberessource verweist.
Eine Shell verbindet den Dateideskriptor 1 mit einer Datei, bevor sie einen Befehl ausführt, dessen Ausgabe umgeleitet wird.
Dateiende (EOF)
Das Dateiende ist ein Leseergebnis, das anzeigt, dass keine weiteren Daten verfügbar sind; bei einer Pipe setzt es einen leeren Puffer und geschlossene Schreibenden voraus.
Ein Leser kann warten, statt das Dateiende zu erhalten, wenn die Shell versehentlich ein Pipe-Schreibende offen hält.

E

Eingebauter Shell-Befehl
Ein eingebauter Shell-Befehl ist eine von der Shell selbst implementierte Operation, mit der sie ihren eigenen dauerhaften Prozesszustand ändern kann.
Der eingebaute Befehl cd ändert das Verzeichnis der Shell, sodass spätere Befehle das neu gewählte Verzeichnis erben.
Eingeschränkte direkte Ausführung
Die eingeschränkte direkte Ausführung führt Benutzerbefehle auf der Hardware aus, während Privilegienbeschränkungen und timergesteuerter Eintritt die Kontrolle des Betriebssystems erhalten.
Eine Anwendung in einer Schleife wird direkt ausgeführt, kann aber den Timer, der sie unterbrechen kann, dennoch nicht deaktivieren.
Elternprozess
Ein Elternprozess ist der Prozess, der einen Kindprozess erzeugt; er kann mit Warteoperationen das Beendigungsergebnis dieses Kindprozesses abrufen.
Eine Shell bleibt der Elternprozess, während ihr Kindprozess sein eigenes Programm durch einen externen Befehl ersetzt.
errno
errno ist ein Fehlerindikator der C-Bibliothek, der von vielen Schnittstellen bei einem Fehler gesetzt und geprüft wird, nachdem der Rückgabewert einen Fehler gemeldet hat.
Ein fehlgeschlagenes open kann errno auf ENOENT setzen, während ein späterer erfolgreicher Aufruf diesen früheren Wert nicht zurücksetzen muss.
exec
exec ersetzt das Programmabbild des aufrufenden Prozesses durch ein neues Programm und erhält dabei die Prozessidentität und ausgewählte Ressourcen.
Der Kindprozess behält seine PID, wenn exec den Code der Shell durch den Code des angeforderten Befehls ersetzt.

F

fork
fork ist die Unix-Operation zur Prozesserzeugung, die einen Kindprozess erzeugt und nach demselben Aufruf sowohl zum Eltern- als auch zum Kindprozess zurückkehrt.
Nach einem erfolgreichen fork erhält der Elternprozess die PID des Kindprozesses und der Kindprozess den Rückgabewert 0.

H

Hardwareinterrupt
Ein Hardwareinterrupt ist eine Benachrichtigung eines Geräts oder Timers, die die CPU unabhängig vom aktuellen Befehl ein Ereignis behandeln lässt.
Ein Speichercontroller löst einen Hardwareinterrupt aus, wenn eine zuvor angeforderte Übertragung abgeschlossen ist.
Hintergrundauftrag
Ein Hintergrundauftrag ist Arbeit, die gestartet wird, ohne dass die Shell sofort auf ihren Abschluss wartet, sodass die Befehlseingabe weitergehen kann.
Die Shell zeigt eine Eingabeaufforderung, während ein Hintergrundauftrag läuft, muss aber nach dessen Ende noch den Status des Kindprozesses abrufen.

K

Kernel
Der Kernel ist der privilegierte Kern eines Betriebssystems, der geschützte Hardwareoperationen steuert und Prozessressourcen verwaltet.
Eine Terminalanwendung fordert den Kernel auf, Eingaben zu lesen, statt das Tastaturgerät selbst zu steuern.
Kernelkontext
Ein Kernelkontext ist der gesicherte Register- und Stackzustand, der nötig ist, um den Ausführungspfad des Kernels für einen ausgesetzten Prozess fortzusetzen.
Beim Wechsel zu Prozess B stellt die Wechselroutine den Kernelkontext von B wieder her, bevor der Kernelaustrittscode dessen Benutzerregister wiederherstellen kann.
Kernelmodus
Der Kernelmodus ist die Privilegienstufe der CPU, die dem Betriebssystem erlaubt, geschützten Hardwarezustand zu verwalten und privilegierte Befehle auszuführen.
Die CPU wechselt in den Kernelmodus, um eine Anfrage der Anwendung eines gewöhnlichen Benutzers zu behandeln.
Kernelstack
Ein Kernelstack enthält gesicherten Benutzerzustand und Kernelausführungsdaten, während der Kernel im Auftrag eines Prozesses oder Ausführungskontexts läuft.
Der Wechsel zu einem anderen Prozess umfasst die Wiederherstellung des Kernelstacks, den der gesicherte Rückweg dieses Prozesses nutzt.
Kindprozess
Ein Kindprozess ist ein neuer Prozess, der von einem Elternprozess erzeugt wird und eine eigene Identität sowie eine Kopie des gewöhnlichen privaten Speichers des Elternprozesses hat.
Der Kindprozess kann nach fork eine private Variable ändern, ohne den Wert des Elternprozesses zu ändern.
Kontextwechsel
Ein Kontextwechsel sichert einen Ausführungskontext und stellt einen anderen wieder her; ein Prozesswechsel wählt außerdem den passenden Adressraum aus.
Wenn ein Leser blockiert, lässt ein Kontextwechsel eine bereite Berechnung auf derselben logischen CPU weiterlaufen.
Kooperative Steuerung
Die kooperative Steuerung ist darauf angewiesen, dass Programme die CPU freiwillig abgeben, Systemaufrufe ausführen oder Ausnahmen verursachen, damit das Betriebssystem die CPU zurückgewinnt.
Eine endlose Benutzerschleife zeigt die Schwäche der kooperativen Steuerung, wenn sie nie einen Rückweg zum Betriebssystem erreicht.

M

Mechanismus
Ein Mechanismus ist das Mittel zur Ausführung einer Betriebssystemoperation, etwa zum Sichern des Zustands eines Prozesses und Wiederherstellen des Zustands eines anderen.
Der Kontextwechselmechanismus unterstützt mehrere mögliche Regeln zur Entscheidung, welcher bereite Prozess als Nächstes läuft.
Mehrprogrammbetrieb
Der Mehrprogrammbetrieb hält mehrere Aufträge im Speicher verfügbar, sodass einer die CPU nutzen kann, während ein anderer wegen Ein-/Ausgabe blockiert ist.
Der Mehrprogrammbetrieb lässt einen Compiler rechnen, während ein anderer Prozess auf den Abschluss eines Speicherlesevorgangs wartet.
Mikrokernel
Ein Mikrokernel behält einen kleinen privilegierten Kern und platziert mehr Betriebssystemdienste in getrennten Prozessen im Benutzermodus, die über Nachrichten kommunizieren.
Ein ausgefallener Treiber im Benutzermodus kann bei einem Mikrokernelentwurf oft neu gestartet werden, während andere Dienste weiterlaufen.
Moduswechsel
Ein Moduswechsel ändert die Privilegienstufe der CPU, etwa beim Eintritt in den Kernel für einen Dienst und der Rückkehr zum selben Benutzerprozess.
Ein nicht blockierender Systemaufruf kann Moduswechsel umfassen, ohne einen anderen Prozess fortzusetzen.
Monolithischer Kernel
Ein monolithischer Kernel führt die meisten Betriebssystemdienste gemeinsam im Kernelmodus aus. Das erlaubt direkte Aufrufe, bedeutet aber auch gemeinsame Risiken durch privilegierte Fehler.
Ein Linux-Treibermodul wird Teil des monolithischen Kernels und kann den Kernelzustand beschädigen, wenn es einen schweren Fehler enthält.

N

Nebenläufigkeit
Nebenläufigkeit bedeutet, dass mehrere Aktivitäten im selben Zeitraum ablaufen, auch wenn ihre Befehle verzahnt werden.
Zwei Programme sind auf einer logischen CPU nebenläufig, wenn jedes wiederholt einen Abschnitt erhält, bevor eines endet.
Nicht blockierendes Warten
Nicht blockierendes Warten prüft, ob ein Ergebnis eines Kindprozesses verfügbar ist, und kehrt zurück, ohne den Aufrufer anzuhalten, wenn kein Ergebnis bereitsteht.
Eine Shell nutzt nicht blockierendes Warten, um abgeschlossene Hintergrundaufträge abzurufen und zugleich die Eingabeaufforderung verfügbar zu halten.

O

Offene Dateibeschreibung
Eine offene Dateibeschreibung ist ein vom Kernel verwalteter Zustand für eine offene Datei, einschließlich ihres Offsets, den mehrere Deskriptoren gemeinsam nutzen können.
Lesevorgänge von Eltern- und Kindprozess über einen geerbten Deskriptor setzen den Offset in einer gemeinsamen offenen Dateibeschreibung weiter.

P

Parallelität
Parallelität bedeutet, dass Arbeit im selben Augenblick auf getrennten Verarbeitungsressourcen ausgeführt wird, etwa auf verschiedenen logischen CPUs.
Ein Compiler und ein Renderprogramm können parallel ausgeführt werden, wenn jedes auf einer anderen logischen CPU läuft.
PATH
PATH ist eine Umgebungsvariable, die Verzeichnisse auflistet, in denen Schnittstellen wie execvp nach Namen ausführbarer Dateien suchen.
Ein über PATH gefundener Befehl kann mit execvp starten, selbst wenn seine Datei nicht im aktuellen Verzeichnis liegt.
Pipe
Eine Pipe ist ein vom Kernel verwalteter Bytekanal in eine Richtung mit einem Lese- und einem Schreibende, der zusammenarbeitende Prozesse verbinden kann.
Eine Shell verbindet die Ausgabe eines Erzeugers mit einer Pipe und die Eingabe eines Verbrauchers mit dem Leseende derselben Pipe.
Privilegierter Befehl
Ein privilegierter Befehl ändert geschützten Maschinenzustand und kann von gewöhnlichem Code im Benutzermodus nicht frei ausgeführt werden.
Das Deaktivieren von Interrupts ist privilegiert, weil es sonst verhindern könnte, dass das Betriebssystem die Kontrolle zurückgewinnt.
Programm
Ein Programm besteht aus gespeicherten Befehlen und Daten, die zur Ausführung geladen werden können; die Datei selbst hat keinen veränderlichen Ausführungszustand.
Dieselbe Shell-Programmdatei kann verwendet werden, um getrennte Prozesse in drei Terminalfenstern zu starten.
Prozess
Ein Prozess ist eine aktive Instanz eines Programms zusammen mit dem Speicher, den Registerwerten und dem Ein-/Ausgabezustand, die für seine Ausführung nötig sind.
Zwei Prozesse, die dieselbe ausführbare Datei verwenden, können unterschiedliche private Variablen und aktuelle Verzeichnisse haben.
Prozesskennung (PID)
Eine Prozesskennung (PID) ist eine einem Prozess zugewiesene numerische Kennung, die unter existierenden Prozessen im selben PID-Namensraum eindeutig ist.
Eine Prozessliste nutzt PID und Eltern-PID, um zu zeigen, dass ein Befehl von einer bestimmten Shell gestartet wurde.
Prozesskontrollblock (PCB)
Ein Prozesskontrollblock (PCB) ist der Datensatz des Kernels für einen Prozess und enthält unter anderem dessen Identität, Zustand, Ressourcen und gesicherten Kontext.
Der Kernel zieht einen Prozesskontrollblock heran, um Speicherinformationen und Elternprozess eines angehaltenen Prozesses zu finden.

R

Raumteilverfahren
Das Raumteilverfahren teilt die Kapazität einer Ressource in Teile, die verschiedene Prozesse oder Benutzer gleichzeitig belegen.
Getrennte Speicherbereiche können mehreren Prozessen zugewiesen bleiben, während nur einer die CPU hat.

S

Scheduler
Der Scheduler ist die Komponente des Betriebssystems, die auswählt, welcher ausführungsbereite Kontext als Nächstes CPU-Zeit erhalten soll.
Nach dem Abschluss eines Festplattenlesevorgangs kann der Scheduler den aktuellen Prozess weiterlaufen lassen, bevor er den Leser auswählt.
Schutzring
Ein Schutzring ist eine Hardwareprivilegienstufe von x86; gängige Systeme nutzen normalerweise Ring 0 für Kernel und Ring 3 für Anwendungen.
Der Wechsel von Anwendungsring 3 zu Kernelring 0 folgt einem vom Betriebssystem konfigurierten kontrollierten Eintrittspfad.
Seitenfehler
Ein Seitenfehler ist ein durch einen Befehl verursachtes Ereignis bei einem Speicherzugriff, dessen Übersetzung oder Berechtigungen eine besondere Behandlung erfordern.
Eine gültige fehlende Seite kann nach einem Seitenfehler geladen werden, sodass der Kernel den unterbrochenen Befehl erneut ausführen kann.
Shell
Eine Shell ist ein Benutzerprogramm, das Befehle interpretiert und Prozess- und Deskriptoroperationen nutzt, um andere Programme zu starten und zu verbinden.
Eine Vordergrund-Shell wartet auf den Kindprozess ihres Befehls, bevor sie die nächste Eingabeaufforderung anzeigt.
Signal
Ein Signal ist eine vom Betriebssystem an einen Prozess übermittelte Softwarebenachrichtigung mit einer Standardaktion oder einem eingerichteten Behandlungsverhalten.
Ein Terminal sendet SIGINT an seinen Vordergrundauftrag, wenn der Benutzer Strg-C drückt, und beendet den Auftrag damit normalerweise.
Stapelverarbeitung
Stapelverarbeitung sammelt eingereichte Aufträge und führt sie ohne interaktive Eingaben der jeweils einreichenden Person aus.
Bei der Stapelverarbeitung wartet eine eingereichte Berechnung auf frühere Aufträge, statt sofort einen Abschnitt zu erhalten.
Stapelzeiger
Der Stapelzeiger ist ein Register, das die aktuelle Position eines für Funktionsaufrufe und Ausführungszustand genutzten Stacks angibt.
Ein Kontextwechsel stellt den Stapelzeiger wieder her, damit die fortgesetzte Ausführung ihren eigenen gesicherten Stack nutzt.
Strategie
Eine Strategie ist eine Regel für die Wahl, wie verfügbare Mechanismen genutzt werden, etwa welcher bereite Prozess CPU-Zeit erhalten soll.
Eine Schedulingstrategie kann sich ändern, während der Code zum Sichern der Register beim Prozesswechsel gleich bleibt.
Systemaufruf
Ein Systemaufruf ist eine kontrollierte Anfrage eines Benutzerprogramms nach einem Kerneldienst über eine definierte Eintrittsschnittstelle.
Das Öffnen einer Datei braucht einen Systemaufruf, weil Kernelcode den Zugriff auf die Ressource der offenen Datei verwaltet.
Systemaufrufnummer
Eine Systemaufrufnummer ist eine architekturspezifische Kennung, anhand derer der Kernel eine bestimmte Dienstimplementierung auswählt.
Linux verwendet unterschiedliche Systemaufrufnummern für write auf x86-64 und AArch64, sodass rohe Nummern nicht portabel sind.

T

Timerinterrupt
Ein Timerinterrupt ist ein Hardwareereignis eines konfigurierten Timers, das dem Kernel Gelegenheit gibt, die Kontrolle über eine CPU zurückzugewinnen.
Ein Timerinterrupt kann in den Kernel führen, selbst während der aktuelle Prozess eine Schleife ohne Systemaufrufe ausführt.
Translation Lookaside Buffer (TLB)
Ein Translation Lookaside Buffer (TLB) speichert Adressübersetzungen zwischen, damit die CPU sie nicht wiederholt im Speicher nachschlagen muss.
Ein Prozesswechsel kann das erneute Laden von Übersetzungen erfordern, wobei gekennzeichnete Einträge nützliche zwischengespeicherte Übersetzungen erhalten können.
Trap
Ein Systemaufruf-Trap ist ein absichtlicher, durch die Dienstanfrage eines Programms verursachter Kerneleintritt über einen geschützten Eintrittsbefehl.
Die Anfrage, eine Ausgabe zu schreiben, verursacht einen Systemaufruf-Trap, auch wenn kein Programmfehler vorliegt.
Trap-Rückkehr
Eine Trap-Rückkehr stellt gesicherten Ausführungszustand wieder her und kehrt von der Kernelbehandlung zum vorgesehenen Benutzerausführungskontext zurück.
Das Betriebssystem kann den Anfangszustand eines neuen Prozesses so vorbereiten, dass der Wiederherstellungspfad das Programm erstmals startet.
Trap-Tabelle
Eine Trap-Tabelle ordnet Ereignisarten geschützten Zielen für Kernelbehandlungsroutinen zu; die genaue Organisation hängt von der CPU ab.
Beim Systemstart bereitet das Betriebssystem Ziele für Behandlungsroutinen vor, damit Benutzercode einen späteren Kerneleintritt nicht umlenken kann.

U

Umleitung
Eine Umleitung ändert den Deskriptor für Standardeingabe, Standardausgabe oder Standardfehlerausgabe eines Befehls so, dass er auf eine andere offene Ein-/Ausgaberessource verweist.
Eine Ausgabeumleitung lässt ein unverändertes Programm seine gewöhnliche Standardausgabe in eine gewählte Datei schreiben.

V

Verwaister Prozess
Ein verwaister Prozess ist ein Prozess, dessen Elternprozess beendet wurde und dem ein neuer Elternprozess zugewiesen wird, damit ein anderer Prozess später sein Ergebnis abrufen kann.
Ein verwaister Prozess kann nach dem Ende seines ursprünglichen Elternprozesses weiterrechnen; er ist nicht notwendigerweise beendet.
Verwaltungsaufwand (Overhead)
Verwaltungsaufwand ist die zusätzliche Zeit oder der zusätzliche Speicher für die Verwaltung einer Operation, statt direkt die nutzbare Arbeit der Anwendung auszuführen.
Das Sichern von Registern bei einem Kontextwechsel erzeugt Verwaltungsaufwand, bevor ein anderer Prozess seine Berechnung fortsetzt.
Virtualisierung
Virtualisierung stellt eine gemeinsam genutzte physische Ressource als bequem nutzbare virtuelle Ressource dar, die jedes Programm über eine einfachere Sicht nutzen kann.
CPU-Virtualisierung lässt Hunderte Prozesse auf einem Rechner mit nur wenigen logischen CPUs Fortschritte machen.

W

Wartestatus
Ein Wartestatus ist ein kodierter Wert, der beschreibt, wie ein Kindprozess endete oder seinen Zustand änderte, und mit den passenden Warte-Makros interpretiert wird.
Ein roher Linux-Wartestatus von 768 kann den normalen Beendigungscode 3 kodieren, sodass seine direkte Ausgabe den falschen Code ergibt.

Z

Zeitscheibe
Eine Zeitscheibe ist ein Intervall der CPU-Ausführung, das einer Arbeit zugeteilt wird, bevor das Scheduling einem anderen ausführungsbereiten Prozess einen Abschnitt geben kann.
Bei festem Wechselaufwand erhöht eine kürzere Zeitscheibe den Zeitanteil, der auf Wechsel entfällt.
Zeitteilverfahren
Das Zeitteilverfahren teilt den Zugriff auf eine Ressource in aufeinanderfolgende Abschnitte und ermöglicht so mehreren Prozessen oder Benutzern, sie über die Zeit gemeinsam zu nutzen.
Das Zeitteilverfahren gibt einem Editor einen weiteren CPU-Abschnitt, während eine lange Berechnung noch läuft.
Zombieprozess
Ein Zombieprozess ist ein beendeter Prozess, dessen Ergebnisdatensatz im Kernel erhalten bleibt, weil sein Elternprozess das Ergebnis noch nicht abgerufen hat.
Ein langlebiger Server verhindert die Ansammlung von Zombieprozessen, indem er den Status beendeter Kindprozesse abruft.
Zustand „bereit“
Der Zustand „bereit“ bedeutet, dass ein Prozess ausgeführt werden kann, aber darauf wartet, dass eine CPU verfügbar wird oder der Scheduler ihn auswählt.
Ein Timer kann eine unvollendete Berechnung ohne Ein-/Ausgabe vom Zustand „laufend“ in den Zustand „bereit“ versetzen.
Zustand „blockiert“
Der Zustand „blockiert“ bedeutet, dass ein Prozess erst fortfahren kann, wenn ein Ereignis eintritt, etwa wenn angeforderte Eingaben verfügbar werden.
Ein blockierender Lesevorgang am Terminal versetzt einen Prozess in den Zustand „blockiert“, bis eine Eingabe die Fortsetzung der Anfrage ermöglicht.
Zustand „laufend“
Der Zustand „laufend“ bedeutet, dass ein Prozess gerade Befehle auf einer CPU ausführt, statt ausführungsbereit zu sein oder auf ein Ereignis zu warten.
Wenn ein bereiter Compiler eine CPU erhält, wechselt er in den Zustand „laufend“ und kann seinen nächsten Befehl ausführen.

Über diese Einheit

Von StudeliFehler melden

Erstelle deine eigenen Lernmaterialien

Mach aus deinen Notizen, Folien und Dokumenten Lernleitfäden, Übungsfragen, Karteikarten und 6 weitere Tools.

Jetzt starten