Axel Rogat
Betriebssysteme und betriebssystemnahes Programmieren
 
1.1: Was ist ein Betriebssystem? Kapitel 1 1.3: Beispiele 
 
  1.2 Historische Entwicklung der Betriebssysteme  
 

Hier haben seit der Entwicklung der ersten Rechnersysteme besonders viele Veränderungen stattgefunden, mehr als in irgendeinem anderen Gebiet. Die Hardware wurde mit enormer Geschwindigkeit verbessert, wodurch sich völlig veränderte Einsatzmöglichkeiten ergeben haben. Dadurch wiederum wurden allmählich höhere Anforderungen an die Leistung des Gesamtsystems gestellt, die letztendlich die Entwicklung leistungsfähigerer Hardware erzwangen, etc.

1.2.1 Zu Beginn: nackte Rechner

Die Rechner ganz zu Beginn des Computer-Zeitalters waren sehr groß und extrem teuer. Sie wurden per Hand aus einer Unzahl von Röhren zusammengebaut und waren entsprechend anfällig. Alle paar Stunden mußte eine defekte Röhre durch spezielles Personal ausgetauscht werden. Diese Rechner waren natürlich völlig experimentell und konnten noch nicht kommerziell eingesetzt werden. Die Hauptaufgaben waren rein numerische komplexe Berechnungen.

1.2.2 Röhren --> Transistoren

In der Zeit, als Röhren allmählich durch Transistoren ersetzt wurden, entwickelten sich die Rechner auch zu "universellen Maschinen". Sie wurden also nicht speziell für eine Aufgabe gebaut oder konfiguriert, sondern erhielten ihre Aufgabe in Form eines Programms beschrieben. Die Software wurde also aus der Hardware herausgelöst.

Die Programme mußten zunächst natürlich in der Maschinensprache des jeweiligen Rechners geschrieben werden. Etwas später wurden dann die ersten höheren Programmiersprachen entwickelt, von denen sich FORTRAN am schnellsten und weitesten verbreitete, später im kommerziellen Bereich auch COBOL. Dadurch wurde die Programmentwicklung schneller und (etwas) sicherer.

Dadurch daß die Hardware zuverlässiger wurde (Transistoren) und das Programmieren einfacher, wurden die Rechner allmählich auch kommerziell einsetzbar, wenn auch zunächst nur in großen Betrieben und Universitäten.

Der Job des Computer-Bedieners teilte sich nun auf in zwei:

Die Eingabe erfolgte üblicherweise über Lochkarten, die Ausgabe auf Papier oder (zur weiteren Verwendung) wieder auf Lochkarten. Häufig benutzte (unveränderliche) Daten wurden auf Magnetband gespeichert, beispielsweise Assembler, Compiler, Linker, Bibliotheken. Kurzer Einschub: Format von Lochkarten
Eine Lochkarte stellt üblicherweise eine Zeile Programmtext (80 Zeichen) oder entsprechend viele binäre Daten dar. Sie ist wie folgt aufgebaut:

Die Karte ist in 12 Zeilen zu je 80 Spalten aufgeteilt. In jedes so entstehende Feld kann ein rechteckiges Loch gestanzt werden. In jeder Spalte wird ein Zeichen oder bei Binärdaten ein binäres Datum dargestellt.

In der zeichenorientierten Darstellung (Hollerith-Code) bilden die Daten in den Zeilen 0, 11 und 12 den Zonenteil, die in den Zeilen 1 bis 9 den numerischen Teil eines Zeichens. Es darf jeweils maximal 1 Loch in den Zonenteil und maximal zwei in den numerischen Teil gestanzt werden, so daß viel Platz verschwendet wird (48 definierte Zeichen, 212=4096). Es k"onnen Gro"sbuchstaben, Ziffern und einige Sonderzeichen dargestellt werden.

In der binären Darstellung wird die Karte in eine obere und eine untere Hälfte aufgeteilt. In jeder Spalte werden zwei 6-Bit-Binärzahlen dargestellt. Wenn diese Zahlen über einen Code wieder als Zeichen aufgefaßt werden (64 mögliche Zeichen), passen so also zwei Zeichen in eine Spalte.

Der übliche Ablauf bei der Enstehung eines Programms war folgender:

  1. Programmierer:
    • Entwicklung des Programms auf Papier
    • Austesten zunächst mit Bleistift und Radiergummi
    • Stanzen des Programms in Lochkarten
    • Abliefern eines Stapels Lochkarten beim Operator: Job" = Programm und Daten, entsprechende Anweisungen (Compiler-Aufruf, etc.)

  2. Operator:
    sobald der Rechner "frei" wird (vorheriger Job beendet):
    • Auswahl eines der vorliegenden Kartenstapel
    • Einlesen des Kartenstapels in den Rechner
    • ggf. Einlesen zusätzlich benötigter Ressourcen (FORTRAN-Compiler vom Band)
    • Starten des Programms
    • Ausgabe (Papier/Karten) sammeln und an den Programmierer schicken

  3. Programmierer:
    Debuggen:
So wurde einiges an Zeit verschwendet. Der Operator lief zwischen Kartenleser, -stanzer und Magnetbandmaschine hin und her. Die Kartenstapel mußten letzendlich durch die CPU eingelesen werden; in dieser Zeit konnte nicht gerechnet werden. Bei der Preislage der damaligen Rechner (und Operatoren) kostete jede verschwendete Minute Tausende.

Lösung 1: Batch-Systeme

batch = Schub, Stoß, also hier: Zusammenfassung mehrerer Befehle
Als erste Verbesserung wurden zusammenpassende Jobs gesammelt, beispielsweise alle, die den FORTRAN-Compiler benötigen, alle, die den COBOL-Compiler benötigen, etc.

Die Compiler werden jeweils von einer bestimmten Bandmaschine geladen. Der Operator muß das Band jeweils per Hand austauschen und den neu benötigten Compiler einlesen lassen. Durch die Job-Sammlung kann hier Zeit gespart werden - der Compiler muß nicht für jeden Job gewechselt werden.

Lösung 2: Automatic Job Sequencing und Monitore

Bei herkömmlichem Operator-Betrieb war der Kartenstapel, den der Benutzer dem Operator übergab, aufgeteilt in Karten mit Programmen (FORTRAN-Quellcode) und numerischen Daten. Was wann einzulesen war, und was damit zu geschehen hatte, entschied der Operator.

Um nun die Daten in den einzelnen Jobs bei Batch-Betrieb günstig zu organisieren, wurden spezielle neue Karten-Typen eingeführt.

Die neuen Karten dienten zur Beschreibung des Zwecks der jeweils nachfolgenden Karten (" Kontroll-Karten"). Als Kennzeichnung enthielten sie als erstes Zeichen (also in der ersten Spalte) meistens ein Dollar-Zeichen $ oder ein unbelegter Codewert (wie 7-9). Nur IBM benutzte zwei Spalten //. Danach folgte jeweils die Spezifikation der Karte - in der Art eines kurzen Befehlsnamens:

$JOB oben auf dem Stapel, Beginn des Jobs, ID, Benutzername, etc.
$END unter dem Stapel, Ende des Jobs, ggf. Wiederholung der ID
$FTN (oder $FORTRAN) -- Laden und Starten des FORTRAN-Compilers (vom Band), das Programm steht auf den nachfolgenden Karten bis zur nächsten Kontroll-Karte. Das übersetzte Programm landet auf einem speziellen System-Band.
$COBOL Laden und Starten des COBOL-Compilers
$ASM Laden und Starten des Assemblers
$LOAD Zurückspulen des Systembands, Laden der erzeugten Daten als Programm
$RUN Starten des geladenen Programms. Die Eingabe-Daten für das Programm stehen auf den nachfolgenden Karten bis zur nächsten Kontroll-Karte.
$LGO load-and-go, Kombination aus $LOAD und $RUN
$DATA Eventuell zusätzliche Markierung eines Bereichs als (Eingabe-)Daten
Ein Job bestand nun also aus einem einzigen Kartenstapel, der intern durch Kontroll-Karten strukturiert war. Die Zugehörigkeit der Daten zum Auftraggeber ist durch die ID des Jobs gegeben. Die CPU kann nun beliebig viele Jobs direkt hintereinander ausführen -- Kartenstapel können miteinander verschmolzen werden. Die CPU startet so lange automatisch neue Programme, wie sie entsprechende Karten erhält ("automatic job sequencing").

Die Interpretation der Kontroll-Befehle geschah im Großrechner. Ein entsprechendes kleines Programm mußte dafür immer ausführbereit im Speicher liegen. Dieses Programm wurde "Monitor" (oder "resident monitor") genannt. Es stellt damit also das erste Betriebssystem der Geschichte dar (genauer einen Betriebssystem-Kern).

Der Monitor wird direkt nach dem Start in den Speicher geladen und bleibt von da an unverändert dort. Er besteht im wesentlichen aus folgenden Teilen:

Control Card Interpreter:
wertet die Befehle auf den Kontroll-Karten aus
Loader:
lädt ein Benutzer- oder ein System-Programm (Compiler) in den Speicher und macht es dort lauffähig
Job Sequencer:
startet ein geladenes Programm (übergibt ihm die CPU-Kontrolle), übernimmt nach dessen Beendigung die Kontrolle und stößt die weitere Befehlsverarbeitung an
Device Drivers:
Geräte-Treiber für die permanent benötigte Peripherie (hauptsächlich die Bandmaschinen); eventuell werden die Treiber bei Bedarf auch nachgeladen.
Als sich diese System-Betriebsart mit Monitor mehr und mehr durchsetzte und die Monitoren mehr Aufgaben übernahmen, bauten die Hersteller eine spezielle Hardware-Unterstützung in die CPUs ein. Der Satz von Maschinenbefehlen wurde zweigeteilt (" dual-mode instructions") in:
privilegierte Befehle: nur vom Monitor verwendbar ("Supervisor Mode")
Benutzer-Befehle: auch von Anwenderprogrammen verwendbar ("User Mode")
Typische Monitorsysteme der damaligen Zeit waren FMS (FORTRAN Monitor System) und IBSYS (für die IBM 7094). Erst später, als die Systeme ausgebaut wurden und der Name Monitor ihnen nicht mehr ganz gerecht wurde, entwickelte sich allmählich der Begriff Betriebsystem (Operating System wie in IBM OS/360, s.u.).

Die Kontroll-Karten bzw. -Befehle wurden oft auch zu einer (sehr kleinen) Batch-Sprache ausgebaut. Beispielsweise bot JCL (Job Control Language) von IBM die Ausführung von Befehlen in Abhängigkeit von Bedingungen, etc. Die heutigen Skript-Sprachen sind Abkömmlinge dieser einfachen Batch-Sprachen.

Lösung 3: Offline-Betrieb

Eine wesentliche Effizienzsteigerung ergab sich später durch den sogenannten "Offline-Betrieb". Zusammenpassende Jobs (mit sich überschneidenden System-Ressourcen wie Compiler) werden zunächst auf einem gesonderten kleinen Rechner (im "Eingaberaum") von Karten auf Magnetband übertragen (" satellite processing"). Sie können danach direkt hintereinander auf dem eigentlichen Großrechner ausgeführt werden.

Voraussetzung für diese Aufteilung ist entsprechende Hardware, nämlich kleine Rechner, die (im Vergleich mit dem Großrechner) rechnenschwach sein dürfen, dafür aber auch wesentlich billiger sein müssen ("reader-to-tape"- und "tape-to-printer"-Maschinen). Ein typischer Fall war:

Die IBM 1401 wurde auch alleine genutzt für einfache Datenspeicherung in Banken und Versicherungen.

Im günstigsten Fall gibt es mehrere Vor- und Nachrechner, so daß der teure Großrechner möglichst lückenlos ausgelastet sein kann.

Lösung 4: Puffern und Interrupts

Eine zusätzliche Verbesserung des Durchsatzes kann im Zusammenhang mit Ein-/Ausgabegeräten bereits durch einfaches Puffern erreicht werden:

Die Software für diesen Pufferungs-Mechanismus wird im Monitor oder im entsprechenden Geräte-Treiber untergebracht. Es liegt also eine weitere Zwischenstufe an Virtualisierung vor -- es wird nicht unbedingt direkt die wirkliche Treiber-Routine angesprochen, sondern die zwischengeschaltete Pufferungs-Routine.

Es gibt eine wichtige Hardware-Voraussetzung für das Puffern, nämlich einen Interrupt-Mechanismus in der CPU:

Die CPU muß hardwaremäßig vom Peripherie-Gerät unterbrochen werden können. Sie nimmt dann das nächste Datum (oder Datenpaket) vom Eingabegerät an und schreibt es in den Puffer, bzw. liefert dem Ausgabegerät das nächste Datum (Datenpaket) aus dem Puffer. Wenn das geschehen ist, kehrt die CPU zu ihrer ursprünglichen Tätigkeit zurück.

Das Unterbrechen geschieht direkt durch ein Signal ("Interrupt") an einem CPU-Anschluß. Es gibt im allgemeinen mehrere Signale oder eine zusätzliche Möglichkeit, Signale durchzunumerieren. Die CPU schaut dann in einer Tabelle nach, an welcher Stelle im Hauptspeicher die zum empfangenen Signal gehörige Behandlungsroutine steht und führt sie aus.

Auf diese Weise können Schwankungen in der Lese-/Schreib- bzw. Berechnungsgeschwindigkeit ausgeglichen werden. Bei den meisten Programmen wird allerdings die Ein-/Ausgabe der langsamere Teil sein, so daß Puffern nur einen (vergleichsweise) geringen Effekt auf die CPU-Ausnutzung hat.

Insgesamt läßt sich über die Rechnersysteme dieser Zeit sagen, daß sie vergleichsweise gut ausgenutzt werden konnten bei rechenintensiven Aufgaben, also in Universitäten, Rechenzentren, etc. Im kommerziellen Betrieb, wie bei der Datenverwaltung in Banken und Versicherungen, überwiegt meist der I/O-Anteil an der Gesamtzeit, und das System wird nicht gut ausgenutzt.

Ein wichtiger Nachteil des Batchings muß erwähnt werden: die Benutzung des Rechners ist nicht mehr interaktiv:

Vor dem Batching:
der Rechner stand dem Programmierer während der ihm zugeteilten Zeit allein zur Verfügung. Es konnte zwar schwer während der Laufzeit eines Programms eingegriffen werden. Leichtere Fehler konnten aber schnell im Ausdruck erkannt und korrigiert werden, und es konnte schnell ein neuer Durchlauf gestartet werden, ggf. ohne ein komplettes neues Einlesen und Konfigurieren.

Bei Batch-Systemen:
Es ist keine direkte Überwachung des Programms mehr möglich. Nach dem Debuggen (per Hand) muß ein neuer Job kreiert und dem Operator übergeben werden. Die Zeit, bis der nächste Testlauf stattfinden kann, nennt man "Turnaround-Zeit" (in dieser Zeit kann sich der Programmierer vom Rechner abwenden). Sie ist bei Batch-Systemen vergleichsweise groß.

1.2.3 Platten und integrierte Schaltungen

In den 60er Jahren gab es viele Entwicklungen im Hardware-Bereich, die direkte Auswirkungen auf den Rechnerbetrieb hatten:

Plattenspeicher
lösten Magnetbänder als schnelle Massenspeicher ab. Platten müssen nicht mehr wie Bänder sequentiell gelesen werden. Man kann mit nur geringer Zeitverzögerung auf Daten an beliebigen Stellen des Mediums zugreifen. Dadurch wurden die rechner-internen Zwischenspeicher um Größenordnungen schneller.

Integrierte Schaltkreise
wurden entwickelt, bei denen auf einem Chip Funktionen aufgebaut waren, die zuvor aus vielen Transistoren einzeln aufgebaut werden mußten. Nicht nur konnten die Rechner dadurch räumlich kleiner werden -- es war vor allem eine wesentlich schnellere Entwicklung neuer Rechner möglich, da sie nun zu großen Teilen modular aus zuverlässigen Bausteinen zusammengesetzt werden konnten.
Es gab dadurch einige wichtige Erscheinungen bei den Großrechnern:

1. Spooling

Spooling wäre sicher schon eher möglich gewesen, wurde aber erst durch die Einführung der Plattenspeicher sinnvoll.

Bei dem Genannten handelt es sich um quantitative Fortschritte. Durch Spooling ergaben sich aber auch qualitativ völlig neue Möglichkeiten: 2. Rechner-Familien und Kompatibilität

IBM "erfand" mit dem System/360 die "Kompatibilität". Dabei handelte es sich nicht um einen Rechner, sondern um eine ganze Familie von Rechnern, die ein großes Leistungsspektrum abdeckte:

Alle Mitglieder der Familie hatten aber dieselbe Architektur und sogar denselben Befehlssatz. Programme liefen also (theoretisch) unverändert auf all diesen Rechnern. Die parallele Entwicklung solch vieler Rechner war erst durch die IC-Technik möglich geworden.
Vorteil:
Die Kunden konnten zu leistungsfähigeren Rechnern "aufsteigen", ohne Programme oder Daten irgendwie umstellen zu müssen. Das war die Grundlage für den überwältigenden Erfolg von System/360.

Nachteil:
Das Betriebssystem (OS/360) mußte mit jeder Hardware zurechtkommen:
  • Es nutzte oft nicht den jeweiligen Rechner optimal aus.
  • Es wurde wesentlich größer als die Vorgänger-Monitore (ein mehrere Meter hoher Kartenstapel).
  • Es wurde von einem sehr großen Team von Programmierern erstellt und gewartet.
Da man noch keine wirkliche Erfahrung mit Betriebssystemen hatte, gab es wenig echte Planung im voraus. Viele Fähigkeiten wurden erst spät während der Entwicklungszeit hinzugefügt.

OS/360 wurde gigantisch kompliziert und entsprechend fehleranfällig. Als Reaktion auf Beschwerden von Kunden wurden Fehler in der nächsten Version möglichst schnell "herausgehackt" - wobei mindestens genauso viele neue Fehler hineingehackt wurden. Bereits die erste Version wurde ausgeliefert, obwohl bereits Hunderte von Fehlern bekannt waren!

3. Multiprogramming

Die wichtigste Neuerung, die zuerst in OS/360 implementiert war, war aber das "Multiprogramming", später (nachdem der Begriff "Task" entwickelt worden war) auch "Multitasking".

Die Neuerungen in den Vorgänger-Systemen zielten alleine darauf, den Durchsatz bei der CPU zu maximieren. Durch die fehlende Interaktivität verlängerte sich dagegen die Programm-Entwicklung (lange Antwortzeit auf einen Job). In der Anfangszeit der IC-Technik wurde die Hardware aber sehr schnell wesentlich billiger - und irgendwann gewannen die Löhne der menschlichen Bediener an Bedeutung.

Die Idee des Job Pools (vom Spooling) wurde ausgeweitet, und der Pool von der Platte in den Hauptspeicher verlegt. Der Hauptspeicher, der nicht fest zum Betriebssystem gehörte, wurde in mehrere Teile aufgeteilt, und in jeden Teil wurde ein Job geladen.

Um die Aufteilung durchführen zu können, mußte Hardware zur Beschränkung des erlaubten Adreßbereichs vorhanden sein. Außerdem war eine automatische Adreß-Umsetzung sinnvoll, damit ein Programm in jedem beliebigen physischen Speicherbereich unverändert lauffähig war. Das System/360 gehörte zu den ersten Systemen, die diese Hardware-Voraussetzungen erfüllten.

Nachdem ein Job vom Monitor die CPU-Kontrolle übernommen hatte, behielt er sie so lange, bis er wegen I/O-Operationen gezwungen war, zu warten. Für diese Operationen mußte er ja ohnehin den Monitor aufrufen - und dieser hatte nun die Möglichkeit, zu entscheiden, lieber einem rechenintensiven Job den Vorrang zu geben.

Die Umschaltung zwischen den Jobs erfolgte also nicht kontinuierlich (per Zeitscheiben-Verfahren), sondern nur bei Wartezeiten des jeweils aktiven Prozesses. Diese einfachere Art von Multitasking nennt man auch " non-preemptive" (das Zeitscheiben-Verfahren heißt "preemptive").

Beispiel: Um die mögliche verbesserte CPU-Ausnutzung zu demonstrieren, betrachten wir als einfache Situation zwei Prozesse, die zwischendurch immer wieder auf Eingaben warten müssen.

In dieser Zeit sind sie "idle" (untätig). Unten sind die Arbeitszeiten mit durchgezogenen Linien, die Idle-Zeiten mit Punkten dargestellt.

In einem System ohne Multitasking sind die Prozesse nur Programme, die nacheinander ausgeführt werden. Die Gesamt-Laufzeit ist die Summe der Einzel-Laufzeiten der Prozesse, inklusive ihrer Idle-Zeiten.

Auch bei Multitasking ohne Zeitscheiben ergeben sich Verbesserungen. Wenn ein Prozeß warten muß, wird der andere vorgelassen.

Die Pfeile deuten den Wechsel der CPU-Kontrolle an.

4. Timesharing

Timesharing ist im wesentlichen Multiprogramming mit mehreren Benutzern, die gleichzeitig an mehreren Terminals arbeiten können.

Die meisten Jobs werden "fast immer" auf Benutzer-Eingaben warten müssen. In dieser Zeit können dann die Jobs laufen, die sich gerade in einer rechenintensiveren Phase befinden.

Vorteil:
Es ist wieder interaktive Programmentwicklung möglich. Programme können ediert werden, während die anderer Benutzer laufen. Das Programm kann direkt vom Benutzer Eingaben per Tastatur erhalten (nicht über einen vorproduzierten Kartenstapel). Dadurch ist ein einfacheres und schnelleres Austesten (und damit eine schnellere Programmentwicklung) möglich.

Nachteil:
Die Benutzeranzahl ist potentiell unbegrenzt. Bei sehr vielen Benutzern gleichzeitig erhält man leicht einen Effekt, der als " Thrashing" bezeichnet wird (thrash = sich vorwärtsquälen, ein nautischer Begriff). Das System ist dann fast nur noch mit dem Mechanismus des Umschaltens zwischen den Benutzern beschäftigt. Eventuell reicht ein einziger zusätzlicher Benutzer, um die Antwortzeiten zu vertausendfachen.
Timesharing wurde zuerst 1962 (als CTSS) auf einer IBM-7094 entwickelt, also auf einer Hardware ohne Möglichkeit zur Memory Protection. Es wurde also erst später wirklich einsetzbar.

Im Zusammenhang mit Timesharing wurden wichtige Konzepte entwickelt, die bis heute eher noch an Bedeutung gewonnen haben:

Shell:
Befehlsinterpreter wurden entwickelt, die eine direkte Schnittstelle vom Benutzer zum System darstellen. Sie leisten im Grunde dasselbe wie der Control Card Interpreter in den alten Monitoren, werden jetzt aber interaktiv an den Benutzerterminals eingesetzt.

Dateisystem:
Die Ein- und Ausgabedaten-Sammlungen waren früher in Form von mehreren Kartenstapeln bzw. des Control-Card-Aufbaus der Jobs strukturiert. Diese Struktur wurde auf Band bzw. Platte einfach übernommen. Der Bedarf für ein Dateisystem ergab sich wirklich erst jetzt:

Eine echte Verzeichnis- Hierarchie, also beliebig tief verschachtelte Unterverzeichnisse für jeden Benutzer, wurde erstaunlicherweise bei vielen Systemen nicht oder erst nachträglich viel später implementiert.

1.2.4 Mikroprozessoren

Die entsprechenden Betriebssysteme mußten sich jetzt nicht an Operatoren, sondern an reine Bediener ohne tiefe Rechner-Kenntnisse wenden. Dadurch wurden sie zwangsweise benutzerfreundlicher.

Durch das Wegfallen der Mehrbenutzer-Struktur wurde die Hardware zunächst wieder vereinfacht (und dadurch verbilligt) - beispielsweise wurden Memory Protection und privilegierte Befehle wieder fallengelassen. Entsprechend wurden auch die Betriebssysteme vereinfacht, wobei leider auch die Mehrprozeß-Elemente eliminiert wurden.

Mit zunehmender Integration und Verbilligung der Hardware - und durch den Ruf nach Benutzer-Komfort - fanden diese Elemente aber nach und nach wieder Eingang in Hardware und Betriebssysteme.

Wesentliche konzeptionelle Neuerungen haben in dieser Zeit dagegen nicht stattgefunden. Erst durch die Vernetzung der Personal Computer und die entstehenden Probleme im Hinblick auf Sicherheit und Zugriff auf entfernte Ressourcen gab es neue Entwicklungen (Netzwerk-Betriebssysteme, verteilte Betriebssysteme).

 
1.1: Was ist ein Betriebssystem? Startseite 1.3: Beispiele