Betriebssysteme - Eduard Glatz - E-Book

Betriebssysteme E-Book

Eduard Glatz

0,0

Beschreibung

Dieses Lehrbuch bietet eine umfassende Einführung in die Grundlagen der Betriebssysteme und in die Systemprogrammierung. Im Vordergrund stehen die Prinzipien moderner Betriebssysteme und die Nutzung ihrer Dienste für die systemnahe Programmierung. Methodisch wird ein Weg zwischen der Betrachtung anfallender Probleme und ihren Lösungen auf einer theoretischen und einer praktischen Basis beschritten. Dabei orientiert sich der Autor an den beiden am meisten verbreiteten Systemwelten, nämlich Unix/Linux und Windows. Zudem werden die wichtigsten Prozessorgrundlagen erklärt, soweit sie für das Verständnis der internen Funktionsweise eines Betriebssystems hilfreich sind. Die 3. Auflage ist in zahlreichen Details überarbeitet, an einigen Stellen ergänzt und generell aktualisiert. Die neuen Kapitel "Mobile Betriebssysteme" und "Virtualisierung" greifen aktuelle Entwicklungen auf. Ergänzt werden zudem die Aufrufmechanismen von Shared Libraries bzw. DLLs, UEFI-BIOS, die GPT (GUID Partition Table) und das Dateisystem ZFS. Einen Blick in die Zukunft wirft ein Exkurs über die Betriebssystem-Forschung. Auf der Buchwebsite unix.hsr.ch stehen Übungsaufgaben mit Lösungen, tabellarische Beschreibungen der wichtigsten Systemaufrufe sowie alle Abbildungen des Buches und Vorlesungsfolien für Dozierende zur freien Verfügung.

Sie lesen das E-Book in den Legimi-Apps auf:

Android
iOS
von Legimi
zertifizierten E-Readern

Seitenzahl: 934

Das E-Book (TTS) können Sie hören im Abo „Legimi Premium” in Legimi-Apps auf:

Android
iOS



Eduard Glatz, Prof. Dr. sc., studierte an der ETH in Zürich Elektrotechnik und Betriebswissenschaften. Nach 16 Jahren Berufspraxis in der Industrie und in Ingenieurunternehmungen wurde er 1996 an die Hochschule für Technik (FH Ostschweiz) in Rapperswil berufen. Seine Lehrgebiete sind Betriebssysteme und Systemsoftware. Daneben ist er in der Forschung und in der Beratung tätig.

Zu diesem Buch – sowie zu vielen weiteren dpunkt.büchern – können Sie auch das entsprechende E-Book im PDF-Format herunterladen. Werden Sie dazu einfach Mitglied bei dpunkt.plus+:

www.dpunkt.de/plus

Betriebssysteme

Grundlagen, Konzepte, Systemprogrammierung

3., überarbeitete und aktualisierte Auflage

Eduard Glatz

Eduard [email protected]

Lektorat: Christa PreisendanzCopy-Editing: Ursula Zimpfer, HerrenbergHerstellung: Frank HeidtUmschlaggestaltung: Helmut Kraus, www.exclam.deDruck und Bindung: Media-Print Informationstechnologie, Paderborn

Bibliografische Information der Deutschen NationalbibliothekDie Deutsche Nationalbibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über http://dnb.d-nb.de abrufbar.

ISBN:Buch 978-3-86490-222-2PDF 978-3-86491-629-8ePub 978-3-86491-630-4

3., überarbeitete und aktualisierte Auflage 2015Copyright © 2015 dpunkt.verlag GmbHWieblinger Weg 1769123 Heidelberg

Die vorliegende Publikation ist urheberrechtlich geschützt. Alle Rechte vorbehalten. Die Verwendung der Texte und Abbildungen, auch auszugsweise, ist ohne die schriftliche Zustimmung des Verlags urheberrechtswidrig und daher strafbar. Dies gilt insbesondere für die Vervielfältigung, Übersetzung oder die Verwendung in elektronischen Systemen.Es wird darauf hingewiesen, dass die im Buch verwendeten Soft- und Hardware-Bezeichnungen sowie Markennamen und Produktbezeichnungen der jeweiligen Firmen im Allgemeinen warenzeichen-, marken- oder patentrechtlichem Schutz unterliegen.Alle Angaben und Programme in diesem Buch wurden mit größter Sorgfalt kontrolliert. Weder Autor noch Verlag können jedoch für Schäden haftbar gemacht werden, die in Zusammenhang mit der Verwendung dieses Buches stehen.543210

Vorwort zur 3. Auflage

Seit der Erstauflage dieses Lehrbuches, die Ende 2005 entstand, hat das Thema der Betriebssysteme nichts an Aktualität verloren. Vielmehr sind Betriebssysteme als Basissoftware von Smartphones und Embedded Systems fortlaufend in neue Anwendungsbereiche vorgestoßen. Entsprechend stellen Betriebssysteme einen obligatorischen Bestandteil einer Informatikausbildung auf Hochschulstufe dar. So gelten eine Mehrheit der in diesem Buch erfassten Themen in den von ACM und IEEE im Dezember 2013 aktualisierten Computer Science Curricula als Core-Tier-1-und -Tier-2-Inhalte, d.h., als Kernthemen eines Informatik-Bachelorstudiums.

Dieses Buch nutzt zwei Betrachtungswinkel: Einerseits ist dies die Sicht auf die Programmierschnittstelle eines Betriebssystems, also die Blackbox-Betrachtung des Softwareentwicklers. Andererseits werden die dahinter steckenden Prinzipien, Algorithmen und Mechanismen beschrieben, was der Whitebox-Betrachtung des Ingenieurs entspricht. Ergänzend werden ein paar Grundlagen der Prozessortechnik vermittelt. Diese helfen die Schnittstelle zwischen Betriebssystem und Hardware besser zu verstehen. Als Beispiele dienen die Betriebssysteme Windows und Unix, deren kombinierte Kenntnis heute eine wichtige Anforderung der Praxis darstellt. Als Hilfe für Dozierende stehen auf der Buch-Website http://unix.hsr.ch Übungsaufgaben mit Lösungen, alle Abbildungen des Buches und etliche Vorlesungfolien in elektronischer Form zur Verfügung.

Für die aktuelle Auflage wurde das Buch gesamthaft überarbeitet, teilweise neu gegliedert und zu Beginn jedes Kapitels mit kompetenzorientierten Lernzielen versehen. Die gestiegene Bedeutung der Mobilbetriebssysteme und der Rechnervirtualisierung wurde gebührend berücksichtigt, indem diese Themen nun in eigenen Kapiteln behandelt werden. Einen Einblick in mögliche zukünftige Systemarchitekturen gibt die Darstellung der aktuellen Forschung bzw. deren Erkenntnisse. Abschließend bedanke ich mich bei allen Personen, die mir bei der Realisierung dieses Buchprojektes geholfen haben.

Eduard GlatzUrdorf, im Dezember 2014

Vorwort zur 1. und 2. Auflage

Obwohl heute Middleware-Systeme zur Verfügung stehen, die in vielen Fällen unabhängig von einem darunter laufenden Betriebssystem sind, ist die Betriebssystemthematik aus der Informatik-Grundausbildung aus verschiedenen Gründen nicht wegzudenken. Ohne Kenntnisse der hauptsächlichen Strukturen, Mechanismen und der Programmierschnittstelle eines Betriebssystems ist es nicht möglich, spezielle Systemdienste zu benutzen oder die Effizienz eines Systems zu optimieren. Es ist ein altes Ideal des Software Engineering, dass die hinter einer Schnittstelle stehende Implementierung als solche unwichtig ist und daher ignoriert werden soll. Auf Betriebssysteme bezogen würde dies heißen, nur die Programmierschnittstelle zu betrachten. Leider sind die dazugehörigen Beschreibungen praktisch immer minimal und befassen sich kaum mit den Konzepten der dahinter stehenden Implementierungen. Dies erschwert nicht nur ein tieferes Verständnis der Systemdienste, sondern kann auch zu einer inadäquaten Nutzung in komplexen Applikationen führen. Dieses Buch will Studierenden der Informatik und weiteren interessierten Personen die Grundlagen der Betriebssystemtheorie aus einer praktischen Perspektive näher bringen. Damit ist gemeint, dass nicht nur die Prinzipien von Betriebssystemen, sondern auch deren Nutzung bei der systemnahen Programmierung aufgezeigt werden. Dieser Ansatz entspricht der Idee des Bachelor-Studiums, das eine Berufsbefähigung nach drei Studienjahren anstrebt und die forschungsorientierte Ausbildung in die Master- und Doktoratstufe verschiebt.

Methodisch wird ein Weg zwischen der Betrachtung anfallender Probleme und ihren Lösungen auf einer theoretischen und einer praktischen Basis beschritten. Der Praxisbezug orientiert sich an den zwei am meisten verbreiteten Systemwelten, nämlich Unix und Windows. Kenntnisse der Prozessortechnik werden keine vorausgesetzt. Wo nötig werden die wichtigsten Prozessorgrundlagen erklärt, soweit sie für das Verständnis des Betriebssystems und der systemnahen Programmierung hilfreich sind. Die zahlreichen Beschreibungen von Systemfunktionen dienen dazu, die Programmbeispiele genau zu verstehen. Dies ist im Zeitalter der Java-Programmierung umso wichtiger, da eine gründliche Ausbildung in der Programmiersprache C nicht mehr vorausgesetzt werden kann, wenn auch C-Grundkenntnisse für dieses Buch notwendig sind. Im Weiteren verschaffen diese Beschreibungen einen Einstieg in die Systemdokumentationen, wie sie in Form der Unix-Handbuchseiten oder der MSDN-Beschreibungen (Microsoft Developer Network) für Windows vorliegen und erfahrungsgemäß für Neulinge nur schwer verständlich sind. Auf die Betrachtung von Computernetzen wird weitgehend verzichtet, da dieses Thema in einem Betriebssystembuch nur oberflächlich gestreift werden kann und ausgezeichnete Standardwerke zur Verfügung stehen. Dafür werden als Ergänzung die aktuellen Themen Multiprozessorsysteme, Handheld-Betriebssysteme und Virtualisierungstechnologien vorgestellt.

Als Hilfe für Dozierende stehen auf der Buch-Website http://unix.hsr.ch Übungsaufgaben mit Lösungen, alle Abbildungen des Buches und etliche Vorlesungsfolien in elektronischer Form zur Verfügung. Abschließend bedanke ich mich bei allen Personen, die mir bei der Realisierung dieses Buchprojektes geholfen haben.

Eduard GlatzUrdorf, im August 2005

Neuheiten in der zweiten Auflage

Eine neue Auflage erlaubt nicht nur das Aktualisieren schnell veralteter Informationen, sondern auch das Einbringen von Erfahrungen und zusätzlichen Themen. Aktualisiert haben wir die Informationen zu Windows 7, Windows CE und Symbian OS. Die ausführlichen tabellarischen Beschreibungen zu den Systemfunktionen sind nun nicht mehr im Buch, sondern in einer PDF-Datei zusammengefasst auf der Buch-Website (http://unix.hsr.ch) verfügbar. Auf vielfältigen Wunsch wurde das Literaturverzeichnis ausgedehnt. Neue Themen betreffen u.a. die Linux-basierten Smartphone-Betriebssysteme Android, WebOS und Maemo, die Systemprogrammierung aus C++, Java und .NET-Sprachen, die Bauweise von SSD (Solid State Disks), spezielle Dateisystemtechnologien (Schattenkopie, Disk Scheduling) und die Vermeidung von Synchronisationsengpässen.

Eduard GlatzUrdorf, im Februar 2010

Inhaltsverzeichnis

1      Einführung

1.1    Zweck

1.2    Definitionen

1.3    Einordnung im Computersystem

1.4    Betriebssystemarten

1.4.1    Klassische Einteilungen

1.4.2    Moderne Einteilungen

1.4.3    Geschichte

1.5    Betriebssystemarchitekturen

1.5.1    Architekturformen

1.5.2    Benutzer-/Kernmodus

1.5.3    Monolithische Systeme

1.5.4    Geschichtete Systeme

1.5.5    Mikrokernsysteme (Client/Server-Modell)

1.5.6    Multiprozessorsysteme

1.5.7    Verteilte Betriebssysteme

1.5.8    Beispiele von Systemarchitekturen

1.5.9    Zukünftige Systemarchitekturen aus Sicht der Forschung

2      Programmausführung und Hardware

2.1    Rechner- und Prozessorgrundlagen

2.1.1    Grundmodell eines Rechners

2.1.2    Befehlsverarbeitung in der CPU

2.1.3    Prozessoraufbau

2.1.4    Allgemeine Prozessorregister (general purpose registers)

2.1.5    Steuerregister (control registers)

2.2    Grundlagen des Adressraums

2.2.1    Adressraumtypen

2.2.2    Bytereihenfolge (byte ordering)

2.2.3    Adressraumbelegungsplan (memory map)

2.2.4    Ausrichtungsregeln im Adressraum

2.2.5    Adressraumbelegung durch Programme

2.2.6    Adressraumnutzung durch C-Programme

2.3    Grundlagen der Programmausführung

2.3.1    Quell- und Binärcode

2.3.2    Programmausführung und Programmzähler (PC)

2.3.3    Funktionsweise des Stapels und Stapelzeigers (SP)

2.3.4    Funktion des Programmstatusworts (PSW)

2.3.5    Programmunterbrechungen (interrupts)

2.3.6    Privilegierte Programmausführung (Benutzer-/Kernmodus)

2.4    Unterprogrammmechanismen

2.4.1    Unterprogrammaufruf und Komplettierung

2.4.2    Formen des Unterprogrammaufrufs

2.4.3    Parameterübergabe beim Unterprogrammaufruf

2.4.4    Realisierung der Parameterübergabe und lokale Variablen

3      Systemprogrammierung

3.1    Wahl der Systemprogrammiersprache

3.1.1    Mischsprachenprogrammierung

3.1.2    Programmiersprache C++

3.1.3    Java Native Interface (JNI)

3.1.4    Microsoft .NET-Sprachen

3.2    Laufzeitsystem der Programmiersprache C

3.3    Unterprogrammtechniken

3.3.1    Formale und aktuelle Parameter

3.3.2    Idempotente Unterprogramme

3.4    Grundlagen der Systemprogrammierung

3.4.1    Dienstanforderung und Erbringung

3.4.2    Dienstparameter und Resultate

3.4.3    Umgebungsvariablenliste (environment list)

3.4.4    Dateideskriptoren & Handles

3.4.5    Systemdatentypen

3.4.6    Anfangsparameter für Prozesse

3.4.7    Beendigungsstatus von Programmen

3.4.8    Fehlerbehandlung

3.4.9    Programmierung für 32- und 64-Bit-Systeme

3.5    Systemprogrammierschnittstellen

3.5.1    Aufrufverfahren

3.5.2    Unix-Programmierschnittstelle

3.5.3    Windows-Programmierschnittstelle

4      Prozesse und Threads

4.1    Parallelverarbeitung

4.1.1    Darstellung paralleler Abläufe

4.1.2    Hardware-Parallelität

4.1.3    Software-Parallelität

4.1.4    Begriffe

4.2    Prozessmodell

4.2.1    Grundprinzip

4.2.2    Prozesserzeugung und Terminierung

4.2.3    Prozesse unter Unix

4.2.4    Funktionsweise der Unix-Shell

4.2.5    Prozesse & Jobs unter Windows

4.2.6    Vererbung unter Prozessen

4.2.7    Systemstart und Prozesshierarchie

4.2.8    Ausführungsmodelle für Betriebssysteme

4.3    Threads

4.3.1    Thread-Modell

4.3.2    Vergleich Prozesse zu Threads

4.3.3    Implementierung des Multithreading

4.3.4    Threads & Fibers unter Windows

4.3.5    Threads unter Unix

4.3.6    Anwendungsprobleme

4.3.7    Task-Konzept

4.4    Prozessorzuteilungsstrategien

4.4.1    Quasiparallelität im Einprozessorsystem

4.4.2    Prozess- und Thread-Zustände

4.4.3    Konzeptionelle Prozessverwaltung

4.4.4    Zuteilungsstrategien

4.4.5    Multiprozessor-Scheduling

4.4.6    POSIX-Thread-Scheduling

4.4.7    Java-Thread-Scheduling

4.4.8    Scheduling unter Windows

4.4.9    Scheduling unter Unix

5      Synchronisation von Prozessen und Threads

5.1    Synchronisationsbedarfe und Lösungsansätze

5.1.1    Problem der Ressourcenteilung

5.1.2    Verlorene Aktualisierung (lost update problem)

5.1.3    Inkonsistente Abfrage (inconsistent read)

5.1.4    Absicherung mit Selbstverwaltung – naiver Ansatz

5.1.5    Absicherung mit Selbstverwaltung – korrekter Ansatz

5.1.6    Absicherung mit Systemmitteln

5.2    Semaphore

5.2.1    Semaphortypen

5.2.2    Implementierungsfragen

5.3    Anwendung der Semaphore

5.3.1    Absicherung kritischer Bereiche (mutual exclusion)

5.3.2    Synchronisation von Abläufen (barrier synchronization)

5.3.3    Produzenten & Konsumenten (producer and consumer)

5.3.4    Leser & Schreiber (readers and writers)

5.3.5    Problem der Prioritätsumkehrung (priority inversion)

5.3.6    Weitere Anwendungsprobleme

5.4    Implementierungen von Semaphoren

5.4.1    Semaphore unter Unix

5.4.2    Semaphore unter Windows

5.5    Unix-Signale

5.5.1    Idee & Grundprinzip der Unix-Signale

5.5.2    Programmierung der Signale

5.5.3    Signale im Multithreading

5.5.4    Realtime-Signale

5.6    Verklemmungsproblematik (deadlocks)

5.6.1    Ursache

5.6.2    Deadlock-Bedingungen

5.6.3    Lösungsansätze und ihre Beurteilung

5.7    Vermeidung von Synchronisationsengpässen

5.7.1    Granularität der Absicherung

5.7.2    Replikation der abgesicherten Ressource

6      Kommunikation von Prozessen und Threads

6.1    Überblick über Synchronisation und Kommunikation

6.2    Nachrichtenbasierte Verfahren

6.2.1    Allgemeine Aspekte

6.2.2    Unix-Pipes

6.2.3    Windows-Pipes

6.2.4    Unix Message Queues

6.2.5    Windows-Messages

6.2.6    Windows-Mailslots

6.3    Speicherbasierte Verfahren

6.3.1    Gemeinsamer Speicher unter Windows

6.3.2    Gemeinsamer Speicher unter Unix

6.4    Monitor

6.4.1    Grundprinzip

6.4.2    Java-Monitor

6.4.3    Monitornachbildung mit Bedingungsvariablen

6.5    Rendezvous

6.5.1    Grundprinzip

6.5.2    Synchronisation in Client/Server-Systemen (barber shop)

6.6    Rechnerübergreifende Interprozesskommunikation

6.6.1    Netzwerksoftware

6.6.2    Berkeley-Sockets

6.6.3    Remote Procedure Call (RPC)

6.6.4    Überblick über Middleware

7      Ein- und Ausgabe

7.1    Peripherie

7.1.1    Einordnung im Rechnermodell

7.1.2    Begriffsdefinitionen

7.2    Ein-/Ausgabeabläufe

7.2.1    Programmgesteuerte Ein-/Ausgabe

7.2.2    Ein-/Ausgabe mittels Programmunterbrechungen

7.2.3    Ein-/Ausgabe mittels DMA

7.2.4    Ein-/Ausgabearten im Vergleich

7.3    Ein-/Ausgabesystem

7.3.1    Treiber

7.3.2    Geräteverwaltung

7.3.3    Treiberschnittstelle

7.3.4    Ein-/Ausgabeschnittstelle

7.3.5    Ein-/Ausgabepufferung

7.3.6    Treibermodell in Linux

7.3.7    Treibermodelle in Windows (WDM & WDF)

7.4    Massenspeicher

7.4.1    Wichtigste Massenspeicher

7.4.2    Eigenschaften von Festplattenlaufwerken

7.4.3    Eigenschaften von Festkörperlaufwerken (SSD)

7.4.4    Zugriffsplanung für Plattenspeicher (disk I/O scheduling)

7.4.5    Pufferung von Plattendaten (disk cache)

7.5    Benutzerinteraktion aus Systemsicht (Benutzeroberflächen)

7.5.1    Allgemeines

7.5.2    Systemarchitekturen

7.5.3    Programmiermodelle

7.5.4    Die Unix-Shell als Kommandointerpreter

7.5.5    Funktionsweise und Programmierung des X-Window-Systems

7.5.6    Funktionsweise und Programmierung des Windows-GUI

8      Speicherverwaltung

8.1    Speichersystem

8.1.1    Einordnung im Rechnermodell

8.1.2    Grundlegende Speicherprinzipien

8.1.3    Speicherhierarchie & Lokalitätsprinzip

8.1.4    Cache-Funktionsweise

8.2    Dynamische Speicherbereitstellung (Heap)

8.2.1    Verwaltungsalgorithmen

8.2.2    Grundprinzip der Speicherzuordnung

8.2.3    Übersicht Implementierungsvarianten

8.2.4    Variante A: Variable Zuordnungsgröße

8.2.5    Variante B: Feste Blockgrößen bzw. Größenklassen

8.2.6    Variante C: Mehrfache einer festen Blockgröße

8.2.7    Variante D: Buddy-System

8.2.8    Heap-Erweiterung

8.2.9    Heap-Management in Windows

8.3    Verwaltung von Prozessadressräumen

8.3.1    Adressraumnutzung durch Programme

8.3.2    Adressraumverwaltung durch das Betriebssystem

8.4    Realer Speicher

8.4.1    Monoprogrammierung

8.4.2    Multiprogrammierung mit Partitionen

8.4.3    Verfahren für knappen Speicher

8.5    Virtueller Speicher

8.5.1    Adressumsetzung

8.5.2    Seitenwechselverfahren (demand paging)

8.5.3    Speicherabgebildete Dateien

8.5.4    Gemeinsamer Speicher (shared memory)

9      Dateisysteme

9.1    Dateisystemkonzepte

9.1.1    Logische Organisation

9.1.2    Dateisystemfunktionen

9.1.3    Gemeinsame Dateinutzung

9.1.4    Speicherabgebildete Dateien

9.2    Realisierung von Dateisystemen

9.2.1    Konzeptionelles Modell

9.2.2    Blockspeicher als Grundlage

9.2.3    Organisationsprinzipien

9.3    UFS – traditionelles Unix-Dateisystem

9.3.1    Datenträgeraufteilung

9.3.2    Dateihaltung und Verzeichnisorganisation

9.3.3    Index Nodes (Inodes)

9.4    FAT– traditionelles Windows-Dateisystem

9.4.1    Datenträgeraufteilung

9.4.2    Aufbau der Belegungstabelle (FAT)

9.4.3    Verzeichnisdaten

9.5    NTFS – modernes Windows-Dateisystem

9.5.1    Entstehung und Eigenschaften

9.5.2    Logische Struktur und Inhalt einer NTFS-Partition

9.5.3    NTFS-Streams

9.5.4    Dateispeicherung

9.5.5    Dateiverzeichnisse

9.6    ZFS – zukunftweisendes Dateisystem

9.6.1    Datenträgerverwaltung

9.6.2    Datenintegrität

9.6.3    Pufferung und Deduplizierung

9.6.4    Interoperabilität

9.7    Netzwerkdateisysteme

9.7.1    Logische Sicht

9.7.2    Implementierung

9.7.3    NFS – Network File System in Unix

9.7.4    SMB – Netzwerkdateisystem in Windows

9.8    Spezielle Dateisystemtechnologien

9.8.1    Protokollierende Dateisysteme

9.8.2    Schattenkopie

9.8.3    Disk Scheduling

9.9    Datenträgerpartitionierung

9.9.1    Anwendungsbereiche

9.9.2    Master Boot Record (MBR)

9.9.3    GUID Partition Table (GPT)

10    Programmentwicklung

10.1  Software-Entwicklungswerkzeuge

10.1.1    Ablauf der Programmübersetzung

10.1.2    Darstellung von Übersetzungsvorgängen mittels T-Notation

10.1.3    Automatisierte Übersetzung

10.1.4    Versionsverwaltung

10.2  Adressraumbelegung und Relokation

10.2.1    Storage Class

10.2.2    Programmorganisation in Sektionen

10.2.3    Relokation von Programmen

10.3  Programmbibliotheken

10.3.1    Grundlagen und Begriffe

10.3.2    Anwendungsbereiche

10.3.3    Programmbibliotheken unter Unix

10.3.4    Programmbibliotheken unter Windows

10.4  Skriptprogrammierung unter Unix

10.4.1    Anwendungsbereiche

10.4.2    Die Shell als Programminterpreter

10.4.3    Portabilität und Kompatibilität

10.4.4    Erstellung von Skriptprogrammen

10.4.5    Ausführung von Skriptprogrammen

10.4.6    Elemente der Skriptsprache

10.4.7    Shell-Befehle

10.4.8    Shell-Variablen

10.4.9    Stringoperatoren für Shell-Variable

10.4.10  Metazeichen

10.4.11  Synonyme und Funktionen

10.4.12  Bedingte Tests (conditional tests)

10.4.13  Arithmetik

10.4.14  Kontrollstrukturen für Skripte

11    Sicherheit

11.1  Schutzziele

11.2  Autorisierung und Zugriffskontrolle

11.2.1    Grundlagen und Begriffe

11.2.2    Schutzdomänenkonzept

11.2.3    Schutzstrategien

11.3  Hochsichere Betriebssysteme

11.4  Sicherheit unter Unix

11.5  Sicherheit unter Windows

12    Virtualisierung

12.1  Anwendungsbereiche

12.2  Virtualisierungstypen

12.2.1    Virtuelle Prozessoren

12.2.2    Virtuelle Prozessumgebungen

12.2.3    Virtuelles Betriebssystem

12.2.4    Virtueller Desktop

12.2.5    Virtuelle Ressourcen

12.2.6    Sandboxing (virtuelles Laufzeitsystem)

12.2.7    Virtuelle Computer (Stufe Computerhardware)

12.3  Virtual Machine Monitor bzw. Hypervisor

12.3.1    Anforderungen

12.3.2    VMM-Funktionsweise

12.3.3    VMM-Typen

12.4  Einsatzgebiete

13    Mobile Betriebssysteme

13.1  Gemeinsame Eigenschaften

13.1.1    Anforderungen durch die Plattform

13.1.2    Middleware als Betriebssystem

13.2  Google Android

13.2.1    Überblick

13.2.2    Architektur

13.2.3    System- und Applikationsstart

13.2.4    Lebenszyklus von Applikationen

13.2.5    Nachrichtensystem

13.3  Apple iOS

13.4  Microsoft Windows Phone 8

13.5  Mozilla Firefox OS

A    Anhang

A.1    Maßeinheiten und Darstellungen

A.1.1    Maßeinheiten in der Informatik

A.1.2    Darstellung von Bitmustern

A.1.3    Oktal- und Hexadezimalzahlen

A.1.4    Kennzeichnung der Zahlensysteme

A.1.5    Rechnerinterne Zahlendarstellungen

A.1.6    Textzeichensätze

A.2   Instruktionssatz der Intel x86-Prozessoren

Literaturhinweise

Index

1 Einführung

Lernziele

Sie erklären den Zweck und die Rolle eines modernen Betriebssystems.Sie erkennen wie Rechnerressourcen durch Applikationen genutzt werden, wenn sie das Betriebssystem verwaltet.Sie beschreiben die Funktionen eines aktuellen Betriebssystems in Bezug auf Benutzbarkeit, Effizienz und Entwicklungsfähigkeit.Sie erklären die Vorteile abstrakter Schichten und ihrer Schnittstellen in hierarchisch gestalteten Architekturen.Sie analysieren die Kompromisse beim Entwurf eines Betriebssystems.Sie erläutern die Architektureigenschaften monolithischer, geschichteter, modularer und Mikrokernsysteme.Sie stellen netzwerkfähige, Client/Server- und verteilte Betriebsysteme einander gegenüber und vergleichen diese.

Als Einstieg in das Thema legen wir fest, welchen Zwecken ein Betriebssystem dient, wie es sich als Begriff definieren lässt und wo es in einem Rechner einzuordnen ist. Danach diskutieren wir die Anforderungen an den Betriebssystementwurf, mögliche Architekturen und weiterführende Ideen aus der Forschung.

1.1 Zweck

Der Begriff »Betriebssystem« kann unterschiedlich aufgefasst werden. Beispielsweise über die Frage: Was leistet ein Betriebssystem? Zwei Grundfunktionen sind:

Erweiterte Maschine: Das Betriebssystem realisiert von vielen Applikationen geichartig genutzte Teilfunktionen als standardisierte Dienste. Damit wird die Applikationsentwicklung einfacher als beim direkten Zugriff auf die blanke Rechnerhardware. Die erweiterte Maschine ist eine Abstraktion der Hardware auf hohem Niveau und entspringt einer Top-down-Sicht.

Betriebsmittelverwalter: Das Betriebssystem verwaltet die zeitliche und räumliche Zuteilung von Rechnerressourcen. Im Mehrprogrammbetrieb wird im Zeitmultiplex der Prozessor zwischen verschiedenen ablauffähigen Programmen hin und her geschaltet. Im Raummultiplex wird der verfügbare Speicher auf geladene Programme aufgeteilt. Ausgehend von den Ressourcen entspricht dies einer Bottom-up-Sicht.

Detaillierter betrachtet erfüllt ein Betriebssystem sehr viele Zwecke. Es kann mehrere oder sogar alle der folgenden Funktionalitäten realisieren:

Hardwareunabhängige Programmierschnittstelle: Programme können unverändert auf verschiedenen Computersystemen ablaufen (auf Quellcodeebene gilt dies sogar für unterschiedliche Prozessorfamilien mit differierenden Instruktionssätzen).

Geräteunabhängige Ein-/Ausgabefunktionen: Programme können ohne Änderung unterschiedliche Modelle einer Peripheriegeräteart ansprechen.

Ressourcenverwaltung: Mehrere Benutzer bzw. Prozesse können gemeinsame Betriebsmittel ohne Konflikte nutzen. Die Ressourcen werden jedem Benutzer so verfügbar gemacht, wie wenn er exklusiven Zugriff darauf hätte.

Speicherverwaltung: Mehrere Prozesse/Applikationen können nebeneinander im Speicher platziert werden, ohne dass sie aufeinander Rücksicht nehmen müssen (jeder Prozess hat den Speicher scheinbar für sich allein). Zudem wird bei knappem Speicher dieser optimal auf alle Nutzer aufgeteilt.

Massenspeicherverwaltung (Dateisystem): Daten können persistent gespeichert und später wieder gefunden werden.

Parallelbetrieb (Multitasking): Mehrere Prozesse können quasiparallel ablaufen. Konzeptionell stehen mehr Prozessoren zur Verfügung als in der Hardware vorhanden, indem versteckt vor den Anwendungen parallele Abläufe, soweit nötig, sequenzialisiert werden.

Interprozesskommunikation: Prozesse können mit anderen Prozessen Informationen austauschen. Die Prozesse können dabei entweder auf dem gleichen Rechner ablaufen (lokal) oder auf verschiedenen Systemen (verteilt) ausgeführt werden.

Sicherheitsmechanismen: Es können sowohl Funktionen für die Datensicherung, d.h. die fehlerfreie Datenverarbeitung, als auch Datenschutzkonzepte implementiert sein. Der Datenschutz kann zum Beispiel durch das explizite Löschen freigegebener Bereiche im Hauptspeicher und auf Plattenspeichern sicherstellen, dass empfindliche Informationen nicht in falsche Hände fallen. Die Zugangskontrolle zum Rechner (Anmeldedialoge, Benutzerverwaltung) dient ebenfalls dem Datenschutz.

Bedienoberflächen: Moderne Betriebssysteme realisieren grafische Bedienoberflächen mit ausgeklügelten Bedienkonzepten, die Dialoge mit dem System und Anwendungen komfortabel gestalten. Ergänzend existieren Eingabemöglichkeiten für Kommandozeilenbefehle, die geübten Benutzern sehr effiziente Dialogmöglichkeiten, z.B. zur Systemadministration, anbieten.

Die geräteunabhängige Ein-/Ausgabe war eine der wichtigsten Errungenschaften bei der erstmaligen Einführung von Betriebssystemen. Früher war es notwendig, dass Applikationen die Eigenheiten der angeschlossenen Peripheriegeräte im Detail kennen mussten. Mit einem Betriebssystem stehen hingegen logische Kanäle zur Verfügung, die Ein-/Ausgaben über standardisierte Funktionen bereitstellen (siehe Abb. 1–1). Die logischen Kanäle werden häufig mittels sprechender Textnamen identifiziert.

Abb. 1–1 Ein-/Ausgabe ohne und mit Betriebssystem

1.2 Definitionen

Leider existiert keine allgemein verbindliche Definition eines Betriebssystems. Welche Komponenten zu einem Betriebssystem gehören und welche nicht, lässt sich daher nicht endgültig festlegen. Nachfolgend sind drei unterschiedliche Definitionen stellvertretend vorgestellt, die dabei helfen, ein Betriebssystem zu charakterisieren. Eine erste, etwas schwer lesbare Definition nach DIN 44 300 beschreibt ein Betriebssystem wie folgt (Ausschnitt):

... die Programme eines digitalen Rechnersystems, die zusammen mit den Eigenschaften dieser Rechenanlage die Basis der möglichen Betriebsarten des Rechnersystems bilden und insbesondere die Abwicklung von Programmen steuern und überwachen.

Eine zweite, der Literatur entnommene Definition lautet:

Ein Betriebssystem ist eine Menge von Programmen, welche die Ausführung von Benutzerprogrammen auf einem Rechner und den Gebrauch der vorhandenen Betriebsmittel steuern.

Eine dritte Definition betrachtet das Betriebssystem als Ressourcenverwalter, wobei die Ressource hauptsächlich die darunter liegende Hardware des Rechners ist. Ein Computersystem lässt sich hierbei als eine strukturierte Sammlung von Ressourcenklassen betrachten, wobei jede Klasse durch eigene Systemprogramme kontrolliert wird (siehe Tab. 1–1).

 

Zentrale Ressourcen

Periphere Ressourcen

Aktive Ressourcen

Prozessor(en)

Kommunikationseinheiten

1. Endgeräte (Tastaturen, Drucker, Anzeigen, Zeigegeräte etc.)

2. Netzwerk (entfernt, lokal) etc.

Passive Ressourcen

Hauptspeicher

Speichereinheiten

1. Platten

2. Bänder

3. CD-ROM/DVD etc.

Tab. 1–1 Ressourcenklassen

Ein Betriebssystem lässt sich auch mit einer Regierung (government) vergleichen. Wie diese realisiert das Betriebssystem keine nützliche Funktion für sich alleine, sondern stellt eine Umgebung zur Verfügung, in welcher andere Beteiligte nützliche Funktionen vollbringen können. Einige Autoren (z.B. K. Bauknecht, C. A. Zehnder) ziehen die Begriffe Systemsoftware bzw. Systemprogramme der Bezeichnung Betriebssystem vor. In diesem Sinne ist folgende Beschreibung dieser Autoren abgefasst:

»Die Systemprogramme, oft unter dem Begriff Betriebssystem zusammengefasst, lassen sich gemäß Abbildung 1–2 gruppieren.

Abb. 1–2 Softwaregliederung

Die eigentlichen Steuerprogramme sind für folgende Funktionen zuständig:

Steuerung aller Computerfunktionen und Koordination der verschiedenen zu aktivierenden Programme.

Steuerung der Ein-/Ausgabeoperationen für die Anwendungsprogramme.

Überwachung und Registrierung der auf dem Computersystem ablaufenden Aktivitäten.

Ermittlung und Korrektur von Systemfehlern.«

Auffallend bei dieser Definition ist der Einbezug von Übersetzern (Compiler, Binder), Testhilfen und Dienstprogrammen. Für klassische Betriebssysteme (z.B. Unix und GNU-Tools) trifft dies vollumfänglich zu, während moderne Betriebssysteme oft die Bereitstellung von Übersetzungstools irgendwelchen Drittherstellern überlassen bzw. diese als separate Applikation ausliefern (z.B. Windows und Visual Studio).

1.3 Einordnung im Computersystem

In einem Rechner stellt das Betriebssystem eine Softwareschicht dar, die zwischen den Benutzerapplikationen einerseits und der Rechnerhardware andererseits liegt (siehe Abb. 1–3). Das Betriebssystem selbst besteht aus einem Betriebssystemkern und einer Sammlung von Programmen, die Betriebssystemdienste bereitstellen. Je nach Betrachtungsweise zählen dazu auch Programme zur Softwareentwicklung, wie Editoren und Compiler. Häufig wird nur der Betriebssystemkern als Betriebssystem bezeichnet, während der Begriff Systemprogramme für das Gesamtpaket inklusive der Programmentwicklungswerkzeuge benutzt wird.

Abb. 1–3 Schichtenmodell eines Rechners

Das Betriebssystem setzt auf der Prozessorarchitektur auf, die durch einen Satz von Maschinenbefehlen und den Registeraufbau charakterisiert wird (sog. Instruktionssatzarchitektur, ISA). Die Systemplatine mit all ihren Bausteinen und den angeschlossenen Peripheriegeräten stellt die Arbeitsumgebung des Prozessors dar. Diese muss ebenfalls dem Betriebssystem in all ihren Details bekannt sein. Von zentraler Bedeutung für den Softwareentwickler ist die Programmierschnittstelle des Betriebssystems (Application Programming Interface, API). Die dort zur Verfügung gestellte Funktionalität kann in Benutzerapplikationen eingesetzt werden. Aus Anwendungssicht unterscheiden sich Betriebssysteme in der Programmierschnittstelle, in den unterstützten Dateiformaten für ausführbare Dateien, im Funktionsumfang, in der Bedienoberfläche und der Maschinensprache, in die ihr Code übersetzt wurde. Zudem kann oft der Funktionsumfang, d.h. die installierten Systemteile, während des Installationsvorgangs unterschiedlich gewählt werden.

Wie bereits erwähnt, setzt das Betriebssystem direkt auf der Rechnerhardware auf und muss diese daher genau kennen. Denn es verwaltet folgende Hardwareelemente:

Prozessor

Arbeitsspeicher (main memory)

Massenspeicher (mass storage), z.B. Festplatten, CD-ROM, DVD

Benutzerschnittstelle (user interface)

Kommunikations- und andere Peripheriegeräte (LAN, WLAN usw.)

Die Betriebssystemtheorie beruht damit auf den Prinzipien der Computertechnik. Computertechnik befasst sich mit:

1. Rechner-Grundmodellen (Von-Neumann-, Harvard-Architektur)

2. Funktionsweise des Prozessors (Instruktionssatz, Registeraufbau)

3. Speichern und ihren Realisierungen (Primär- und Sekundärspeicher)

4. Peripheriegeräten (Tastatur, Bildschirm, Schnittstellenbausteine usw.)

Um die hardwarenahen Teile des Betriebssystems oder nur schon den exakten Ablauf der Programmausführung zu verstehen, ist es daher unerlässlich, sich mit ein paar Details der Computertechnik zu befassen. Einige computertechnische Funktionsweisen, soweit sie für das Verständnis des Betriebssystems nötig sind, werden an passenden Stellen im Buch erklärt. Für weiter gehende Realisierungsdetails der Hardwareelemente sei auf entsprechende Spezialliteratur verwiesen.

1.4 Betriebssystemarten

Ein Betriebssystem stellt eine Umgebung zur Verfügung, in der Anwendungsprogramme ablaufen können. Eine Ablaufumgebung kann recht unterschiedlich realisiert sein:

Als Laufzeitsystem (Run-Time System) einer Programmiersprache (ADA, Modula-2)

Als virtuelle Maschine zur Ausführung eines Zwischencodes (z.B. Java Virtual Machine, .NET Common Language Runtime)

Als Basisprogramm eines Rechners (z.B. Unix, Windows)

Als (sprachunabhängige) Programmbibliothek (z.B. Mikrokontroller-Betriebssysteme)

Häufig findet man Kombinationen dieser vier Varianten. Beispielsweise können Sprach-Laufzeitsysteme Fähigkeiten zur Verfügung stellen, die ansonsten nur Bestandteil von Betriebssystemen sind. Dies beinhaltet Multitasking-Funktionen (z.B. in Java, Ada, Modula-2) und die Speicherverwaltung (verschiedene Sprachen).

1.4.1 Klassische Einteilungen

Eine elementare Klassifizierung von Betriebssystemen basiert auf folgenden Anwendungsarten:

Stapelverarbeitung (batch processing): Typisches Merkmal ist, dass Programme angestoßen werden, aber ansonsten keine nennenswerte Benutzerinteraktion stattfindet. Die auszuführenden Befehle sind stattdessen in einer Stapeldatei abgelegt, deren Inhalt fortlaufend interpretiert wird. Klassische Großrechnerbetriebssysteme werden auf diese Art und Weise genutzt, z.B. zur Ausführung von Buchhaltungsprogrammen über Nacht.

Time-Sharing-Betrieb: Die zur Verfügung stehende Rechenleistung wird in Form von Zeitscheiben (time slices, time shares) auf die einzelnen Benutzer aufgeteilt mit dem Ziel, dass jeder Benutzer scheinbar den Rechner für sich alleine zur Verfügung hat. Historisch gesehen sind Time-Sharing-Systeme die Nachfolger bzw. Ergänzung der Batch-Systeme mit der Neuerung, dass sie Benutzer interaktiv arbeiten lassen (Dialogbetrieb).

Echtzeitbetrieb: Die Rechenleistung wird auf mehrere Benutzer oder zumindest Prozesse aufgeteilt, wobei zeitliche Randbedingungen beachtet werden. Oft sind Echtzeitsysteme reaktive Systeme, indem sie auf gewisse Signale aus der Umgebung (Interrupts, Meldungen) möglichst rasch reagieren.

1.4.2 Moderne Einteilungen

Moderne Betriebssysteme fallen mehr oder weniger in die Gruppe der Echtzeitsysteme, weswegen letztere für uns im Vordergrund stehen. Eine ergänzende Klassifizierung unterteilt Betriebssysteme nach unterstützter Rechnerstruktur:

Einprozessorsysteme

Multiprozessorsysteme

Verteiltes System

Je nach Auslegung kann ein Betriebssystem eine oder mehrere dieser drei Rechnerstrukturen unterstützen. Ergänzend sei noch bemerkt, dass populäre Betriebssysteme netzwerkfähig (networked operating system) sind, auch wenn sie nicht verteilt ablaufen. Beispielsweise unterstützen sie verbreitete Netzwerkprotokolle, die Anbindung entfernter Laufwerke und – teilweise konfigurierbar – eine zentralisierte Benutzerverwaltung.

Beispiele:

Windows unterstützt Einprozessorsysteme und Multiprozessorsysteme. Das Betriebssystem Amoeba ermöglicht transparentes Arbeiten auf einem verteilten System. Für den Benutzer präsentiert es sich wie ein Einzelrechner, besteht in der Tat aber aus mehreren über ein Netzwerk verbundenen Computern.

1.4.3 Geschichte

Abbildung 1–4 zeigt eine kleine Auswahl an Entwicklungslinien gängiger Betriebssysteme, deren Geschichte wir kurz charakterisieren.

Abb. 1–4 Entwicklungslinien einiger gängiger Betriebssysteme

Ein erstes Betriebssystem für Großrechner war das rudimentäre IBSYS, das Stapelverarbeitung ermöglichte. Umfangreicher war bereits das OS/360 von IBM, das in weiterentwickelter Form als z/OS auf heutigen Mainframe-Systemen läuft. Anfänglich hat es nur die Stapelverarbeitung unterstützt, wurde aber bald durch die TSO (Time Sharing Option) für den Dialogbetrieb ergänzt. Unahängig davon entstand das CTSS (Compatible Time Sharing System), das den Dialogbetrieb auf Großrechnern bereits sehr früh erlaubte. Sein Nachfolger war MULTICS (Multiplexed Information and Computing Service), ein Konsortiumsprojekt, das letzlich nicht sehr erfolgreich war, jedoch viele neue Konzepte realisierte. Darin war es ein Vorbild für das ursprüngliche Unix, das jedoch ein wesentlich kompakterer Entwurf war, der die Komplexität des MULTICS vermied. Unix hat über viele Zwischenschritte die heutigen Systeme Linux, Oracle Solaris und Apple OS Xgeprägt. Das BSD (Berkeley Software Distribution) Unix existiert heute als FreeBSD, NetBSD und OpenBSD in geringer Verbreitung weiter. Separate Entwicklungslinien gelten für das Microsoft Windows. Ursprünglich hat es als grafische Oberfläche für MS-DOS begonnen, wurde aber immer unabhängiger davon. Separat zu dieser originären Windows-Linie entstand das Windows NT, das von den DEC VMS (Virtual Memory System) Minicomputer-Betriebsystemen abgeleitet wurde, jedoch die API des Microsoft Windows realisierte. Mit dem Windows XP wurde die originäre Windows-Linie beendet, womit der schwache Unterbau des MS-DOS verschwand. Das VMS existiert als OpenVMS noch heute, ist aber nur minimal verbreitet.

1.5 Betriebssystemarchitekturen

Beim Entwurf eines Betriebssystems sind viele Anforderungen in Einklang zu bringen, die nicht widerspruchsfrei sind, weswegen Kompromisse nötig sind. Neben der Realisierung der in Abschnitt 1.1 beschriebenen Kernfunktionalitäten sind folgende exemplarische Entwurfsziele zu berücksichtigen:

Fehlerfreiheit des Codes: z.B. durch minimale Komplexität des Quellcodes

Einfache Operationen (auf API und für alle Schnittstellen)

Erweiterbarkeit (extensibility)

Skalierbarkeit (scalability)

Orthogonalität: Operationen wirken gleich auf verschiedenartigen Objekten

Robuste Betriebsumgebung (»crash-proof«, »reliable«)

Einhaltung der Sicherheitsziele (mehrere Anforderungsstufen denkbar)

Portabilität (Unterstützung verschiedenartiger Plattformen)

Echtzeitfähigkeit (z.B. für Multimedia-Anwendungen)

Effizienz (schnelle Diensterbringung, minimaler Ressourcenbedarf)

Weiterentwickelbarkeit: Trennung von Strategie (policy) und Mechanismus (mechanism)

Auf der Suche nach einem optimalen Entwurf sind verschiedenartige Architekturideen entwickelt worden. Diese werden nachfolgend kurz beschrieben und diskutiert. Als Blick in die mögliche Zukunft des Betriebssystembaus wird eine kurze Zusammenfassung einiger interessanter Forschungsarbeiten zum Thema vorgestellt.

1.5.1 Architekturformen

Solange es lediglich um die Systemprogrammierung geht, ist eine Blackbox-Betrachtung des Betriebssystems ausreichend. Nach außen ist damit nur die Programmierschnittstelle sichtbar, jedoch nicht das Systeminnere (siehe A in Abb. 1–5). Dies entspricht einem klassischen Ideal des Software Engineering, das aussagt, dass die Schnittstelle das Maß aller Dinge ist und die Implementierung dahinter beliebig austauschbar sein soll. Dennoch kann es hilfreich sein, die Innereien eines Betriebssystems zu kennen, damit man nicht Gefahr läuft, gegen die Implementierung zu programmieren. Dies könnte zum Beispiel in einem überhöhten Ressourcenverbrauch oder einer unbefriedigenden Ausführungsgeschwindigkeit resultieren. Daneben ist es stets interessant, unter die »Motorraumhaube« eines Betriebssystems zu gucken. Mit anderen Worten, es geht um eine Whitebox-Betrachtung (siehe B in Abb. 1–5).

Abb. 1–5 Black- und Whitebox-Betrachtung (Beispiel: Unix)

Damit verbunden sind die Entwurfs- und Konstruktionsprinzipien, die einen erst dann interessieren, wenn man über die Blackbox-Betrachtung hinausgeht. Eine wesentliche Frage ist dabei die Art und Weise, wie die Betriebssystemsoftware strukturiert ist. In der Theorie kennt man drei Grundstrukturen, denen sich konkrete Betriebssysteme zuordnen lassen: monolithische, geschichtete und Mikrokernsysteme. Diese werden durch Strukturen für Multiprozessor- und Verteilte Systeme ergänzt. Zuerst soll jedoch auf die Funktionsweise und Bedeutung der Benutzer-/Kernmodus-Umschaltung eingegangen werden, da sie bei der Betrachtung dieser Strukturen eine zentrale Rolle spielt.

1.5.2 Benutzer-/Kernmodus

Als hardwarenahe Softwarekomponente ist ein Betriebssystem eng mit den Möglichkeiten der unterliegenden Plattform verbunden. Es haben sich mit den Jahren unterschiedliche Leistungsklassen von Prozessoren und zugehöriger Hilfslogik etabliert:

Mikrocontroller: Es handelt sich hierbei um einfache Mikroprozessoren, die primär in sehr einfachen eingebetteten Systemen (embedded systems) eingesetzt werden. Um die Kosten gering zu halten, verfügen sie lediglich über einen Prozessor mit wenig oder gar keinen weiterführenden Mechanismen zur Unterstützung eines Betriebssystems. Hingegen sind sie zusammen mit verschiedenen Peripherieeinheiten (E/A, Kommunikation, Zeitgeber usw.) in einen einzigen Halbleiterchip integriert, was Kosten und Platz spart. Beispiele: Intel 8051, Siemens 80C166, Motorola HC6805

Einfache Universalmikroprozessoren: Sie entsprechen in vielen Punkten den Mikrocontrollern, enthalten jedoch auf dem gleichen Chip keine Peripherieeinheiten. In dieser Gruppe finden wir vor allem die älteren Prozessortypen. Beispiele: Intel 8080/85/86, Motorola 6800, 68000

Leistungsfähige Universalmikroprozessoren: Diese Rechnerchips verfügen über eine ganze Reihe von Hardwareelementen, die ein Betriebssystem unterstützen. Dazu zählen eine MMU (Memory Management Unit) und Mechanismen für einen privilegierten Betriebsmodus für die Systemsoftware (Privilegiensystem). Diese Prozessoren eignen sich nicht nur für Desktop-Systeme, Servermaschinen, Tablets und Smartphones, sondern auch für viele Embedded Systems, da sie die Verwendung angepasster Desktop-Betriebssysteme mit ihrer reichhaltigen Funktionalität erlauben.

Bei Universalmikroprozessoren werden durch das Privilegiensystem heikle Operationen und Zugriffe geschützt, damit ein Programmierfehler in einem Anwendungsprogramm nicht das ganze Computersystem durcheinanderbringt. Insbesondere bei Multitasking-Anwendungen und Multiuser-Betrieb (mehrere gleichzeitige Benutzer) ist ein solcher Schutz erwünscht. So wird in den meisten Betriebssystemen der Zugriff auf Hardwareteile mittels dieser Schutzfunktionen dem normalen Anwender (bzw. Benutzerapplikationen) verwehrt. Dazu dienen unterschiedliche CPU-Betriebsarten, wobei jede Betriebsart in ihren Pflichten und Rechten genau definiert ist. Im Minimum beinhaltet dies:

Einen Kernmodus (kernel mode, supervisor mode) mit »allen Rechten« für Betriebssystemcode

Einen Benutzermodus (user mode) mit »eingeschränkten Rechten« für Applikationscode

Das Ziel besteht darin, die Applikationen untereinander und den Betriebssystemcode gegen diese zu schützen. Dies bedeutet, dass eine Applikation nicht das ganze System lahmlegen kann. Komfortablere Lösungen unterstützen mehr als zwei Betriebsarten, die dann Privilegienstufen (privilege level) genannt werden. Die Möglichkeiten des Privilegiensystems werden stets in Kombination mit der Speicherverwaltung genutzt. So könnte die MMU dafür sorgen, dass nur im Kernmodus ein Zugriff auf Systemcode und Daten möglich ist (mehr Details dazu siehe Abschnitt 8.5). Den Benutzerprozessen ist mithilfe dieser hardwaregestützten Mechanismen in der Regel weder ein direkter Zugriff auf Hardwareteile noch ein Überschreiben von Systemcode oder Systemdaten möglich. Mit der Kenntnis der Fähigkeiten der Benutzer-/Kernmodus-Umschaltung lassen sich die nachfolgend aufgeführten drei Grundtypen von Aufbaustrukturen klarer in ihren Eigenschaften unterscheiden. Oberstes Ziel ist, das unabsichtliche Überschreiben von Systemdaten und Code zu verhindern, um unkontrollierte Systemabstürze zu vermeiden.

 

Benutzermodus

Kernmodus

Ausführbare Maschinenbefehle

Begrenzte Auswahl

Alle

Hardwarezugriff

Nein bzw. nur mithilfe des Betriebssystems

Ja, Vollzugriff

Zugriff auf Systemcode bzw. Daten

Keiner bzw. nur lesend

Exklusiv

Tab. 1–2 Vergleich zwischen Benutzer- und Kernmodus

Im Idealfall werden Schutzmechanismen auch für die Abschottung verschiedener Systemteile untereinander eingesetzt. Die Grenze des Sinnvollen ist allerdings darin zu sehen, dass eine teilweise lahmgelegte Systemsoftware aus Sicht des Anwenders oft nicht besser ist als ein Totalabsturz. Es kann jedoch manchmal nützlich sein, nicht vertrauenswürdige Teile der Systemsoftware, wie Erweiterungen oder Treiber von Drittherstellern, in ihrem Schadenspotenzial einzugrenzen. Ein sekundäres Ziel für den Einsatz der Benutzer-/Kernmodus-Umschaltung können Maßnahmen zur Eindämmung von Systemmanipulationen sein, die zum Ziel haben, vertrauenswürdige Daten zu missbrauchen. So ist Sicherheitssoftware fundamental von den Sicherheitseigenschaften einer Systemplattform abhängig, die softwareseitig durch das benutzte Betriebssystem gegeben ist.

1.5.3 Monolithische Systeme

Die Struktur dieser Systeme besteht darin, dass sie keine oder nur eine unklare Struktur haben (Abb. 1–6).

Abb. 1–6 Beispiel für eine monolithische Betriebssystemstruktur

Meist handelt es sich um evolutionär gewachsene Betriebssysteme, bei denen es anfänglich unwichtig war, einzelne Teilfunktionen klar mit Schnittstellen voneinander abzugrenzen. Beispiele dafür sind MS-DOS und ältere Unix-Varianten. Derartige Systeme können sehr effizient sein, da sich Schnittstellen frei wählen lassen. Sie sind jedoch schwierig wartbar, wenn ihre Struktur schlecht erkennbar ist.

Modulare Betriebsysteme stellen eine Erweiterung dar, bei der ausgewählte Komponenten derart mit definierten Schnittstellen versehen werden, dass sie den Zugriff auf unterschiedliche Implementierungsvarianten erlauben. Beispielsweise können so über dieselbe Dateisystemschnittstelle verschiedenartige Dateisystemformate unterstützt werden. Meist lassen sich Module dynamisch laden/entladen. Beispiele sind Linux und Oracle Solaris.

1.5.4 Geschichtete Systeme

Bei dieser Strukturierungsform sind die Betriebssystemfunktionen in viele Teilfunktionen gegliedert, die hierarchisch auf mehrere Schichten verteilt sind. Die Festlegung der Schichtenstruktur ist vom einzelnen Betriebssystem abhängig, eine Standardstruktur gibt es nicht.

Abb. 1–7 Beispiel einer geschichteten Struktur

Wie in Abbildung 1–7 zu sehen ist, bauen Funktionen einer höheren Schicht strikt nur auf Funktionen einer tieferen Schicht auf. Jede Schicht realisiert eine bestimmte Funktionsgruppe. Systemaufrufe passieren nach unten alle Schichten, bis sie auf die Hardware einwirken. Eingabedaten durchlaufen umgekehrt alle Schichten von unten bis oben zur Benutzerapplikation. Die Schicht 1 in Abbildung 1–7 könnte zum Beispiel eine Hardware-Abstraktionsschicht sein, die eine allgemeine Betriebssystemimplementierung auf eine bestimmte Hardwareplattform anpasst. Beispiele für geschichtete Betriebssysteme sind neuere Unix-Varianten und OS/2. Vorteile dieser Struktur sind, dass sich einzelne Schichten gegen andere Implementierungen austauschen lassen und die Sichtbarkeit der Module durch die Schichten eingegrenzt wird. Durch die Schichtenaufteilung besteht jedoch das Problem, dass manche Funktionen künstlich aufgeteilt werden müssen, da sie nicht eindeutig einer bestimmten Schicht zuordenbar sind.

1.5.5 Mikrokernsysteme (Client/Server-Modell)

Nur die allerzentralsten Funktionen sind in einem Kernteil zusammengefasst, alle übrigen Funktionen sind als Serverdienste separat realisiert (z.B. Dateidienste, Verzeichnisdienste). Der Mikrokern enthält lediglich die vier Basisdienste Nachrichtenübermittlung (message passing), Speicherverwaltung (virtual memory), Prozessorverwaltung (scheduling) und Gerätetreiber (device drivers). Diese sind dabei in ihrer einfachsten Form realisiert. Weiter gehende Funktionen sind in den Serverprozessen enthalten, die im Benutzermodus ausgeführt werden. Dadurch werden die komplexeren Systemteile in klar abgegrenzte Teile aufgesplittet, wovon man sich eine Reduktion der Komplexität verspricht. Da zudem ein Großteil des Betriebssystemcodes auf die Benutzerebene (Benutzermodus) verschoben wird, sind überschaubare zentrale Teile des Systems durch den Kernmodus gegen fehlerhafte Manipulationen geschützt. Ebenso ist es nur dem eigentlichen Kern erlaubt, auf die Hardware zuzugreifen. Beansprucht ein Benutzerprozess einen Systemdienst, so wird die Anforderung als Meldung durch den Mikrokern an den zuständigen Serverprozess weitergeleitet. Entsprechend transportiert der Mikrokern auch die Antwort an den anfordernden Prozess zurück (siehe Abb. 1–8). Vorteilhaft ist das derart verwendete Client/Server-Modell für verteilte Betriebssysteme. Für den Benutzerprozess bleibt es verborgen (transparent), ob der Serverprozess lokal oder an einem entfernten Ort ausgeführt wird.

Abb. 1–8 Mikrokern nach dem Client/Server-Prinzip

Kommerzielle Mikrokernbetriebssysteme verlagern neben den vier erwähnten Grunddiensten zur Effizienzsteigerung zusätzliche Funktionen in den Mikrokern. Betrachtet man beispielsweise den Datenverkehr zwischen einem Benutzerprozess und dem Displayserver (siehe Abb. 1–8), so muss für eine bestimmte Operation auf dem grafischen Desktop insgesamt viermal eine Umschaltung zwischen Benutzer- und Kernmodus stattfinden, was durch Verschiebung der Displayfunktionen in den Kern effizienter gelöst werden kann. Beispiele derartiger Systemarchitekturen sind das Mac OS X (basierend auf dem Mach Kernel und BSD Unix) sowie Amoeba.

1.5.6 Multiprozessorsysteme

Multiprozessorsysteme haben durch die Einführung von Multicore-CPUs eine große Verbreitung erfahren. Dabei teilen sich typischerweise alle Rechenkerne die Peripherie und den Hauptspeicher, weswegen man sie Shared-Memory-Multiprozessoren nennt.

Drei Betriebssystemtypen sind für diese Rechner denkbar:

1. Jede CPU hat ihr eigenes Betriebssystem:

Der Speicher wird in Partitionen (pro CPU/Betriebssystem) aufgeteilt.

2. Asymmetrische Multiprozessoren (asymmetric multiprocessing, AMP):

Das Betriebssystem läuft nur auf einer einzigen CPU (=Master), die Anwendungsausführung nutzt alle restlichen Prozessoren (=Slaves).

3. Symmetrische Multiprozessoren (symmetric multiprocessing, SMP):

Nur eine einzige Kopie des Betriebssystems liegt im Speicher. Diese ist von jeder CPU ausführbar.

Am einfachsten ist die Lösung, dass jede CPU ihr eigenes Betriebssystem hat und der Speicher partitionsweise den einzelnen CPUs zugeteilt wird, womit die einzelnen Prozessoren unabhängig voneinander arbeiten. Der Code des Betriebssystems (siehe Abb. 1–9) ist nur einmal im Speicher abgelegt, da unveränderlich. Infolge des fehlenden Lastausgleichs und der fixen Speicheraufteilung skaliert diese Lösung schlecht und hat deswegen keine nennenswerte Verbreitung gefunden.

Abb. 1–9 Jede CPU hat ein eigenes Betriebssystem.

Die Variante der asymmetrischen Multiprozessoren weist alle Betriebssystemaktivität einer bestimmten CPU zu, die damit der Chef (Master) wird. Die Anwenderprozesse laufen auf den restlichen CPUs, die man Slaves nennt (siehe Abb. 1–10). Vorteilhaft ist die Möglichkeit der flexiblen Zuteilung ablaufwilliger Prozesse an die einzelnen Slaves. Der wesentliche Nachteil ist aber der Flaschenhals, der durch den Master entsteht, da alle Systemaufrufe nur von dieser CPU bearbeitet werden. Nimmt man beispielsweise an, dass die Anwenderprozesse 13% der Zeit in Systemaufrufen verweilen, so entstehen infolge des Flaschenhalses bereits bei einem System mit 8 Prozessoren Wartezeiten. Es handelt sich also um eine Lösung, die nur für kleine Prozessoranzahlen sinnvoll ist.

Abb. 1–10 Asymmetrisches (Master/Slave-)Multiprozessorsystem

Die Lösung mit symmetrischen Multiprozessoren führt das Betriebssystem genau einmal im Speicher, und zwar sowohl den Code als auch die Daten (siehe Abb. 1–11). Systemaufrufe können von allen CPUs ausgeführt werden. Da die Daten des Betriebssystems für alle gemeinsam zugreifbar sind, entfällt damit der Flaschenhals der Master-CPU.

Allerdings stellt sich damit auch das Problem des koordinierten Zugriffs auf die Systemdaten, um Dateninkonsistenzen zu vermeiden (kritische Bereiche). Der einfachste Weg wäre der, dass zu jedem Zeitpunkt nur eine einzige CPU einen Systemaufruf ausführen darf. Damit wäre das Flaschenhalsproblem aber in einer neuen Form wieder vorhanden und die Leistung limitiert. In der Praxis genügt es aber, wenn die einzelnen Systemtabellen separat abgesichert werden. Systemaufrufe auf unterschiedlichen Tabellen können dann parallel stattfinden. Da viele Systemtabellen in verschiedener Beziehung voneinander abhängen, ist die Realisierung eines derartigen Betriebssystems sehr anspruchsvoll (z.B. Deadlock-Problematik).

Abb. 1–11 Symmetrisches Multiprozessor-(SMP-)System

1.5.7 Verteilte Betriebssysteme

Verteilte Betriebssysteme nutzen eine Menge von über ein Netzwerk verbundenen Rechnern zur Lösung größerer Aufgaben oder einfach, um eine gemeinsame Rechenplattform in größerem Rahmen zu realisieren. Idealerweise wird das Betriebssystem so realisiert, dass Ortstransparenz herrscht. Dies bedeutet, dass der Benutzer nur ein einziges System sieht (Single System Image, SSI), egal an welchem der teilnehmenden Rechner er sich momentan angemeldet hat. Solche Betriebssysteme realisieren die rechnerübergreifende Kommunikation systemintern und unterstützen Mechanismen zum Lastausgleich zwischen den einzelnen Rechnern wie auch Ausfallredundanz. Die verwandten Clustersysteme hingegen unterstützen die Ortstransparenz nur partiell oder gar nicht, womit sie partiell Fähigkeiten verteilter Betriebssysteme besitzen.

1.5.8 Beispiele von Systemarchitekturen

Unix System V

Der innere Aufbau des Unix-Betriebssystems (System V Release 3 hier als Beispiel betrachtet) spiegelt die zwei zentralen Unix-Konzepte Dateien (files) und Prozesse (processes) über entsprechende Subsysteme wider. Diese sind in Abbildung 1–12 als klar abgegrenzte logische Blöcke zu sehen.

Abb. 1–12 Interne Struktur des Unix (Kern des System V Release 3)

In der Realität sind die Abgrenzungen aber nicht so eindeutig, da einige Module auf interne Funktionen anderer Module einwirken (monolithische Struktur). Die Architektur teilt sich in die drei Schichten Benutzerebene (user level), Kernebene (kernel level) und Hardwareebene (hardware level) auf. Zuoberst stehen die Benutzerprogramme, die entweder direkt (über Trap-Interrupt von Assemblersprache) oder mithilfe von API-Funktionen aus einer Programmbibliothek (Hochsprache) die Systemdienste nutzen. Für die Interrupt-Verarbeitung liegen Kernroutinen vor, die bei einer Programmunterbrechung aufgerufen werden. Erwähnenswert ist, dass viele Unix-Kommandos gleichartig wie Benutzerprogramme implementiert sind, indem sie als ausführbare Dateien vorliegen und die Systemprogrammierschnittstelle zur Kommunikation mit dem Kern benutzen (Beispiel: Unix Kommandointerpreter, shells). Dadurch kann der Kern kompakt und überschaubar gestaltet werden. Die Steuerung von Peripheriegeräten erfolgt durch die Treiber, die entweder zeichenorientiert arbeiten (character device driver) oder ganze Datenblöcke manövrieren (block device driver). Letztere Gruppe von Treibern kann mithilfe eines Puffers Daten sowohl beim Lesen als auch beim Schreiben zwischenpuffern. Unix ist in C programmiert, ergänzt mit wenigen hardwarenahen Teilen in der Assemblersprache der unterliegenden Hardwareplattform.

Windows 10

Das Betriebssystem Windows 10 besitzt eine Architekturmischform, die sowohl Elemente der Mikrokernidee als auch der geschichteten Strukturierung realisiert (siehe Abb. 1–13). Jedoch unterscheidet es sich nicht groß von vielen Unix-Systemen, indem wesentliche Teile des Systemcodes einschließlich der Treiber in der gleichen Ausführungsumgebung ablaufen und damit unter sich keinen Schutz gegen Fehlzugriffe genießen. Hingegen sind viele Dienste und Hilfsfunktionen in separate Prozesse ausgelagert und daher genauso gegeneinander geschützt wie Benutzerprozesse unter sich. Alle Systemteile im Kernmodus bzw. die in Systemdienstprozesse ausgelagerten Teile sind gegen böswillige Benutzerprozesse abgeschottet. Damit unterscheidet sich Windows 10 deutlich von früheren Windows-Produkten der Reihe 3.x/95/98/ME, die keinen vollständigen Schutz des Systemcodes vor Manipulationen durch fehlerhafte Applikationen boten. Dies ist jedoch eine fundamentale Anforderung für ein stabiles und robustes Betriebssystem. Windows 10 ist in C, zu kleineren Teilen in C++ programmiert. Wenige Softwareteile, die direkt die Hardware ansprechen, sind auch in Assemblersprache codiert.

Nachdem Windows über viele Jahre nur als Gesamtsystem installier- und ladbar war, wurde mit Windows Server 2008 eine Version ohne grafische Oberfläche geschaffen (windows core), da ein GUI für den Serverbetrieb nicht unbedingt notwendig ist. Mit Windows 7 wurde zudem ein MinWin definiert, das nur aus dem eigentlichen Windows Kernel, den Netzwerkprotokollen, dem Dateisystem und einem minimalen Satz von Diensten (core services) besteht und in 40 MB Hauptspeicher Platz findet. MinWin kann unabhängig vom restlichen Windows-Code geladen und getestet werden, womit die höheren Schichten des Systems besser abgekoppelt werden.

Abb. 1–13 Interne Struktur von Windows 10

Entsprechend wurde auch systemintern eine MinWin API definiert, die nun von den höheren Schichten genutzt wird. Windows 10 skaliert gut über die Plattformgröße, indem für Kleinsysteme aus der Embedded-Welt bis zu Servermaschinen passende Systemvarianten verfügbar sind.

Google Chrome OS

Google realisiert mit diesem Betriebssystem eine neue Idee, wie Anwender mit Programmen arbeiten, nämlich webbasiert. Man ist auch versucht zu sagen, dass das Chrome OS die Versprechungen einlöst, die für das Web 2.0 gemacht wurden. Die Architekturidee besteht im Wesentlichen darin, aus serverbasierten Applikationen (Cloud-Services, Cloud Computing) und kostengünstigen Netbooks eine Systemlösung zu realisieren, die übliche Anwendungen, wie Office-Programme, Kalender- und Informationsdienste, dem Anwender als Webapplikationen zur Verfügung stellt, ohne dass er diese Applikationen auf seinem Computer installieren muss. Der Zugriff auf die Cloud-Services erfolgt via Chrome Webbrowser. Traditionelle GUI-Applikationen lassen sich nicht installieren, da als Benutzeroberfläche der Webbrowser dient, der seinerseits auf einem Linux-Kernel aufsetzt. Damit der Benutzer zwischendurch ohne Internetanbindung arbeiten kann, ermöglicht ihm das ebenfalls von Google stammende Browser-Plug-in Gears, seine Daten lokal zu speichern. Um einen möglichst schnellen Arbeitsbeginn zu erreichen, setzt das Chrome OS auf einer umfangreichen Firmware auf, die für eine blitzartige Initialisierung der Hardware sorgt. So gesehen liegt eine Drei-Schichten-Architektur vor: zuunterst die Firmware, dann der Linux-Kernel und zuoberst der Chrome Webbrowser.

1.5.9 Zukünftige Systemarchitekturen aus Sicht der Forschung

Betriebssysteme bieten viele Ansatzpunkte für Verbesserungen. Aus Benutzersicht ist die Bedienoberfläche das prägende Element eines Betriebssystems. Aus Systemsicht sind die Architektur bzw. Entwurfsprinzipien, die auf die Lösung bekannter Probleme abzielen, von größerem Interesse. Die Gestaltung von Benutzeroberflächen ist ein umfangreiches Wissensgebiet der Informatik, wofür auf entsprechende Spezialliteratur verwiesen sei. Zur Systemarchitektur und ihren Aspekten wird hingegen nachfolgend eine Auswahl an Forschungsarbeiten vorgestellt, die illustrieren, wohin die Entwicklung von Betriebssystemen jenseits der Benutzeroberfläche in Zukunft gehen könnte. Dazu stellen wir eine Reihe von Fragen, auf die mögliche Antworten gefunden wurden.

Auf welcher Schicht sollen Systemfunktionen implementiert werden? Schichten (layers) sind ein beliebtes Strukturierungsmittel für Software, wobei die Idee der geschichteten Systeme nahelegt, eine bestimmte Funktion in schichtenspezifische Teilfunktionen aufzuteilen. Die End-to-End Arguments von J. H. Saltzer et al. (1984) sagen dagegen aus, dass Funktionen auf tiefer Abstraktionsstufe oft zu teuer sind für ihren Nutzen sowie dass es Funktionen gibt, die nur mit Wissen bzw. mithilfe der Applikation an den Kommunikationsendpunkten gelöst werden können. Diese Beobachtung gilt für Kommunikationssysteme generell, stellt aber auch die Grundlage des Internets dar. Ähnliche Aussagen zu Betriebssystemen wurden von B. W. Lampson bereits 1974 gemacht, wobei er betont, dass Funktionen nie fix auf tiefer Stufe gelöst sein sollten, sondern sich stets durch die Applikation mit einer spezialisierteren Version ersetzen lassen. Teilfunktionen auf tiefer Stufe können hingegen zu hoher Effizienz beitragen.

Wie lassen sich Betriebssysteme flexibel erweitern? Traditionelle Betriebssysteme begrenzen die Performanz, Flexibilität und Funktionalität von Applikationen durch ihre fixen Schnittstellen und Implementierungen von Abstraktionen, wie z.B. Interprozesskommunikation und virtueller Speicher. Die Idee der Extensible Systems ist, dass ein minimaler vertrauenswürdiger Kern die Hardwareressourcen via elementare Schnittstellen an nicht vertrauenswürdige Betriebssysteme exportiert. Letztere sind als Programmbibliotheken implementiert (Library Operating System, LOS). Die LOS realisieren Systemobjekte und Systemstrategien und laufen im Benutzermodus im abgeschotteten Speicher des sie benutzenden Prozesses. Damit lassen sich Dienste einfacher und applikationsangepasster realisieren, womit sie effizienter erbracht werden. Applikationen können eigene Abstraktionen definieren oder die vom Kern angebotenen minimalen Abstraktionen erweitern oder spezialisieren. Eine Proof-of-Concept-Implementierung, genannt ExoKernel, wurde von D. R. Engler und M. F. Kaashoek im Jahr 1995 realisiert und mit dem kommerziellen Unix-System Ultrix verglichen, womit die Effizienzsteigerung eindrücklich belegt wurde. Die Idee der Library Operating Systems wurde in einer Nachfolgearbeit unter einem anderen Blickwinkel erforscht, nämlich der Isolierung, wie dies sonst nur mit Virtual Machine Monitors (VMM, siehe Abschnitt 12.2.7) erreichbar ist. Das Resultat ist das Betriebssystem Drawbridge (D. E. Porter et al., 2011) das die Architektur von Windows 7 so umbaut, dass ein Security Monitor Systemfunktionalität, wie Dateisysteme, Netzwerk-Protokollstapel und Peripheriegerätetreiber, bereitstellt, auf die dann die eigentlichen Library Operating Systems aufsetzen. Die LOS realisieren den Großteil der restlichen Systemdienste, sodass im Security Monitor nur ein kleiner Teil des gesamten Betriebssystems, entsprechend 2% des Gesamtcode-Umfangs, gemeinsam ist. Im Gegensatz zu VMM-Lösungen werden erheblich weniger Ressourcen benötigt, wenn das gleiche Betriebssystem stark isoliert mehrere Applikationsumgebungen realisieren soll. Drawbridge ermöglicht ferner eine sehr einfache Applikationsmigration während des Betriebs, vergleichbar mit VMM-Lösungen.

Wie kann ein Betriebssystem sicher erweitert werden? Es gibt Applikationen, für die vorgefertigte Betriebssystemdienste bzw. Betriebssystemschnittstellen schlecht passen. Mit dem Experimental-Betriebssystem SPIN (B. N. Bershad et al., 1995) wurde die Idee realisiert, mithilfe einer Infrastruktur zur Systemerweiterung, eines Grundsatzes an erweiterbaren Systemfunktionen und der Verwendung einer typsicheren Hochsprache den Applikationen die Spezialisierung von Systemdiensten zu ermöglichen. Die Erweiterungen werden dabei beim Laden oder während des Betriebs dynamisch in vom restlichen Kern logisch getrennte Domänen (logical protection domains) eingebunden. SPIN zeigt, dass eine effiziente Implementierung eines erweiterbaren Betriebssystems bei vollem Schutz möglich ist.

Wie wird die Ausführung unsicheren Codes verhindert? Moderne Sprach-Laufzeitsysteme zeigen, dass mit typsicheren Hochsprachen (z.B. Java, C#) und komplementären Prüfmechanismen bei der Compilierung, beim Laden des Zwischencodes (z.B. Java-Bytecode, .NET MSIL) und während der Programmausführung nicht erlaubte Zugriffe auf Code und Daten verhindert werden. Leider sind damit Applikationen, die in weniger sicheren Sprachen programmiert wurden, immer noch ein Problem, da sie evolutionär entstanden sind und eine Portierung in eine sichere Sprache unattraktiv ist. Bereits 1996 wurde von G. C. Necula und P. Lee eine Lösung präsentiert, die basierend auf einer Safety Policy den Binärcode (Proof Carrying Code, PCC) vor der Ausführung prüft, ob er sicher ist. Damit diese Prüfung schnell abläuft, wird dem Binärcode ein kryptografisches Zertifikat mitgeliefert, das eine Sicherheitsüberprüfung ohne detaillierte Codeanalyse erlaubt. G. Morrisett et al. definierten 1999 einen typsicheren Instruktionssatz (Typed Assembly Language, TAL), der die Erzeugung typsicherer Binärprogramme aus Hochsprachen als PCC erlaubt.

Kann typsicherer Code die Isolation durch Hardwaremechanismen bei voller Sicherheit ersparen? Traditionelle Betriebssysteme isolieren Prozesse mittels der hardwareunterstützten Mechanismen der MMU (virtueller Speicher) und der CPU (Unterscheidung Benutzer-/Kernmodus). Nachteilig ist, dass jeder Prozesswechsel dadurch zusätzliche Zeit benötigt. 2006 wurde von M. Aiken et al. das Forschungsbetriebssystem Singularity vorgestellt, das auf typsicheren Sprachen beruht und ohne Hardwaremechanismen volle Sicherheit gewährleistet, womit es im Vergleich zu herkömmlichen Systemen effizienter abläuft. Programme laufen als Software Isolated Processes (SIP) ab und stellen geschlossene Objekträume (Closed Object Spaces) dar, da Prozesse indirekt via Exchange Heap kommunizieren, wobei der Sender die Referenz auf ein abgelegtes Objekt zwangsweise verliert, bevor dem Empfänger eine Zugriffsreferenz übergeben wird. Das Betriebssystem selbst ist als minimaler vertrauenswürdiger Kern und eine Menge von SIP für höherwertige Systemfunktionen realisiert. Da der Prozesswechsel und die Interprozesskommunikation sehr effizient sind, lassen sich auch Treiber als SIP realisieren, ohne dass die Performanz leidet.

Wie skaliert man Betriebssysteme für Manycore- und Cloud-Systeme? Verbreitete Betriebssysteme wurden für Plattformen entworfen, die nur über einen oder wenige Rechenkerne verfügen. Mit dem Factored Operating System (fos) zeigen D. Wentzlaff et al. (2010), dass sich ein verteiltes Betriebssystem hoch skalierbar realisieren lässt, wenn das Gesamtsystem in viele Komponenten aufgeschlüsselt wird, die je für sich Dienste anbieten, die selbst wiederum als eine Dienstmenge (als fleet bezeichnet) auf die teilnehmenden Rechner verteilt sind. Je nach aktueller Nachfrage werden diese Dienstmengen ausgeweitet oder geschrumpft. Im Gegensatz zu bekannten Infrastructure-as-a-Service-(IaaS-)Lösungen werden Ressourcen in einer einheitlichen, einfach skalierbaren Art und Weise angeboten.

Wie fehlerfrei sind verbreitete Betriebssysteme programmiert? Betriebssysteme sind komplexe Softwareprodukte und entsprechend anfällig für Entwurfs- und Programmmierfehler. M. M. Swift et al. haben 2003 die Ursachen für Systemabstürze des Microsoft Windows XP untersucht und dabei festgestellt, dass diese zu 85% durch Treiber verursacht wurden. Treiber stellen jedoch Plug-in-Komponenten eines Betriebssystems dar, die großenteils durch Drittparteien programmiert werden. Als eine Konsequenz hat Microsoft eine neue Treiberschnittstelle (WDM, siehe Abschnitt 7.3.7) zur Komplexitätsreduktion der Treiberentwicklung eingeführt. 2011 wurden von N. Palix et al. im Linux-Kernel 2.6.33 durch systematische Analysen 736 Fehler identifiziert. Ergänzend dazu steht ihre Beobachtung, dass über 10 Jahre hinweg der Codeumfang des Linux Kernel sich mehr als verdoppelt hat, jedoch die Fehleranzahl in etwa gleich geblieben ist, was für eine deutliche Steigerung der Softwarequalität spricht.

2 Programmausführung und Hardware

Lernziele

Sie erklären die Funktionsweise des Von-Neumann-Rechners.Sie beschreiben, wie eine CPU prinzipiell den Programmcode (Maschinencode) ausführt.Sie erläutern den Zweck der Prozessorregister und die Funktionsweise der Steuerregister PC, SP und PSW.Sie unterscheiden zwei Adressraumtypen.Sie interpretieren Speicherinhalte unter Berücksichtigung der geltenden Bytereihenfolge und Ausrichtungsregeln.Sie illustrieren den Zusammenhang zwischen C- und Assembler-Quellcode sowie Maschinencode anhand eines einfachen Beispiels.Sie klassifizieren Instruktionssätze anhand der Operandenanzahl.Sie ordnen C-Programmelemente, wie Code und Daten, den Speicherorten global, Stapel und Heap korrekt zu.Sie unterscheiden drei mögliche Adressraumlayouts für C-Programme.Sie stellen Vor- und Nachteile der Benutzung von Unterbrechungen (Interrupts) einander gegenüber.Sie unterscheiden den Benutzer- und Kernmodus eines Prozessors und erläutern die Bedeutung für das Betriebssystem.Sie unterscheiden drei Unterprogrammaufrufarten und drei Varianten der Funktionsparameterübergabe.Sie interpretieren die in einem Debugger hexadezimal visualisierten Inhalte eines Aktivierungsrahmens nach Verwendungszweck.

Die Ausführung von Programmen bildet eine gemeinsame Aufgabe der Prozessorhardware und des Betriebssystems. Da letztlich auch das Betriebssystem aus Sicht der Hardware nur ein Programm darstellt, ist die Ausführung von Programmen auf der blanken Hardware die Basis aller Prozesse. Die entsprechenden computertechnischen Grundlagen stehen deshalb am Anfang. Beginnend beim Von-Neumann-Rechnermodell lernen wir die elementaren Prozessorelemente kennen, die für die Programmausführung eine Rolle spielen. Dazu gehört u.a der Benutzer- und Kernmodus der CPU, da er die Basis des Schutzsystems darstellt. Ein paar Grundlagen der Adressraumnutzung und die Unterprogrammmechanismen erlauben die Interpretation von Stapelinhalten nach Verwendungszweck.

2.1 Rechner- und Prozessorgrundlagen

Eine Sequenz von Maschinenbefehlen (= Prozessorinstruktionen) wird zusammen mit ihrer Datenhaltung als Programm bezeichnet, entsprechend die Ausführung durch den Prozessor als Programmausführung. Nachfolgend betrachten wir elementare Eigenschaften eines Prozessors. Dies versetzt uns in die Lage, die Programmausführung unter einem Betriebssystem besser zu verstehen. Zudem ermöglicht es uns, unter einem Programm-Debugger die direkte Ausführung von Hochsprachprogrammen auf einem Rechner zu analysieren.

2.1.1 Grundmodell eines Rechners

Die meisten heute gebauten Computersysteme beruhen auf der Aufbaustruktur des Von-Neumann-Rechners, die John von Neumann 1946 aufgestellt hat. Seltener kommt die alternative Struktur des Harvard-Rechners zum Zug, benannt nach der Struktur des Mark-I-Rechners an der Harvard University (1939-44). Der Von-Neumann-Rechner besteht aus vier Funktionseinheiten (siehe Abb. 2–1):

Leitwerk (Control Unit, CU): Programme werden maschinenintern als Zahlen, auch Maschinenbefehle genannt, gespeichert. Die Maschinenbefehle legen die vom Prozessor auszuführenden Operationen fest. Das Leitwerk holt die Maschinenbefehle nacheinander aus dem Speicher, interpretiert sie und setzt sie in die zugehörigen Steueralgorithmen um. Das Leitwerk übernimmt damit als Befehlsprozessor die Steuerung der Instruktionsausführung. Unter Steueralgorithmen verstehen wir mögliche Prozessoroperationen, wie z.B. eine Addition, logische Oder-Verknüpfung oder Datenkopieren.Analogie »Fabrik«: Das Leitwerk fungiert als Vorarbeiter, der aktiv Aufträge in Form von Instruktionen entgegennimmt, diese interpretiert und dem Rechenwerk (= Arbeiter) die entsprechenden Arbeitsanweisungen erteilt. Dies entspricht einer Arbeitsvorbereitung.

Rechenwerk (Processing Unit, PU): Eingabedaten können neben den Maschinenbefehlen Bestandteile eines Programms sein (sie liegen dann im Speicher vor) oder werden während des Programmablaufs über die Funktionseinheit Ein-/Ausgabe von der Peripherie hereingeholt. Das Rechenwerk holt die Daten aus dem Speicher bzw. von der Eingabe, transformiert diese Daten mittels unterschiedlicher Steueralgorithmen und legt sie im Speicher ab bzw. übergibt sie der Ausgabe. Als eigentlicher Datenprozessor realisiert es die logischen und arithmetischen Operationen.Analogie »Fabrik«: Das Rechenwerk entspricht dem Arbeiter, der gemäß Arbeitsanweisungen die Rohmaterialien (= Operanden) in ein Endprodukt (= Resultat) verwandelt. Manchmal geht es auch nur darum, Eigenschaften von Materialien zu ermitteln oder Materialien zu transportieren. Das Rechenwerk übernimmt somit die Arbeitsausführung.

Speicher (memory): Er enthält die Maschinenbefehle und die zu verarbeitenden Daten. Eine Folge von logisch zusammengehörenden Maschinenbefehlen bezeichnen wir als Programm. Sowohl Befehle als auch Daten befinden sich in einem gemeinsamen Adressraum. Der Speicher dient somit der kombinierten Ablage von Programmen und Daten.Analogie »Fabrik«: Der Speicher entspricht einem Lager, in dem Rohmaterial, Zwischen- und Endprodukte abgelegt sind. Damit die Analogie passt, müssen auch Aufträge im Lager gespeichert werden.

Ein-/Ausgabe (Input/Output, I/O): Sie verbindet die Peripheriegeräte (z.B. Tastatur, Monitor, Drucker) mit dem Rechenwerk, stellt also eine oder mehrere Schnittstellen zur Umwelt dar. Mittels passender Maschinenbefehle werden über die Ein-/Ausgabe Daten von der Peripherie entgegengenommen oder dieser übergeben. Ursprünglich wurden das Eingabewerk und das Ausgabewerk als zwei getrennte Funktionsblöcke betrachtet. Heute werden sie zur Ein-/Ausgabe zusammengefasst.Analogie »Fabrik«: Die Ein-/Ausgabe entspricht der Spedition. Rohmaterialien werden eingeliefert und Zwischen- sowie Endprodukte ausgeliefert. Etwas ungewohnt werden auch Aufträge über diese Einheit entgegengenommen.

Abb. 2–1 Funktionsblöcke des Von-Neumann-Rechners

Häufig werden Rechenwerk und Leitwerk zusammengefasst und als Prozessor (Central Processing Unit, CPU) bezeichnet. Die Register stellen benannte Speicherplätze innerhalb der CPU dar (siehe Abb. 2–1). Sie ergänzen den Baublock Speicher, sind aber vergleichsweise nur in verschwindend kleiner Anzahl vorhanden. Die Anbindung des Prozessors an den Speicher wird oft als Von-Neumann-Flaschenhals bezeichnet, da sowohl Daten als auch Maschinenbefehle durch das gemeinsame Transportsystem (Bus) transferiert werden. Die Schritte

Befehl aus Befehlsspeicher holen (= Abholen des Befehls),

Daten aus dem Datenspeicher verknüpfen (= Befehl interpretieren, ausführen)

müssen beim Von-Neumann-Rechner zwingend hintereinander abgearbeitet werden, da sich Befehle und Daten nur hintereinander über den Bus transportieren lassen. Die Harvard-Architektur beseitigt diesen Nachteil, indem sie den parallelen Transport über den Bus durch separate Zugriffspfade ermöglicht. Realisiert wird dies, indem der Funktionsblock Speicher in zwei getrennte Blöcke Programmspeicher (program memory) und Datenspeicher (data memory) aufgeteilt wird. Dies erzeugt jedoch Hardware-Mehraufwand, da für den Datentransport doppelt so viele Leitungen notwendig sind. Zudem besteht das Problem, dass aus Quellcode erzeugte Programme vom Compiler im Datenspeicher abgelegt werden, aus dem sie erst in den Programmspeicher kopiert werden müssen, bevor sie ablaufen können. Infolge der Nachteile wird die Harvard-Architektur eher wenig eingesetzt, z.B. in angepasster Form für die ARM Cortex-Prozessoren oder für Signalverarbeitungsprozessoren.

Überlegt man sich, welche Betriebssystemteile zu welchen Baublöcken des Von-Neumann-Rechners gehören, so stellt man fest, dass etliche Teile mehreren Baublöcken zugeordnet sind. Diejenigen Systemkomponenten, die sich spezifisch zuordnen lassen, sind in Abbildung 2–2 eingetragen.

Abb. 2–2 Betriebssystem und Von-Neumann-Rechner

2.1.2 Befehlsverarbeitung in der CPU

Programme werden durch eine Rechnerhardware ausgeführt, in der ein Prozessor (CPU) die zentrale Einheit darstellt. Die CPU dient als Befehlsprozessor, der Schritt für Schritt die Befehle aus dem Speicher holt, interpretiert und die zugehörigen Steueralgorithmen aktiviert. Die CPU stellt den Kern jedes Rechners dar und realisiert die elektronische Datenverarbeitung.

Ein Programm liegt im Speicher in seiner einfachsten Form als Folge aufeinander folgender Maschinenbefehle vor. Die adressmäßige Anordnung im Speicher ist dabei aufsteigend, d.h., der erste Befehl hat die zahlenmäßig tiefste Adresse. Die Startadresse eines Programms ist die Adresse der ersten Programminstruktion. Die Maschinenbefehle sind in Form von Binärzahlen abgelegt, die vom Prozessor bei der Ausführung interpretiert werden. Betrachtet man mittels passender Programme die Speicherinhalte eines Rechners, so kann man die Programminstruktionen nicht von den Daten unterscheiden. Nur das Wissen, was wo platziert ist, entscheidet bei der Verarbeitung, ob die Speicherinhalte als Maschinenbefehle oder Datenwerte interpretiert werden. Jede Prozessorfamilie benutzt eine Reihe vom Hersteller festgelegter Regeln, die Binärzahlwerte den Prozessoroperationen zuordnen (sogenannte Instruktionssatzarchitektur, Instruction Set Architecture, ISA).

Nach einem Rücksetzen (reset) des Prozessors erfolgt zuerst die Prozessorinitialisierung, bei der unter anderem interne Register gelöscht und die Adresse der ersten auszuführenden Instruktion (Urstartadresse) geladen wird. Anschließend interpretiert die CPU in einer endlosen Schleife nacheinander jeden Befehl für sich (siehe Abb. 2–3).

Abb. 2–3 Arbeitsweise des Befehlsprozessors

Für jeden Befehl werden folgende Schritte durchlaufen:

Fetch:

- Instruktionscode aus dem Speicher holen

Execute:

- Instruktion decodieren (Arbeitsvorschrift aus Binärcode extrahieren)

- Evtl. Operand(en) aus Register oder Speicher holen (instruktionsabhängig)

- Evtl. Datentransformation ausführen (instruktionsabhängig, z.B. Addition)

- Evtl. Resultat in Register oder in Speicher ablegen (instruktionsabhängig)

- Evtl. Programmstatuswort verändern (instruktionsabhängig)

Welcher Befehl als nächster zu holen und damit auszuführen ist, wird durch den Adresswert im Programmzählerregister (PC) festgelegt. Anfänglich entspricht dieser Wert der Urstartadresse und wird nachfolgend bei jeder Befehlsausführung auf den nächsten Befehl im Hauptspeicher weitergeschoben (für Details siehe S. 48). Programme werden maschinenintern als Binärzahlen gespeichert, die vom Prozessor als Maschinenbefehle (Prozessorinstruktionen) interpretiert werden. Eine Reihe von Maschinenbefehlen bezeichnet man als Maschinencode. Für die Programmierung benutzt man nach festen Regeln zugeordnete alphanumerische Symbole (sog. Mnemonics). Diese sind besser lesbar als die reinen Binärzahlen der Maschinenbefehle. Programme, die diese Mnemonics enthalten, werden als Assemblerquellprogramme bezeichnet. Ein bestimmter Maschinenbefehl, mittels Mnemonics dargestellt, wird Assemblerbefehl genannt (siehe Abb. 2–4 für ein Beispiel). In der Praxis werden manchmal die Begriffe Maschinenbefehl und Assemblerbefehl synonym gebraucht. Dies ist jedoch nicht sonderlich präzis. Assemblerquellprogramme können mittels eines passenden Dienstprogramms (= Assembler) in Maschinencode übersetzt werden. Umgekehrt enthalten die meisten Programm-Debugger die Fähigkeit, im Speicher vorliegenden Maschinencode in Form der Assemblerbefehle lesbar darzustellen. Zu bedenken ist dabei, dass auf diese Art und Weise auch beliebige Datenwerte im Speicher scheinbar als Assemblerbefehle dargestellt werden.

Abb. 2–4 Assemblerbefehlsbeispiel (x86-Prozessor, GNU-Assembler)

2.1.3 Prozessoraufbau

Jede CPU-Familie besitzt einen spezifischen Instruktionssatz und Registeraufbau, mit dem sie sich von anderen Prozessorfamilien unterscheidet. Deswegen muss ein Programm stets für die richtige CPU übersetzt sein, damit die Maschinenbefehle korrekt interpretiert werden. Innerhalb einer sogenannten CPU-Familie kann jedoch der gleiche Code benutzt werden. Ein Prozessor besteht grob gesehen aus den Teilen Leitwerk, Rechenwerk, Register, Adress- und Bussteuerung (siehe Abb. 2–5). Register lassen sich in zwei Gruppen aufteilen:

Allgemeine Register: Sie dienen als Zwischenspeicher für Operanden, Resultate und Zeiger (d.h. Code- und Datenadressen). Sie gehören zum Rechenwerk und werden in ihrer Gesamtheit als Registerblock bezeichnet.

Steuerregister: Ihre Aufgabe ist die Steuerung des Programmablaufs. Teilweise können sie auch Informationen des Rechenwerks enthalten (z.B. Programmstatuswort).

Die Baublöcke Adresssteuerung und Bussteuerung spielen eine wichtige Rolle bei der Bestimmung von Zugriffsadressen und der Ansteuerung des Prozessorbusses. Da es sich aber um Funktionen handelt, mit denen man bei der Softwareentwicklung nicht groß in Kontakt kommt, sei für Details auf die spezialisierte Literatur verwiesen. Das Unterbrechungssystem realisiert die Möglichkeit, in speziellen Situationen den normalen Programmfluss zu unterbrechen und eine Ausnahmebehandlung vorzunehmen (für Details siehe S. 52).

Abb. 2–5 Schematischer Prozessoraufbau

2.1.4 Allgemeine Prozessorregister (general purpose registers)

Bei den allgemeinen Registern unterscheiden sich Prozessoren in folgenden Eigenschaften:

Allgemeine Register enthalten typischerweise Operanden, Adressen von Operanden und Funktionen (Zeiger) sowie Zwischen- und Endresultate von Rechenoperationen. Für das Betriebssystem stellen Register einen Teil der Ausführungsumgebung eines Programms dar, d.h., sie sind Teil des Prozesskontexts (process context). Ein Prozesskontext beinhaltet alle diejenigen Informationen, die bei einer Programmunterbrechung gespeichert und bei der Weiterführung wieder zurückgeholt werden müssen. Nur dann kann ein Programm später korrekt weiter ausgeführt werden, da es potenziell ja wichtige Daten in Registern abgelegt hat. Ein Betriebssystem ist nicht in der Lage, festzustellen, welche Register für ein Programm relevante Werte enthalten und welche nicht. Aus diesem Grund müssen alle diese Registerinhalte bei einer Unterbrechung gesichert werden.

2.1.5 Steuerregister (control registers)

Abhängig vom Prozessortyp kann eine unterschiedliche Anzahl an Steuerregistern vorliegen. Drei wichtige Register sind jedoch in allen Prozessoren realisiert, nämlich der Programmzähler (PC), der Stapelzeiger (SP) und das Programmstatuswort (PSW). Ihre Funktionen innerhalb einer CPU werden weiter unten kurz erläutert und im Detail in den Abschnitten 2.3.2 - 2.3.4 beschrieben. Prozessorabhängig gibt es folgende Unterschiede:

Registerbreite des PC, SP, PSW

Maschinenbefehle zum Lesen/Schreiben der Register

Zulässige Registerinhalte

Funktionalität des PSW (d.h. Registeraufbau und Inhalt)

Auch die Steuerregister sind ein Teil des Prozesskontexts eines Programms und müssen daher vom Betriebssystem bei der Prozessumschaltung gesichert und bei der Weiterführung wiederhergestellt werden. In Abbildung 2–6 sind drei Beispiele für CPU-Registerauslegungen angegeben. Der IBM PowerPC-Prozessor verfügt über 32 allgemeine und eine von der Chipvariante abhängige, relativ hohe Anzahl an Steuerregistern. Auffallend ist, dass weder ein Stapelzeiger noch ein Programmzähler dazugehört. Bei dieser CPU ist der Programmzähler versteckt, da die Software im Grunde genommen nicht direkt darauf zugreifen muss. Als Stapelzeiger können die GPRs dienen, da der Stapel rein von der Software gesteuert wird.

Beim Intel x86-Prozessor stehen sieben allgemeine Register und drei Steuerregister zur Verfügung. Zusätzliche in Abbildung 2–6 nicht dargestellte Register dienen speziellen Zwecken in der CPU-Steuerung und der Speicherverwaltung. Zum Vergleich: die Intel 64-Bit-Prozessoren (x86-64) erhöhen die Anzahl der allgemeinen Register auf 15, wobei alle Register eine Größe von 64 Bit besitzen. Für die rückwärtskompatible Ausführung von 32-Bit-Programmen (x86) werden die höherwertigen 32 Bit und die zusätzlichen Register ignoriert.

Abb. 2–6 Beispiele von CPU-Registern

Der ARM Cortex-Prozessor schließlich ist aus Sicht der Software mit 16 Registern bestückt. Register R0 bis R12 dienen allgemeinen Zwecken, während R13 als Stapelzeiger (SP), R15 als Programmzähler (PC) und das Register CPSR als PSW dienen. Eine Spezialität stellt das R14 (Link Register, LR) dar, das bei der Unterprogrammausführung die Rücksprungadresse zwischenspeichert. Obwohl die Software stets nur 16 Register sieht, sind in der Hardware in Form von Registerbänken mehr vorhanden, da eine Untermenge der Register in mehreren Exemplaren existiert. Pro CPU-Modus (z.B. User, Supervisor, Abort, IRQ) kommt jeweils eine separate Registerbank zum Zug.

2.2 Grundlagen des Adressraums

Der Zugriff auf die Befehle eines Programms erfolgt ebenso wie auf alle im Speicher abgelegten Operanden mittels Speicheradressen. Die Organisation des Adressraums stellt damit eine wichtige Eigenschaft und allenfalls auch Limitierung einer bestimmten Rechnerplattform dar.

Der in Abschnitt 2.1 vorgestellte Von-Neumann-Rechner benutzt einen für Code und Daten gemeinsamen Speicher, der direkt adressiert wird. Direkt darum, weil jede Speicherstelle über eine »Hausnummer«, Adresse genannt, verfügt. Die Kapazität des Speichers ergibt sich als Produkt aller möglichen Adresswerte und der Speicherfähigkeit einer einzelnen Speicherstelle (z.B. 216 Speicherstellen zu 1 Byte ergeben total 64 KB). Die Speicherstelle stellt die kleinste adressierbare Speichergröße dar. Historisch gesehen gab es in real existierenden Rechnern verschiedenste Speicherfähigkeiten separat adressierbarer Speicherstellen. So waren Werte beispielsweise von 6, 7, 8, 9, 12, 16, 18 oder 24 Bit verbreitet. Moderne Universalrechner benutzen aber praktisch ausnahmslos eine Speicherfähigkeit von 8 Bit pro Speicherstelle (bei Mikrocontrollern sind z.T. auch Einzelbits adressierbar). Deswegen gehen wir nachfolgend in diesem Buch von 8 Bit (= 1 Byte) Speicherfähigkeit pro Speicherstelle aus, sofern nichts anderes vermerkt ist (siehe Abb. 2–7). Im Zusammenhang mit dem Betriebssystem ist der Begriff des Adressraums wichtig.

Bei einem Prozessor mit einem 24-Bit-Adressbus beträgt die Anzahl aller möglichen Adressen (= Adressraumgröße) 224. Die Menge aller möglichen Adressen (= Adressraum) besteht aus {0, 1, 2, ..., 224