Verteilte Betriebssysteme
Geschwurbel von Daniel Schwamm (02-03/1994)
Inhalt
Am Anfang der Informationstechnologie gab es nur den Von-Neumann-Rechner mit
seiner Single Instruction Single Data-Architektur (SISD): Instruktionen und Daten
werden hier aus demselben Speicher geladen, von einer Kontrolleinheit decodiert und von
nur einem Prozessor verarbeitet. Später wurde die Rechnerarchitektur
Multiple Instruction Multiple Data (MIMD) entwickelt, bei der
mehrere Prozessoren parallel arbeiten, die jeweils einen eigenen Strom an Daten und
Instruktionen besitzen. Je nachdem, ob die Prozessoren über einen
gemeinsamen Speicher kommunizieren bzw. jeder einen separaten unterhält,
spricht man von Multiprozessorsystemen (z.B. die Cray-Rechner) bzw.
Multicomputersystemen, zu denen im weiteren Sinne auch die herkömmlichen
Rechnernetze gehören.
Rechnernetze sind stark im Wachstum begriffen. Die Hardware (HW) der Computer,
Knoten und Übertragungsmedien verfügt über immer mehr
Leistungskapazität bei gleichzeitig fallenden Preisen. Die Vernetzung
der Welt ist daher ein Prozess, der sich in den kommenden Jahren sicher
noch explosionsartig weiter entwickeln wird.
Den Höhepunkt der bisherigen Rechnernetztechnik stellt die Entwicklung
sogenannter "verteilter Betriebssysteme" dar, die von ihrer Architektur her den
Multiprozessorsystemen gleichen. Eine genaue Definition des Begriffs "verteiltes
Betriebssystem" gibt es zwar nicht. Wir halten uns in dieser Arbeit jedoch an die
folgenden allgemein akzeptierten Charakteristiken:
Ein verteiltes Betriebssystem verfügt über mehrere,
voneinander unabhängige Knotenrechner, die jeweils mit mindestens einer CPU
ausgestattet sind. Zwischen den Knoten gibt es ein Verbindungsnetz, über welches die
Knoten miteinander kommunizieren können. Auf allen Knoten kommt derselbe
Betriebssystemkern zum Einsatz. Stürzt ein Betriebssystem auf einem Rechner
ab, kann der Anwender vom gleichen Rechner aus über einen anderen Rechner
in transparenter Weise weiterarbeiten - das Gesamtsystem verliert dabei nur
so viel an Leistung, wie der abgestürzte Rechner durch seine CPU
dazu gegeben hatte. Als MIMD-Multiprozessor-Architektur verfügen alle
Knoten über einen gemeinsamen Speicherbereich, was hilft, Techniken
zu entwickeln, die die Single Point of Failure-Problematik herkömmlicher
Rechnernetze ausschliessen und Verteilungstransparenz ermöglichen.
Verteilungstransparenz ist die höchste in einem Netzwerk erreichbare
Transparenzstufe. Sie sagt aus, dass ein Knotenprozess nicht explizit
angeben muss, wo sich ein anderer Prozess, mit dem er kommunizieren
muss, innerhalb des verteilten Betriebssystems befindet - dieses Wissen
wird quasi implizit vom verteilten Betriebssystem gestellt.
Verteilungstransparenz umfasst damit im Einzelnen folgende Dinge:
-
Zugriffstransparenz: Zugriff auf entfernte Daten geschieht in gleicher
Weise wie ein Zugriff auf lokale Daten.
-
Lokationstransparenz: Zugriff auf Daten möglich, ohne Wissen um deren
genauen Speicherort.
-
Replikationstransparenz: Zugriff auf Daten ohne Wissen um evtl.
vorliegende konsistente Mehrfachspeicherung der Daten möglich.
-
Fragmentierungstransparenz: Zugriff auf bestimmte Daten ohne Wissen um
evtl. vorliegende Fragmentierung der Daten möglich.
Die oben aufgeführten Transparenzeigenschaften unterscheiden sich deutlich
von denen, die ein herkömmliches Netzbetriebssystem anbietet. Bei einem
nicht-verteilten Betriebssystem kann zwar jeder Rechner im Netz ein anderes
Betriebssystem betreiben, es muss jedoch jedem Prozess explizit
mitgeteilt werden, wo er welche Ressourcen finden kann. Stürzt ein
nicht-verteiltes Betriebssystem auf einem Rechner ab, kann dieser Rechner
nicht mehr an der Kommunikation teilnehmen (im Gegensatz zu verteilten
Betriebssystemen, wo dies nur zu einer allgemeinen Leistungsminderung im
Netzwerk führt). Das Wichtigste aber ist, dass nicht-verteilte
Betriebssysteme über keine Verteilungstransparenz verfügen, auch
wenn sie diese Problematik durch graduell verschiedene Tools bzw. Dienste
zu umgehen suchen.
Sehen wir uns das nicht-verteilte Betriebssystem UNIX an. Es versucht,
über folgende Methoden eine Art Verteilungstransparenz zu erreichen:
-
Dateisystem: Das Dateisystem von UNIX erlaubt das Kopieren von Dateien
innerhalb des lokalen Rechnernetzes unter Angabe des Pfadnamens (keine
Ortstransparenz gegeben).
-
Network-Disk-Server: Ein Diskettenserver, der es mehreren (diskless)
Workstations ermöglicht, über ein entferntes Diskettenbetriebssystem
Daten auszutauschen. Auch hier muss der Pfadname vollständig
angegeben werden.
-
Network File System (NFS): Ein verteiltes Dienstprogramm (in UNIX
integriert), welches die Montage fremder Verzeichnisse in das eigene Verzeichnis
ermöglicht (um genau zu sein: Das Mount-Protokoll ist ein eigenständiges
Programm, denn NFS stellt eigentlich nur Lese- und Schreiboperationen zur Verfügung).
Nach der Montage kann fast von Lokationstransparenz und Zugriffstransparenz gesprochen
werden, aber eben nur fast: Die Pfadnamen des eigenen Directory-Baums
müssen weiterhin angegeben werden. Schwerwiegender ist aber, dass
vor dem Montieren keine Ortstransparenz vorliegt, da der Benutzer wissen
muss, wo sich das zu montierende Verzeichnis finden lässt.
Auch unterstützt NFS keine Transaktionen, da es mit zustandslosen
Servern (svw.) arbeitet. Unter Verwendung der UDP- bzw. TCP-Protokolle
gestattet es aber immerhin Remote Procedure Calls (RPC) von pointerlosen,
idempotenten Funktionen, die über Timeout- und Duplikationstechniken
sogar zuverlässig arbeiten können.
Interaktionsmuster geben an, wie sich Prozessoren koordinieren können,
um gemeinsam ein Problem bearbeiten zu können.
-
Pipelining: Bei diesem Interaktionsmuster empfängt ein Prozess
F einen einzelnen Eingabewert x, bearbeitete diesen und gibt das Ergebnis F(x)
aus oder macht etwas anderes damit. Zu beachten ist: Die Verbindung über
Pipelines ist unidirektional, d.h., will F das Ergebnis F(x) irgendwo anders
hin senden, so muss er dazu eine neue Pipeline aufbauen.
PROCESS F;
LOOP DO
RECEIVE(x);
...
SEND(F(x));
END
-
Peer-to-Peer-Interaktionsmuster: Hier kommunizieren zwei gleichwertige
Prozesse miteinander im Duplex-Betrieb. Normalerweise wird dies ausgenutzt, um
zuverlässige Verbindungen zwischen den Partnern aufzubauen, in dem z.B.
ein vollständiges (OSI-)Handshake-Verfahren durchgeführt wird.
PROCESS P1; PROCESS P2;
LOOP DO LOOP DO
SEND(request); ...
RECEIVE(confirm); RECEIVE(indication);
... SEND(response);
END END
-
Client/Server-Konzept: Bei diesem Interaktionsmuster kommunizieren
zwei nicht-gleichberechtigte Partner miteinander. Der Client ist stets der
Dienstleistungsempfänger und der Server stets der Dienstleistungsbetreiber. Es
gilt auch: Ein Server bedient viele Clients, ein Client benutzt nur wenige
Server. Diese Art der Kommunikation ist die in verteilten Betriebssystemen
übliche.
PROCESS Client (fordert aktiv) PROCESS Server (wartet passiv)
LOOP DO LOOP DO
SEND(request); RECEIVE(request);
... wartet hier in der Regel ...
RECEIVE(reply); SEND(reply);
END END
-
Heartbeat-Interaktionsmuster: Diese Art der Kommunikation wird häufig
in Parallelrechnern eingesetzt. Sie arbeitet zweiphasig: In der ersten Phase senden
z.B. die Prozessoren P1, P2 und P3 gleichzeitig Nachrichten an die Prozessoren
P4, P5 und P6, und in der zweiten Phase senden P4, P5 und P6 gleichzeitig
Nachrichten an P1, P2 und P3.
-
Probe/Echo-Konzept: Dieses Interaktionsmuster wird häufig eingesetzt,
wenn die Prozessoren hierarchisch (z.B. als Binärbaum) organisiert sind.
Dabei gibt ein Elternknoten (ein Basisprozess) eine sogenannte Probe-Nachricht
an seine Kinder weiter, die sie dann weiter an ihre Kinder leiten usw., bis sie
schliesslich das Baumende/den Zielprozess erreicht haben und in
verarbeitetet Form als Echo-Nachrichten den Baum wieder heraufsteigen.
PROCESS Knoten Pi
LOOP DO
RECEIVE(probe) FROM parent
SEND(probe) to child 1, ..., child n;
RECEIVE(echo) from child 1, ..., child n;
SEND(echo) to parent;
END
-
Token-Passing-Methode: Bei diesem Interaktionsmuster koordinieren sich
Prozessoren dadurch, dass stets nur der Nachrichten versenden darf, der im
Besitz des Tokens ist. Jeder kann das Token nur für kurze Zeit halten und
muss es dann weitergeben, sodass kein Prozessor über längere
Zeit das ganze Kommunikationsmedium blockieren kann (es geht also "fair" zu!).
Unter Umständen ist es auch möglich, dass sich mehrere Token im
System befinden, sodass durchaus auch parallel Nachrichten versendet werden
können.
PROCESS Pi
LOOP DO
RECEIVE(token);
...
SEND(token);
END
Oben haben wir gesehen, über welche Interaktionsmuster Prozesse
Nachrichten austauschen können. In verteilten Betriebssystemen ist davon
auszugehen, dass sich die Prozesse nicht auf den gleichen Rechnern befinden,
daher müssen die Prozesse so organisiert werden, dass sie über ein
LAN (u.U. auch über ein WAN) hinweg miteinander kommunizieren können.
Insbesondere muss auch dafür gesorgt werden, dass alle Rechner
im LAN gleichmässig ausgelastet werden. Man spricht hier von
Prozessorganisation oder besser: Serverorganisation, weil den verteilten
Betriebssystemen fast immer das Client/Server-Interaktionsmuster zugrunde liegt.
Verschiedenen Methoden der Server-Organisation wollen wir uns nun einmal
betrachten.
-
Work-Sharing-Server: Bei dieser Technik verfügt jeder Serverrechner
im LAN über eine bestimmte Anzahl von Ports, also Netzzugängen, denen
jeweils genau ein Serverprozess zugeordnet ist. Jeder Client braucht hier
also nur die Serveradresse zusammen mit der Portnummer ins LAN einzugeben und
schon bekommt er Zugriff auf einen bestimmten Dienstprozess des
Serverrechners. Dies hat allerdings den schweren Nachteil, dass jeder
Serverprozess nur genau einmal instanziiert werden kann, weil ihm ja nur
eine Portnummer zur Verfügung steht.
-
Load-Sharing-Server: Bei dieser Technik verfügt der Serverrechner
über einen Listener, der alle Port-Eingänge abhört. Kommt ein
Verbindungswunsch eines Clients an einem Port an, so erzeugt der Listener einen
Link-Handler für den gewünschten Dienst (der daraufhin erst
instanziiert wird) und weist ihm die Portnummer zu.
PROCESS Listener PROCESS Handler
LOOP DO DO
RECEIVE(request); RECEIVE(port-id);
CREATE(handler); CALL(service-process);
... SEND(reply);
END END
-
Server-Replikation: In grösseren LANs ist es üblich,
dass mehrere Server-Rechner eingesetzt werden, die jeweils die gleichen
Dienste anbieten. Damit die Clients die Dienste von dem am wenigsten
ausgelasteten Serverrechner ausgeführt bekommen, ist es nötig,
einen Server-Server zu betreiben, den sogenannten Pool-Manager. Alle Anfragen
(Requests) der Clients gehen also nicht an die Dienstserver, sondern an den
Pool-Manager, der dann einen Server-Rechner bestimmt, der die gewünschte
Antwort (Reply) liefern soll. Natürlich birgt diese Zentralisation wie
immer die üblichen Probleme: Flaschenhals und Single Point of Failure.
-
Client/Server-Kooperation: Bei dieser Methode sendet der Client seine
Requests nicht an einen bestimmten Dienstserver oder Pool-Manager, sondern gibt sie
für alle sichtbar (broadcast) ins Netz. Hier gibt es nun verschiedene Arten, wie mit
dieser Broadcast-Nachricht verfahren wird. Einmal ist es möglich, dass die
Clients sich damit untereinander abstimmen, wer welchen Server benutzen darf
(client-kontrollierte Auswahl). Besser ist es aber, die Server selbst bestimmen
zu lassen, wie sie auf die Broadcast-Anfrage reagieren sollen
(serverkontrollierte Auswahl). Die Server kennen sich i.d.R. gegenseitig und
können dadurch den jeweiligen Auslastungsgrad ermitteln, sodass
sich stets der am wenigsten ausgelastete Server bei Client melden kann.
PROCESS Client
DO
BROADCAST(wer ist frei?);
RECEIVE(ACK von bestimmten server);
SEND(request);
RECEIVE(reply);
END
Interaktionsmuster haben etwas damit zu tun, wie sich Prozesse
untereinander Nachrichten zusenden. Prozessorganisation hat etwas damit
zu tun, wie dafür gesorgt werden kann, dass alle Rechner im Netz
gleichmässig ausgelastet werden können.
Im vorherigen Kapitel wurde beschrieben, über welche Interaktionsmuster
sich Prozesse koordinieren können, um gemeinsam eine Aufgabe zu lösen.
Als das wichtigste Interaktionsmuster wurde dabei das Client/Server-Konzept
herausgestellt. Des Weiteren wurde gezeigt, wie sich die im Netz verteilten
Serverprozesse gegenseitig organisieren, sodass die einzelnen
Server-Rechner möglichst gleichmässig ausgelastet sind.
In diesem Kapitel wenden wir uns der Nachrichtenart "Primitive" zu. Diese
dienen dazu, aus einem (Client-)Prozess heraus einen anderen
(Server-)Prozess zu erzeugen. Je nach Komplexität derartiger
Primitive können die folgenden Kommunikationsmodelle unterschieden werden.
-
Mitteilungsorientiertes Kommunikationsmodell: Bei diesem Modell können
nur sehr einfache Primitive durch den Client gesendet werden, bei denen keine
Rückantwort durch den Server erfolgt. Diese Primitive sind also
unidirektionale Nachrichten. Ein Beispiel hierfür sind unquittierte E-Mails.
Wenn Sender und Empfänger nicht synchronisiert werden, dann arbeitet
der mitteilungsorientierte Sender nach dem "No-Wait-Send"-Prinzip, d.h., er sendete
die Primitive ohne jede Verzögerung. Dies bedingt jedoch, dass der
Empfänger entweder über Puffer verfügt oder ein "Busy-Waiting"
durchführt. Evtl. kann auch im Netzwerk selbst ein Pufferung vorgenommen
werden.
Sind Sender und Empfänger synchronisierbar, dann arbeitet der Sender
nach dem "Synchronization-Send"-Prinzip, auch "Rendezvous"-Prinzip genannt, weil
sich hier Client und Server zu einem bestimmten Zeitpunkt treffen. Dazu
benötigt der Empfänger keine Puffer (ausser evtl. zur
Flusskontrolle).
-
Auftragsorientiertes Kommunikationsmodell: Bei diesem Modell erhält
der Client nach Sendung des Primitives ein Resultat von dem dadurch erzeugten
Server-Prozess zurück. Die auftragsorientierten Primitive sind also
duplexe Nachrichten. Ein Beispiel hierfür: Uhrzeit-Server.
Im asynchronen Betriebsmodus arbeitet der auftragsorientierte Sender nach
dem "Remote Service Invocation"-Prinzip. Hier kann der Client nach der
Dienstanforderung weiterarbeiten und muss nicht warten, bis der Server die
Antwort sendet (Nicht-Blockierung). Diese Technik wird allerdings in der Praxis
kaum realisiert.
Im synchronen Betriebsmodus arbeitet der Sender nach dem
"Remote Procedure Call"-Prinzip. Hier arbeiten Primitive genauso wie
Unterprogramme. Während das Unterprogramm (der Serverprozess)
ausgeführt wird, blockiert das Hauptprogramm (der Clientprozess)
so lange. Dies ist die meist benutzte Technik bei verteilten Betriebssystemen,
wobei das erklärte Ziel völlige Transparenz ist, d.h., dass
entfernte Unterprogrammaufrufe nicht von lokalen zu unterscheiden sein sollen.
Allerdings erlauben Remote Procedure Calls nur sehr selten Pointer als
Argumente.
-
Transaktionsorientiertes Kommunikationsmodell: Bei diesem Modell
können komplexe Primitive vom Client gesendet werden, die nur dann ein
Resultat vom Server-Prozess erhalten, wenn sie zuvor vollständig
abgearbeitet worden sind. Dadurch wird effektiv ausgeschlossen, dass halb
ausgeführte Serverdienste irgendwelche unbeabsichtigten Seiteneffekte
hervorbringen können. Ein Beispiel hierfür: Homebanking.
Transaktionen zeichnen sich im Übrigen durch die folgende ACID-Eigenschaft aus:
- Atomicity:: Sie arbeiten nach dem Alles-oder-Nichts-Prinzip.
- Consistency: Sie gehen von einem konsistenten Zustand in einen anderen über.
- Isolation: Sie eigen nur bei Erfolg Auswirkungen.
- Durability: Ihr Erfolg geht nicht verloren.
Die oben aufgeführten Kommunikationsmodelle sind erheblich variierbar,
wodurch sie sich optimal auf das jeweilige Subnet oder die zugrunde liegenden
Rechnerkonfigurationen anpassen lassen. So können die Modelle z.B. sein:
-
gepuffert/ungepuffert, wobei eine alternative Pufferung beim
Sender/Empfänger/Medium möglich ist. I.d.R. ist eine Pufferung nur
bei nicht-blockierenden Knoten und also asynchronem Betrieb nötig.
-
atomic/zuverlässig/unzuverlässig, wobei folgende Fehlerklassen
möglich sind:
- maybe: Ein Paket kommt vielleicht nie am Ziel an.
- at least once: Mindestens ein (Duplikat-)Paket erreicht Ziel.
- at most once: Höchstens ein (Duplikat-)Paket erreicht Ziel.
- exactly once: Idealisiertes "at most once".
-
direkt oder indirekt adressierend, d.h., der Client spricht einen
Serverdienst direkt oder indirekt über einen Name-Server an.
-
blockierend/nicht-blockierend (i.d.R. identisch mit synchron/asynchron), d.h.
der Client kann eventuell weiterarbeiten, während der Server den Dienst
erfüllt.
Als Protokolle kommen üblicherweise Fensterprotokolle infrage (v.a.
beim asynchronen Betrieb), die durch Wahl ihrer Fenstergrösse sehr
anpassungsfähig sind. Bei einer Fenstergrösse von 1 hat man z.B.
ein einfaches Stop-and-Wait-, bei grosser Fensterwahl dagegen ein recht
komplexes Übertragungsprotokoll.
Kommunikationsmodelle haben etwas mit der Komplexität
der nach dem Client/Server-Konzept austauschbaren Primitive zu tun. Die
synchrone Variante des auftragsorientierten Kommunikationsmodells ist dabei das
für verteilte Betriebssysteme am ehesten geeignete.
In einem Rechnernetz haben folgende Objekte Namen: Rechner (Knoten),
Prozesse und Benutzer.
-
Identifikation: Namen sollen existierende Objekte bezeichnen.
-
Indirektion: Namen sollen (dynamische) Objekte referenzieren.
-
Abstraktion: Namen sollen die Details vor dem Benutzer verbergen. Daher
sind vor allem reine Namen (ohne Pfadangabe) beliebt, da sie transparenter als
unreine Namen sind.
Namen müssen widersprüchliche Ziele ausbalancieren.
Sie können sein:
-
unrein/rein, d.h. mit/ohne Ortstransparenz versehen. Bei reinen Namen
fehlen die Pfadangaben, die ein Name-Server über seine Kontextinformationen
herausfinden muss. Das hat den grossen Vorteil, dass Objekte
mit reinen Namen migrieren können, ohne ihren Namen neu definieren zu
müssen.
-
flach/hierarchisch/routingorientiert.
-
benutzerfreundlich/maschinengerecht, d.h., die ersten Namen sind i.d.R. hierarchisch
und die zweiten Namen i.d.R. flach/routingorientiert aufgebaut. Meistens ist die eine
Namensform über eine Abbildungsfunktion in die andere transformierbar - man spricht
dann von Benutzernamen und Systemnamen.
-
statisch/dynamisch, d.h. stabil oder änderbar.
-
absolut/relativ, d.h. mit einem ganzen bzw. relativen Pfadnamen versehen.
-
direkt/indirekt, wobei Letzteres bedeutet, über Mailbox, Ports oder
Links bestimmte Prozesse anzusprechen.
-
Bezeichner, die sowohl zeit- als auch ortstransparent sind. Z.B. "Drucke"
für einen Serverdienst.
-
Identifikatoren, die zwar ortstransparent sind, dafür aber meistens
nicht zeittransparent, z.B. eine Prozess-ID.
-
Adressen, die zwar nicht ortstransparent sind, dafür aber meistens
zeittransparent, z.B. Ethernet-Adresse.
Die meisten Name werden letztlich auf Adressen abgebildet (über eine
sogenannte Abbildungsfunktion). Dabei wird kontextabhängig-analytisch
vorgegangen, d.h., der Name wird aufgespalten, wobei jeder Teil (jedes Attribut)
einen anderen Rechnernetz-Kontext umfasst, abhängig davon, ob ein
hierarchischer, routingorientierter oder sonstiger nicht-flacher Adressenaufbau
vorliegt.
Name-Server sollen helfen, in verteilten Betriebssystemen globale Kontexte
aufzubauen. Neben der Namensverwaltung führen sie üblicherweise auch
die Authentifikation der Clients durch.
-
Zentraler Name-Server: Ein Server verwaltet alle Namen im Netz,
verfügt also über den (idealen) globalen Kontext.
-
Verteilter Name-Server mit ortsabhängigen Namen: Jeder Client ist auch
Name-Server, der bei Broadcast-Calls reagiert, wenn er den gesuchten Namen im
eigenen Kontext führt. Der Pfadname muss jedoch mit angegeben werden,
d.h., ortstransparente Namen sind nicht möglich.
-
Voll verteilter Name-Server: Jeder Client ist auch Name-Server, der jeden
beliebigen Namen auflösen kann. Ortstransparenz ist gegeben.
-
Verkettete Name-Server: In grossen Netzen empfiehlt sich diese
Name-Server-Organisation. Jeder Name-Server verwaltet hier nur den Kontext von
Namen, die er selbst generiert hat. Findet er bei einer Anfrage den gesuchten
Namen nicht, werden andere Name-Server kontaktiert.
Die Namensauflösung bei verketteten Name-Servern kann auf fünf Methoden geschehen:
-
Chaining-Methode: Ein Name Agent-Prozess des Client-Rechners fragt
den Name-Server seines Kontextes nach einem Namen. Findet dieser den Namen
nicht, so fragt er den nächsthöheren Kontext-Name-Server. Findet
dieser den Namen, so meldet er ihn dem ersten Name-Server zurück, der ihn
dann dem Name Agent mitteilt, der ihn sich mittels internem Caching merkt.
Findet er ihn nicht, verfährt er wie der erste Name-Server.
-
Zuweisungsmethode (Referential Method): Bei dieser Methode erfährt
der Name Agent bei Suchmisserfolg eines Name-Servers von diesem jeweils die
Adresse des nächsthöheren Kontext-Name-Servers, den er dann selbst
kontaktieren muss.
-
Weitergabe-Methode: Hier hangelt sich die Suche ähnlich zu der
Chaining-Methode immer höher in den Baum, bis der Name bei einem
Kontext-Name-Server gefunden wird. Diesmal wird allerdings danach nicht mehr der
Baum zurückgelaufen, sondern die bekannte physische Adresse des Name Agent
direkt kontaktiert.
-
Broadcast-Methode: Jeder Client bekommt den gesuchten Namen zugesendet und
der richtige Client antwortet. Diese Methode wird z.B. im ARPA Internet durch
das Address Resolution Protocol-Protokoll realisiert: Man fragt nach "Susanne" und erhält
"129.55.210.111" zurück.
-
Multicast-Methode: Nicht jeder Client, sondern nur jeder
Kontext-Name-Server erfährt den gesuchten Namen und sieht nach, ob er ihn
verwaltet. Wenn ja, meldet er sich beim Client.
Als Beispiel für einen Name-Server kann die X.500-Norm der CCITT
angeführt werden. Dieser globale Directory-Dienst arbeitet mit einer
verketteten Name-Server-Architektur, benutzt daher auch hierarchische Namen,
die kontextuell folgendermassen aufgebaut sind: Country=a,
Organisation=b, Fachbereich=c, Namen=d. Der Namen wird über die
Multicast-, Chaining- oder Referential-Methode von User Agents und
Directory Service Agents aufgelöst, obwohl hier aufgrund des geringeren
Datenverkehrs und der kürzeren Name-Server-Blockierung sicher die
Weitergabe-Methode die bessere wäre.
Auch V-Kernel verfügt über einen Directory-Dienst für das
Mailing. Es gibt drei Ebenen: Die hierarchische Ebene mit Kennung "gov", "edu",
"de" usw., die administrative Ebene mit Kennung "info.uni-mannheim" und
die operationale Ebene mit Kennung "Schwamm", "Bohn" usw. Nur der operationale
Kontext wird von Privatbenutzern gewartet, die restliche Verwaltung obliegt
der Telekom. Die Clients verfügen über einen Namenspräfix-Cache,
um sich den Speicherort von Prozessen merken zu können. Da diese Caches
laufend gewartet werden, können die Objektmanager der operationalen Ebene
gefahrlos migrieren (z.B. nach einem Absturz).
Namen werden in verteilten Betriebssystemen i.d.R. durch
Name-Server verwaltet. Als die Geeignetsten erscheinen uns dabei verkettete
Name-Server, die zur Namensauflösung die Weitergabe-Methode praktizieren.
In einem verteilten Betriebssystem sind die Benutzer, die Betriebsmittel und
die Daten zu schützen. Alternativ kann entschieden werden, ob die
Schutzmechanismen im Betriebssystemkern verankert werden oder ob sie dem zu
schützenden Objekt beigefügt werden. Im ersten Fall muss von
einem abhörsicheren Betriebssystem ausgegangen werden und im zweiten Fall
von einem abhörsicheren Kommunikationssystem. Der zweite Fall scheint
uns für ein verteiltes Betriebssystem bessere geeignet zu sein, da hier die
Systemcalls zum grossen Teil über Remote Procedure Calls simuliert
werden, die über das Subnet abgewickelt werden. Solche RPCs laufen
über Client-/Server-Stubs ab, die über eine Broadcast-Nachricht bzw.
einen Directory-Service das Linken zwischen den Ports übernehmen und z.T.
auch noch eine einfache Datenrepräsentation über Kodierungsregeln
erlauben.
Es gibt viele Wege, ein verteiltes Betriebssystem sicher zu gestalten und
vor Missbrauch zu schützen. Einige davon seien im Folgenden
vorgestellt.
-
Ein-Wege-Autorisierung: Der Benutzer muss sich dem System gegenüber
als autorisierter Benutzer identifizieren. Hierfür eignen sich die klassischen
Passwort-Verfahren.
-
Zwei-Wege-Autorisierung: Der Benutzer und das System müssen sich
gegenseitig als autorisiert zu erkennen geben. Dies schützt in erster
Linie davor, dass "Login-Masken"-Programme den Benutzer täuschen
können, die nur dazu dienen, die Passwörter der Benutzer
abzufangen. Auch hierfür eignen sich die klassischen Passwort-Verfahren.
-
Autorisierungsprozesse zu Beginn einer Computersitzung können nie
ganz sicherstellen, dass nicht doch ein Unbefugter ins Netz hineinkommt.
Sei es durch Zufall oder weil er ein Passwort geklaut oder erraten hat.
Wirklich sensible Daten werden daher häufig noch über kryptologische
Verfahren geschützt. Grob unterschieden werden hier zwei Arten:
-
Symmetrische Verfahren: Codierung und Decodierung erfolgen über nur
einen geheimen Schlüssel.
-
Asymmetrische Verfahren: Codierung und Decodierung erfolgen über
jeweils verschiedene Schlüssel.
-
Sichere verteilte Betriebssysteme Müssen sich vergewissern,
dass ein Client, der einen Dienst anfordert, dazu überhaupt
berechtigt ist. Daher muss sich der Client dazu zuvor einer
Authentifizierungsprozedur unterziehen. Hier existieren zwei Strategien:
-
Der Client (Subjekt) weiss nicht, welche Rechte er auf welche Dienste
(Objekte) hat, der Dienstserver kann dies jedoch über Zugriffsmatrizen
feststellen unter dadurch u.U. eine Verbindung unterdrücken. Zuvor muss
sich der Client bei einem Name-Server authentifiziert haben. Nur dann erhält
er die Dienstserveradresse und die Port-ID für den gewünschten Dienst
zurück. So kann sich kein Client als ein anderer ausgeben und den Dienstserver
übers Ohr hauen.
-
Der Client weiss, welche Rechte er auf welche Dienste hat, weil er für sie
bestimmte Befähigungen (Capabilities) besitzt. Solche Capabilities sind im Prinzip
alternativ implementierte Zugriffsmatrizen. Will er einen Dienst bei einem Server
anzufordern, so authentifiziert er sich zuerst beim Name-Server und sendet diesem
seine Capability zu. Nur wenn sie korrekt ist, rückt der Name-Server mit der
Dienstserveradresse und der Portnummer für den gewünschten Dienst
heraus.
Auch wenn es nötig ist, dass die Capabilities
fälschungssicher sein müssen, so empfiehlt sich die
Capability-Methode doch eher als die Zugriffsmatrix-Methode, um einen Client
dazu zu autorisieren, einen bestimmten Dienst nutzen zu dürfen.
Wir wollen uns nun einen einfachen Algorithmus ansehen, der dafür
sorgt, dass ein Server sicher sein kann, dass der Client auch genau
der ist, für den er sich ausgibt (was natürlich nicht heisst,
dass er auch jeden Dienst in Anspruch nehmen kann).
1. Der Client sendet dem Server die Message "Ich bin C", einmal im
Klartext und einmal als Code.
2. Der Server codiert die Klartext-Message und vergleicht sie mit der
Code-Message. Ist sie identisch, dann kannte der Client den geheimen
Schlüssel.
Zwei Nachteile hat diese Methode: Sie birgt die Gefahr von Replies, d.h.,
ein Unautorisierter kann hier leicht die Client-Message abfangen und beim
Server als die eigene ausgeben, wo er dann prompt den gewünschten Dienst
erfüllt bekommt. Ausserdem muss hier jeder Server sämtliche
Schlüssel führen.
Um derartige Replies zu verhindern, kann man einmalige Nachrichten (z.B. Uhrzeiten)
versenden, die ein Unautorisierter nicht codieren kann, da er den geheimen
Schlüssel nicht kennt.
1. Client sendet "Ich bin C" an Server.
2. Der Server sendet eine einmalige Nachricht an den Client.
3. Der Client codiert die einmalige Nachricht und sendet sie zurück.
4. Server prüft, ob die einmalige Nachricht richtig codiert wurde.
Problematisch bleibt nach wie vor, dass jeder Server sämtliche
Schlüssel der Clients kennen muss.
Hier kann man sich mit der Einführung einer dritten Instanz helfen,
nämlich mit einem Autorisierungsserver.
1. Client sendet "Ich bin C" an Server.
2. Server sendet einmalige Nachricht an Client.
3. Client codiert einmalige Nachricht und sendet sie zum Server.
4. Der Server codiert einmalige Nachricht mit eigenem Schlüssel,
sendet sie zum Autorisierungsserver.
5. Der Autorisierungsserver entschlüsselt die einmalige Nachricht
zweimal, codiert sie dann mit dem Schlüssel des Servers und
sendet sie diesem zu.
6. Der Server decodiert einmalige Nachricht und überprüft sie.
Bisher haben wir nur symmetrische Verfahren betrachtet. Der letzte
Algorithmus lässt sich jedoch mit asymmetrischen Verfahren (Public
Key) noch etwas verfeinern.
1. Client sendet "Ich bin C" an Server.
2. Server sendet einmalige Nachricht an Client.
3. Client codiert die einmalige Nachricht mit seinem geheimen
Schlüssel und sendet sie zum Server zurück.
4. Server fordert vom Autorisierungsserver den öffentlichen
Schlüssel von C an und decodiert und prüft damit
die geheime Nachricht.
Als Beispiel für das Public Key-Verfahren lässt sich
das RSA-Kryptosystem nennen. Hier ist die Idee, dass öffentlicher und privater
Schlüssel grosse Primzahlen sind, die miteinander dupliziert den Schlüssel
zur Decodierung darstellen. Da es nun aber sehr schwer ist, eine solche
Multiplikation wieder aufzubrechen, selbst dann, wenn einer der Schlüssel und
der Kryptosystem-Programmcode bekannt sind (!), kann einer der Schlüssel ohne Gefahren
öffentlich verfügbar sein.
Um verteilte Betriebssysteme sicher zu machen, muss das ganze
Subnet abhörsicher gestaltet sein. Am besten ist, die Benutzer einer
Zwei-Wege-Autorisierung zu unterziehen. Die Clients verfügen über
Capabilities, die sie sich bei Name-Servern bestätigen lassen, die sie dann
bei Erfolg weiter mit speziellen Dienst-Servern verbinden. Um sicherzugehen,
dass die Clients die sind, als die sie sich ausgeben, empfiehlt sich die geheime
Verschlüsselung einmaliger Nachrichten, zu deren Überprüfung sich
der Name-Server dann jeweils beim Autorisierungsserver den öffentlichen
Schüssel besorgen kann.
Prozesse laufen in Rechnern sequenziell oder nebenläufig ab. Für
verteilte Betriebssysteme interessieren uns v.a. nebenläufige Prozesse. Hier
unterscheiden wir:
-
Multiprogramming: Nebenläufige Prozesse benutzen eine CPU im
Wechsel.
-
Multiprocessing: Nebenläufige Prozesse benutzen mehrere CPUs, aber
nur einen gemeinsamen Speicherbereich.
-
Parallel Processing: Nebenläufige Prozesse benutzen mehrere CPUs und
Speicher. Sie kommunizieren über ein schnelles internes Netz (Bus).
-
Distributed Processing: Nebenläufige Prozesse benutzen mehrere CPUs
und Speicher. Sie kommunizieren über ein schnelles externes Rechnernetz
(LAN).
In UNIX werden z.B. parallele Prozesse durch den Systemcall "fork()" erzeugt
und über "join()" mit dem Vaterprozess synchronisiert, um die
Ergebnisrückgabe des Kind-Prozesses an den Vater an einer bestimmten Stelle
geschehen zu lassen.
Eine andere Möglichkeit zur Realisierung nebenläufiger Prozesse
sind die "cobegin_coend"-Programmkonstrukte:
cobegin
Prozess 1;
Prozess 2;
...
Prozess n;
coend
Prozesskontrollblöcke sind Datenstrukturen im Kernel, die
dem Betriebssystem helfen, Prozesse zu verwalten. Hier wird für jeden Prozess
PID, Priorität, Programmzähler, Register, Stack-Pointer,
Zustandsdaten usw. gespeichert.
Tasks sind die kleinste Verwaltungseinheit zur Betriebsmittelvergabe
(z.B. Stack und Memory). Sie sind heavy-weight, wenn sie mehrere Threads
enthalten. Sie sind medium-weight, wenn sie mehrere Threads enthalten, die
einen eigenen Stack, aber auch einen gemeinsamen Adressraum besitzen. Sie sind
lightweight, wenn sie nur einen Thread enthalten.
Threads sind die Basiseinheit des Scheduling, denn sie sind es, die die
Berechnungen eines Prozesses durchführen, die also die CPU beanspruchen.
Wenn der sie enthaltende Task nicht medium-weight ist, verfügen Threads
über einen eigenen Speicherbereich und einen eigenen Stack, d.h., jeder
Thread kann parallel innerhalb eines Tasks ablaufen.
MACH ist z.B. ein Betriebssystem, welches mit dem oben beschriebenen
Task-Thread-Verwaltungsmodell arbeitet. Seine Tasks sind medium-weight,
die enthaltenden Threads teilen sich also einen gemeinsamen Adressraum,
hier Segmente genannt, die dynamisch vom Betriebssystem anzufordern sind.
Die Prozesskontrollblöcke sind auf Heterogenität eingestellt,
d.h., sie können sich der Architektur des Rechners anpassen, auf dem
sie gerade laufen. Daher muss der Benutzer mittels Process Capabilities
zuerst einmal einen Host-Descriptor anfordern, der eine
Maschinen-Architektur-Beschreibung enthält. Über diesen gelangt
er zu einem Prozess-Descriptor, der die Capabilities auf einen
Exception Handler, Segment-Handler und Thread-Handler vergibt, die einen
Task verwaltbar machen. Auch gibt es bei MACH keine einfachen Systemcalls
mehr, sondern es wird zur IPC ein Kernel-Server benutzt, dessen
Nachrichtensystem zwar umständlicher ist, der damit aber genauso
behandelt wird wie jeder andere Dienst in einem verteilten
Betriebssystem.
Der Scheduler sorgt dafür, dass jeder lokale
Thread gemäss seiner Auslastung CPU-Zeit zugewiesen bekommt. Dabei kann er
zyklisch vorgehen, prioritätsgesteuert sein oder Strategien anwenden wie
"shortest first" oder "first come, first serve".
In Betriebssystemen kann ein Prozess folgende Status innehaben, die mittels des
Dispatchers gewechselt werden:
-
Running: Prozess ist im Besitz der CPU.
-
Ready: Alle Betriebsmittel bis auf CPU vorhanden.
-
Wait: Prozess inaktiv/beendet.
-
Lock: Prozess zur Synchronisation mit anderem Prozess
blockiert.
In verteilten Betriebssystemen genügt das normale Scheduling nicht, da
sich dieses auf nur eine CPU und einen Rechner mit lokalen Tasks beschränkt.
Was nötig ist, ist ein globales Scheduling (was streng genommen gar kein
Scheduling mehr ist). Damit ein solcher globaler Scheduler erkennen kann, von
welchem Prozessor er welchen Task ausführen lässt, ist es sinnvoll,
dass er erfährt, wie ausgelastet die einzelnen CPUs im Netz sind.
Das Dilemma dabei ist immer, dass man eigentlich erst nach dem
Prozessablauf abschätzen kann, wie gross sein Betriebsmittelbedarf
ist. Sehen wir uns dazu einige Lastenausgleichsstrategien an:
-
Lastenabstossungsstrategie (auch "Börsen"-Algorithmus genannt, weil
hier das beste "Maklerangebot" gesucht wird, nach dem sich der "Verkäufer"
dynamisch-lokal entscheidet):
LOOP DO
Lastenmessung in Delta-t
IF Überlast THEN an alle Knoten: Wer nimmt meinen Prozess?
-
Lastenanziehungsstrategie:
LOOP DO
Lastenmessung in Delta-t
IF Unterlast THEN an alle Knoten: Wer hat einen Prozess für mich?
-
Token Passing: Der Rechner, der das Token hat, kann sich broadcast
Lastinformationen einholen und darf dann evtl. Prozesse verlagern.
-
Wurm-Programmierung: Das Wurmprogramm kann sich auf freien Rechnern
fortpflanzen und dann dort eine Migrations-Arbeitsumgebung schaffen, die mit all
seinen Segmenten auf den anderen Rechnern kommunizieren kann.
Die Lastausgleichsstrategien eignen sich auch für ein Migrieren von Tasks,
d.h. für ein Verlagern von Prozessen noch während der Laufzeit, was v.a.
bei lange dauernden Prozessen sinnvoll sein kann. Problematisch dabei ist aber,
dass bei jeder Migration der komplette Adressraum des Prozesses
mitkopiert werden muss (bei Teilmigration ist auch eine
"Copy on Reference"-Aufforderungsstrategie möglich).
Wie soll die Inter-Process Communication danach weitergeführt werden?
-
Weiterleitungsnachrichtenfluss: Statthalter könnten im alten
Knoten bleiben, die alle ankommenden Nachrichten an den neuen Knoten
weiterleiten.
-
Vorab-Informations-Nachrichtenfluss: Vor der Migration werden alle
Partnerprozesse über den neuen Knoten informiert.
-
Verlust- und Recovery-Nachrichtenfluss: Nach der Migration werden alle
Partnerprozesse über den neuen Knoten informiert - dadurch gehen alle
Nachrichten dazwischen verloren, die jedoch u.U. rekonstruierbar sind.
Verteilte Betriebssysteme sollten Prozesskontrollblöcke
besitzen, die für verschiedene Rechnerarchitekturen benutzbar sind.
Das Task-Thread-Prozess-Verwaltungsmodell erlaubt die parallele
Verarbeitung von Prozessen innerhalb einer Verwaltungseinheit. Dies
erhöht bei heavy-weight-Tasks die Geschwindigkeit, aber auch den
Verwaltungsaufwand - light-weight-Tasks scheinen uns daher die angemesseneren
zu sein. Auch was das Scheduling angeht, heisst Einfachheit die
Devise: Lastenausgleichsstrategien über globale Scheduler und Migration
von Prozessen kosten mehr, als dass sie nutzen - auf sie sollte
verzichtet werden.
Herkömmliche Dateiverwaltungssysteme werden
geschichtet aufgebaut, wobei jede Schicht bestimmte Dienste für die
darüberliegende Schicht erledigt. Auch oder v.a. bei verteilten
Betriebssystemen ist das Schichtsystem einzuhalten, z.B. in der Form:
Disk-Service -> File-Service -> Directory-Service -> Transaktionsservice
Anders als bei herkömmlichen Betriebssystemen muss in einem verteilten
Betriebssystem nicht jeder Rechner alle Schichten integriert haben. Es genügt,
wenn ein Rechner z.B. nur eine Schicht (nur einen Dienst) zur Verfügung
stellt, und die restlichen Schichten sich ganz woanders befinden. Bis zu einem
gewissen Grad ist dieses Prinzip bereit in UNIX zu finden, wo es
Diskless-Workstations gibt, die auf Datei-Server angewiesen sind (nur bis zu
einem gewissen Grad deshalb, weil die hier verwendeten Datei-Transfersysteme
wie FTAM und FTP nicht transparent arbeiten).
-
Zentraler Datei-Server, der die komplette Dateiverwaltung
übernimmt.
-
Replizierter Datei-Server, der zwar skalierbar ist, aber Update-Problemen
unterliegt, und wo der jeweils richtige Server für den Client schwerer
zu finden ist.
-
Vollständig verteiltes Dateisystem: Alle Rechner können
Datei-Server sein für ihr lokales Umfeld, sind also mit einem eigenen
Dateisystem ausgestattet.
Ein Problem bei verteilten Dateisystemen, die ja
auch die Kernel-Dienste als ausführbare Prozesse anbieten sollen, ist der
erhöhte Bedarf an Zeit pro Dateizugriff, die sich zusammensetzt aus:
-
Plattenzugriffszeit + E/A-Kanalübertragungsdauer (diese Zeit kann
fast auf null reduziert werden, wenn Cache-Techniken eingesetzt werden).
-
DÜ übers Netz (kommt bei entfernten Zugriff hinzu).
-
CPU-Zeit zur Bearbeitung der Informationen.
Das Suchen von Dateinamen im Netz kann der Client
übernehmen, doch sollte dazu besser ein Datei-Server verwendet werden.
In der Regel wird dieser Pfadnamen über rekursive Lookup-Algorithmen zu
lokalisieren versuchen (z.B. Lookup von "/a/b/C/prg1"). Dies erscheint
v.a. im Zusammenhang mit den oben vorgestellten verketteten Datei-Servern
sinnvoll, die jeweils ihren Kontext durchsuchen können. Verbesserungen
sind mittels Caching möglich. Auch ein NFS-ähnliches Mounting
zur Schaffung eines globalen Dateibaums erleichtert das Lookup, da es sich
auf die gemounteten Teilbäume beschränken kann.
Zur Verdeutlichung: Mittels NFS will Rechner A über das Verzeichnis
"A:/usr1/shared" den Directory-Baum "B:/usr2/local" des Rechners B mounten.
Herauskommt: "A:/usr1/local", wobei der ursprüngliche Teilbaum erst nach
einem Remount wieder zugreifbar ist.
Welche Bedeutung hat ein (ändernder) Dateizugriff eines Prozesses
für andere Prozesse?
-
UNIX-Semantik: Wenn hier jemand eine Datei beschreibt, dann bekommt das
jeder andere Prozess bei einem Read unmittelbar mit. Der Dateiserver kann
stateless und verbindungsunabhängig sein. Dadurch ist das Netz durch
seinen Absturz nicht sehr gefährdet. Allerdings muss sichergestellt
sein, dass Prozesse, die er anbietet, idempotent sind.
-
Session-Semantik: Wenn hier jemand eine Datei beschreibt, bekommen dies
nur die lokalen Prozesse unmittelbar mit, weil die entfernten Prozesse mit Duplikaten
arbeiten. Der Dateiserver kann stateless oder stateful sein.
-
Immutable Semantik: Wenn hier jemand eine Datei beschreiben will, so
muss er zuerst ein Duplikat davon erzeugen. Der Dateiserver dürfte
stateless sein.
-
Transaction-Semantik: Wenn hier jemand eine Datei beschreibt, hat er sie
zuvor mit einer Lock-Option geöffnet. Der Dateiserver ist dann meist
stateful und verbindungsorientiert, d.h., er merkt sich für jeden Client
die Position des Schreiblesekopfs u.ä. Ein Absturz kann das Netz
zusammenbrechen lassen. Aber auch ein stateless Datei-Server ist denkbar.
Hier wird das Lokalitätsprinzip ausgenutzt, also die
Tatsache, dass ein Prozess meistens Dateien beansprucht, die sich in
unmittelbarer Nähe zueinander befinden. Daher ist es sinnvoll bei entfernten
Zugriffen nicht nur den einen Dienst, sondern gleich ganze Blöcke oder
Seiten in den lokalen Speicher des entfernten Rechners (oder des eigenen) zu
kopieren.
Wichtig sind dabei Validierungsstrategien bzw. Update-Strategien, die am besten vom
Dateiserver durchgeführt werden, um sicherzustellen, dass der
Cache-Inhalt mit dem Platteninhalt übereinstimmt. Hierzu bieten sich
an:
-
write through: Jedes Cache-Beschreiben bewirkt Plattenbeschreiben.
-
delay write: Schreiben vom Cache auf Platte z.B. alle 30 Sekunden.
-
write-on-close: Schreiben vom Cache auf Platte wenn der Dateizugriff
beendet wird.
Auf das Schichtprinzip kann bei verteilten Betriebssystemen nicht
verzichtet werden. Vollständig verteilte Dateiserver sind zentralen Dateiservern
vorzuziehen. Zur Dateinamensfindung sollten fremde Directories in den eigenen
Directory-Baum montierbar sein, die Suche übernehmen die Datei-Server über
ein rekursives Lockup der Pfade. Für den Dateizugriff sind stateless Datei-Server
vorzuziehen, die nach der Transaction-Semantik arbeiten. Und auf Caching - v.a.
bei den entfernten Server-Rechnern - sollte wohl auch nicht verzichtet werden.
In den obigen Kapiteln wurde beschrieben, auf welche verschieden Weisen sich
verteilte Betriebssysteme realisieren lassen.
Im Anschluss zu jedem Kapitel wurde dabei die Favorisierung bestimmter Eigenschaften
vorgenommen, die wir hier nun in Form einer Checkliste für ein optimales verteiltes
Betriebssystem auflisten wollen:
- Client/Server-Interaktionsmuster
- Client/Server-Kooperationsorganisation
- Broadcast-Medium
- Load Sharing-Server
- Synchrone Kommunikation
- auftragsorientiertes Kommunikationsmodell
- Primitive-Austausch
- verkettete Name-Server
- Weitergabe-Methode zur Namensauflösung
- abhörsicheres Subnet
- 2-Wege-Eingangsautorisierung
- Zugriffs-Capabilities
- asymmetrische Codierung (öffentliche Schlüssel)
- Autorisierungsserver mit Name-Server kombiniert
- einmalige Nachrichten zur Entschlüsselungsprobe
- von der Heterogenität adaptive Prozesskontrollblöcke
- Task-Thread-Prinzip mit Lightweight-Tasks
- globales Scheduling ohne Lastenausgleich und Migrationsstrategien
- Schichtkonzept
- vollständig verteilte Dateiserver
- montierbare Directories
- stateless Dateiserver
- Transaction-Semantik
- Caching beim entfernten Server
AMOEBA ist ein Forschungssystem der Universität Amsterdam, entwickelt
u.a. von Andrew S. Tanenbaum. Es handelt sich hierbei um ein verteiltes Betriebssystem
der Art Kernverbundsystem, d.h., dass nur der Kern des Betriebssystem
SIDOS (Single Disk Operating System) auf allen Knoten läuft. Die an das
LAN angeschlossenen Workstations sind alleine nicht lauffähig, sie
beherrschen nur noch die Inter-Process Communication- (IPC) und die
E/A-Kanäle. Sie finden im Netz einen Prozessorpool und diverse Server-Rechner,
die alle sonstigen Betriebssystemleistungen stellen können, z.B. die Datei- und
Speicherverwaltung.
An besonderen Servern gibt es:
-
Bullet-Server: Ein spezieller Datei-Server, der keine Dateiänderungen
erlaubt und daher sehr schnell arbeitet, weil z.B. die konkurrierende
Zugriffssteuerung weg fällt.
-
Boot-Server: Ein Überwachungsserver, der dafür sorgt, dass
ein abgestürzter Server wieder hochgefahren wird.