Durch die Hintertür zu Unix: Minix für den Atari ST

Julian Reschke/Stefan Eissing

In diesem Artikel machen wir Sie mit einem neuen Betriebssystem für den Atari ST vertraut, das sich von anderen seiner Art in mehreren Punkten unterscheidet. »Minix ST«, so der Name des Betriebssystems, stammt von Andrew S. Tanenbaum, einem Professor an der Vrije Universiteit Amsterdam. Er hat es 1986/87 selbst entwickelt und dann in seinem Buch »Operating Systems: Design and Implementation« mitsamt dem Quelltext veröffentlicht. Gleichzeitig mit dem Erscheinen dieses Buches war Minix für den IBM-PC erhältlich — es wurde auf diesem Rechner entwickelt — und fand dort auch starke Verbreitung. Mit der Portierung auf den ST (und damit auf den MC 68000) wird Minix sicher demnächst auch auf anderen Computern mit diesem Prozessor wie dem Macintosh und dem Amiga erhältlich sein.

Jeder, der sich näher mit Computern befaßt, hat schon von dem Betriebssystem Unix der Firma AT&T gehört. In den Siebzigern entwickelt, waren die früheren Versionen von Unix auch im Quelltext leicht erhältlich, und vor allem Universitäten verwandten diese als Beispiel in Vorlesungen über Betriebssysteme. Mit der weiten kommerziellen Verbreitung von Unix in der Version 7 verschärfte AT&T die Lizenzbedingungen, und der Quelltext durfte nicht mehr in Vorlesungen benutzt werden.

Dies scheint Andrew S. Tanenbaum nicht gefallen zu haben, und nach dem Motto »Grau ist alle Theorie!« machte er sich daran, sein eigenes Mini-Unix zu schreiben. Leser des oben genannten Buches sind damit in der ungewohnten Lage, neben interessanten theoretischen Konzepten auch deren Anwendung in der Praxis kennenzulernen. Wer in einem Lehrbuch zum x-ten Mal den Satz »Der einfache Beweis (Implementation, etc.) bleibt dem geneigten Leser überlassen« liest, weiß dies zu schätzen.

Minix ist weitgehend kompatibel mit der Version 7 von Unix. So ist die Schnittstelle für Betriebssystemfunktionen und auch die Benutzeroberfläche (wenn man einen Command-line-Interpreter so nennen möchte) nahezu identisch mit der von Unix.

13000 Zeilen Quelltext

Ebenso wie der große Bruder wurde Minix fast ausschließlich in der Programmiersprache C geschrieben. Der Quelltext umfaßt knapp 13000 Zeilen, und das ist für ein Betriebssystem wenig. Um die Lesbarkeit zu erhöhen, legte Tanenbaum auf strukturierte Programmierung großen Wert und unterteilte Minix in vier Module:

Laufende Programme bezeichnet Minix als Prozesse. Im Gegensatz zu GEMDOS kann Minix aber mehrere Prozesse gleichzeitig bearbeiten (sogenanntes Multiprogramming). So sind Sie zum Beispiel in der Lage, mit Ihrer Text Verarbeitung einen Artikel zu schreiben, während ein anderer Prozeß versucht, Ihre favorisierte Mailbox anzurufen. Gleichzeitig überwacht ein weiteres Programm Ihren Terminkalender, damit Sie Ihre nächste Verabredung mit Ihrer Freundin/Ihrem Freund nicht versäumen.

Nun bearbeitet natürlich eine einzige CPU, wie es bei den meisten Computern noch üblich ist, nicht mehrere Programme wirklich gleichzeitig. Vielmehr schaltet Minix mehrmals in der Sekunde zwischen den einzelnen Prozessen um und bearbeitet immer einen von diesen ein paar Millisekunden lang.

Dieser Vorgang wird in der Fachsprache als »Scheduling« bezeichnet. Der Teil eines Betriebssystems, dem diese Aufgabe zufällt, heißt »Scheduler«. Ein Prozeß befindet sich somit immer in einem von drei verschiedenen Zuständen: laufen, bereit oder blockiert (running, ready, blocked). Er ist blockiert, wenn er zum Beispiel darauf wartet, daß ein Block von der Diskette gelesen wird; es hätte keinen Sinn ihm Rechenzeit zu geben, bevor dieser Block gelesen ist, er also wieder bereit ist. Beim Umschalten zwischen den Prozessen kann der Scheduler nun verschiedene Strategien verfolgen.

Eine kleine Zwischenbemerkung: Wir bombardieren Sie hier nicht mit Fachausdrücken, weil wir uns dabei so toll Vorkommen, sondern weil sich diese Begriffe etabliert haben und so jeder sofort weiß, wovon der andere spricht.

Die einfachste Vorgehensweise ist das »Round Robin Scheduling«. Der Prozeß, der am längsten inaktiv war, kommt als nächster dran. Das klingt sehr vernünftig, aber es berücksichtigt nicht, daß es wichtige Prozesse gibt, die möglichst oft laufen müssen. So ist in Minix das Dateisystem und auch der Memory-Manager ein eigener Prozeß. Wenn sie genauso wenig Zeit wie andere Programme bekommen, entsteht ein Engpaß, da sie für fast alle Programme Arbeiten erledigen.

Eine Lösung dieses Problems bietet das »Priority Scheduling«. Jeder Prozeß bekommt eine Priorität zugewiesen. Schaltet der Scheduler auf einen anderen Prozeß um, so nimmt er immer den mit der höchsten Priorität, der bereit ist. Ändert sich die Priorität eines Prozesses nicht, kann dieses Verfahren dazu führen, daß ein Programm mit niedriger Priorität nie an die Reihe kommt. Deswegen hat man eine dynamische Variante dieses Verfahrens entwickelt. Sie erhöht die Priorität eines Prozesses, der lange nicht mehr gelaufen ist, immer mehr. Nachdem dieser Prozeß zwangsläufig einmal an die Reihe gekommen ist, bekommt er wieder seine alte Einstufung. Doch diese Vorgehensweise hat den Nachteil, daß der Scheduler mehr Verwaltungsaufwand durchzuführen hat als beim Round-Robin-Scheduling, wo das Betriebssystem die Prozesse in einer einfachen Liste führt.

Minix benutzt eine Mischung dieser beiden Verfahren. Es gibt drei verschiedene Prioritäten. Die höchste Einstufung haben die sogenannten I/O-Tasks (dazu später mehr), danach kommen das Dateisystem und der Memory-Manager. Die niedrigste Priorität haben die vom Benutzer gestarteten Programme, die nach Art des Round-Robin verwaltet werden.

Fünf speisende Philosophen

Der größte Teil des Kapitels über Prozesse im Buch von Tanenbaum beschäftigt sich mit der Kommunikation der Prozesse untereinander. Die dort auftretenden Probleme sind zu vielschichtig, um sie hier zu behandeln, aber ein Problem wollen wir Ihnen doch vorstellen: die fünf dinierenden Philosophen (»The dining philosophers problem«).

Fünf Philosophen sitzen an einem runden Tisch. Jeder hat einen Teller mit Spaghetti vor sich und zwischen jedem Teller liegt eine Gabel. Das Leben eines Philosophen besteht aus Zeiten des Nachdenkens und Essens (auch Andrew S. Tanenbaum meint, daß das sogar für Philosophen etwas zu abstrakt gesehen sei, aber die anderen Aktivitäten spielen in diesem Fall keine Rolle). Das Problematische an der Sache sind nur die Nudeln:

Sie sind so schlüpfrig, daß man zwei Gabeln braucht, um sie essen zu können. Bekommt ein Philosoph die zwei Gabeln neben seinem Teller, so ißt er eine Zeitlang und legt sie dann wieder hin. Können Sie nun ein Programm für jeden Philosophen schreiben, das genau dieses tut und keinen Philosophen verhungern läßt? Die Zeiten des Essens und Nachdenkens sind zufällig zu wählen.

In Minix kommunizieren Prozesse miteinander, indem sie sich Nachrichten senden. Ein sendender Prozeß ist blockiert, bis der andere die Nachricht gelesen hat (Rendezvous-Technik). Diese Technik erfordert wenig Verwaltungsaufwand, da Nachrichten nicht zwischengespeichert werden müssen.

Wie der Name EDV schon sagt, ist ein Computer dazu da, Daten zu verarbeiten. Programme lesen Daten (Input), verarbeiten diese und geben die Ergebnisse wieder aus (Output). Dabei sind die Quellen und das Ziel dieser Ein-/Ausgabe verschiedenster Natur: Tastatur, Disketten, Modems, Drucker, Bildschirm etc. Betriebssysteme, die für einen bestimmten Computer geschrieben wurden, wie beispielsweise GEMDOS, sehen meist nur bestimmte Ein-/Ausgabegeräte (I/O Devices) vor und unterstützen das Einbinden neuer Geräte mehr schlecht als recht. Programme, die neue Geräte ansteuern, sogenannte Gerätetreiber (Device Driver), lassen sich in Minix dagegen dank des gut strukturierten Aufbaus leicht installieren. Sie sind in folgendes Schichtenmodell eingebunden:

oberste Schicht:

ganz unten:

Im Normalfall sieht der Ablauf wie folgt aus: Das Benutzerprogramm teilt dem Dateisystem mit, daß es ein Zeichen aus einer bestimmten Datei lesen möchte. Das Dateisystem stellt fest, daß diese Datei für die serielle Schnittstelle des Computers steht und bittet den entsprechenden Gerätetreiber um ein Zeichen von diesem Gerät. Ist vorher noch kein Zeichen an der Schnittstelle angekommen, so legt sich der Treiber schlafen: Er geht in den Zustand »blockiert«.

Wenn die Hardware ein Zeichen empfängt, löst sie einen Interrupt (Unterbrechung des gerade laufenden Programms) aus, eine Interruptroutine schreibt das Zeichen in einen Puffer und weckt den Gerätetreiber (Status ready). Der Gerätetreiber, auch I/O-Task genannt (siehe oben), wird wegen seiner hohen Priorität schon bald zu einem laufenden Prozeß und gibt das Zeichen an das Dateisystem weiter; von dort aus gelangt es an das Benutzerprogramm. Natürlich ist das Benutzerprogramm während dieses Vorgangs blockiert, damit in der Wartezeit auch andere Programme laufen.

Da unter Minix auch mehrere Programme gleichzeitig laufen, kann es zu Problemen mit dem Verteilen von Geräten kommen. Es wäre sinnlos, wenn zwei Programme zugleich einen Drucker benutzen, selbst wenn sie in verschiedenen Farben drucken. Die Methode, mit der man dieses Problem umgeht, ist denkbar einfach: Wer zuerst kommt, mahlt zuerst. Der erste Prozeß darf den Drucker benutzen, alle nachfolgenden Anforderungen von Prozessen bewirken, daß diese warten müssen, bis der erste Prozeß den Drucker wieder freigibt.

Deadlocks weiden ignoriert

Das funktioniert ja gut, sagen Sie, aber Achtung! Angenommen wir haben zwei Prozesse X und Y und dazu zwei Geräte A und B. X benutzt Gerät A, Y Gerät B. X hätte jetzt zusätzlich gerne Gerät B, die Anforderung wird zurückgestellt, und X wartet darauf, daß Y Gerät B wieder freigibt. Nun verfällt Y aber auf die Idee, daß es Gerät A gebrauchen könnte. Da X weiterhin Gerät A belegt, wird nun auch Y schlafen gelegt. Beide Prozesse schlafen jetzt bis in alle Ewigkeit, da sie an der Konstellation nichts mehr ändern können.

Solche Situationen nennt man Deadlocks. Da die Anzahl der Geräte und Prozesse nicht auf vier beschränkt ist, können die Konstellationen, die zu einem Deadlock führen, beliebig kompliziert sein. Leider gibt es (noch?) keinen Algorithmus, der alle Deadlocks verhindert. Minix und auch Unix haben einen sehr einfachen Weg gefunden, mit Deadlocks umzugehen: Sie ignorieren diese Thematik einfach (Tanenbaum nennt diesen Nicht-Algorithmus das »Ostrich-Verfahren«).

Die Anforderungen an die Speicherverwaltung in Minix sind nicht allzu hoch, da Minix aus Gründen der Portabilität mit möglichst wenig Anforderungen an die Hardware auskommen will. Ein Prozeß besteht vom Standpunkt der Speicherverwaltung aus drei Segmenten: Text-, Data- und Stacksegment. Dabei liegen das Data- und Stacksegment immer hintereinander im Speicher. Der Stack wächst in Richtung des Datasegments, während der Datenbereich auf den Stack zu vergrößert wird. Die Gesamtgröße eines Prozesses ändert sich dagegen nie. Wie den Namen leicht zu entnehmen ist, enthält das Textsegment den ausführbaren Code und das Datasegment die Daten des Programms.

Im wesentlichen muß der Memory-Manager zwei Systemroutinen verwirklichen: »fork()« und »exec()«. fork() legt eine exakte Kopie eines Prozesses an, und exec() ersetzt den aufrufenden Prozeß durch einen neuen. Die übliche Vorgehensweise um ein Programm zu starten, ist zuerst ein Aufruf von fork(), um danach die Kopie mittels exec() durch das gewünschte Programm zu ersetzen.

Da das Textsegment einer Kopie zu jeder Zeit mit dem Original identisch ist, braucht Minix bei einem fork() nur Data- und Stacksegment zu verdoppeln. Der Memory-Manager führt dazu eine Liste freier Speicherblöcke und durchsucht diese bei Bedarf nach dem ersten Block, der groß genug ist, die Anforderungen zu erfüllen. Dieser Algorithmus wird allgemein als »First-Fit« bezeichnet.

Prozesse haben unter Unix zwar das gleiche Format, lassen sich dort aber viel flexibler verwalten. Fast alle Computer, auf denen Unix läuft, beherrschen die virtuelle Adressierung. Unix teilt dazu den Speicher in Segmente von meist 4 KByte und weist den physikalischen Adressen dieser Segmente logische Adressen zu. Belegt ein Prozeß 20 Segmente, so liegen diese logisch hintereinander, dürfen aber physikalisch weit verstreut sein. Damit spielt die Zerstückelung des Speichers keine Rolle mehr, es müssen nur genug Segmente für einen neuen Prozeß frei sein, um ihn zu starten. Zudem fordert Unix von der Hardware einen Speicherschutz, damit kein Prozeß auf den Speicherbereich eines anderen zugreifen kann. Ein »Krieg der Kerne« zwischen den Benutzern findet also unter keinen Umständen statt.

Wie weit die Unterstützung durch leistungsstarke Hardware ausgenutzt wird, zeigt das Konzept des »Demand Paging«. Benutzt ein Programm fork(), so setzt das Betriebssystem bei den Segmenten des Data- und Stackbereiches ein spezielles Bit, vergrößert aber den Speicherbereich nicht. Beide Prozesse arbeiten jetzt auf demselben Bereich. Solange beide nur Daten lesen, passiert nichts. Bei einem Schreibzugriff auf ein Segment löst das dafür gesetzte Bit eine Unterbrechung aus, und das Betriebssystem verdoppelt nur dieses eine Segment. Die beiden Prozesse bekommen dadurch nur soviel Speicher wie unbedingt nötig.

Geräteunabhängig

Nicht zuletzt aus Gründen der Portabilität wurde die Dateiverwaltung in Unix, und damit auch in Minix, möglichst geräteunabhängig konzipiert. Zwar spielt es für den Benutzer von GEMDOS auch keine Rolle, wie sein Diskettenlaufwerk genau funktioniert, oder wie dort die einzelnen Dateien angelegt sind, aber er weiß immer, auf welchem Gerät (Laufwerk) er sich befindet. Wenn Sie schon einmal mit einem Programm auf der Festplatte arbeiten wollten, das seine Daten immer auf Laufwerk A sucht, wissen Sie um die Nachteile eines solchen Systems.

Minix hat ein großes Dateisystem, das — für den Benutzer nicht sichtbar — auf mehreren Geräten verteilt sein darf. Das Dateisystem hat eine baumartige Struktur: Wie unter GEMDOS sind auch hier Ordner bekannt, die ihrerseits wiederum Ordner enthalten dürfen. Der Baum des Dateisystems hat ein »Wurzelverzeichnis«, besser als Root-Directory bekannt, mit dem Namen »/«. Von diesem gehen alle Pfade aus, wie zum Beispiel »/usr/Steve/artikel«.

Das Wort Dateisystem hat in der Unix-Welt leider verschiedene Bedeutungen. Zum einen ist damit das logische, eben beschriebene Dateisystem gemeint, zum anderen wird damit auch die physikalische Anordnung von Dateien auf einem Medium bezeichnet.

Physikalische Dateisysteme lassen sich mit speziellen Befehlen in das logische an beliebiger Stelle einfügen. Mit dem Befehl

/etc/mount /dev/fd0 /user

weiß das Programm »mount« im Ordner »etc«, daß es das physikalische Dateisystem in Laufwerk A im logischen Dateisystem im Ordner »user« einhängen soll. Doch was ist »/dev/fd0«? Dazu muß man wissen, daß es in Minix spezielle Dateien gibt (sie liegen üblicherweise im Ordner »dev«), die verschiedene Geräte repräsentieren. Wenn ein Programm die Datei »/dev/tty« öffnet und hineinschreibt, so werden die Daten an das Terminal geschickt, das der Treiber »tty« verwaltet.

Damit sich unter Minix nun nicht jeder mit den Geräten in »dev« auseinandersetzen muß, weist das Betriebssystem jedem Programm beim Start drei Standardgeräte zu: stdout (Ausgabe), stdin (Eingabe) und stderr (Ausgabe für Fehlermeldungen). Dies sind Kennungen für geöffnete Dateien, das Programm kann direkt darauf zugreifen. Normalerweise steht stdin für die Tastatur und stdout für den Bildschirm, der Benutzer darf aber auch jedes andere Gerät (soweit sinnvoll) benutzen. Diese Umlenkung der Ein-/ Ausgabe (I/O-Redirection) ist eine der Stärken von Minix.

Viele Hilfsprogramme unter Minix benutzen nur stdin/-out. Ein Beispiel: Proff, ein Textformatierer, geht diesen Weg.

proff < eingabe.dat >

liest aus eingabe.dat und gibt auf den Bildschirm aus.

proff < eingabe.dat > ausgabe.dat

schreibt die Ausgabe in die Datei ausgabe.dat.

proff < eingabe.dat > /dev/lpr

druckt den formatierten Text aus.

Weiterhin ist Minix in der Lage, eine Kette von Prozessen aufzubauen, die im wahrsten Sinne des Wortes Hand in Hand arbeiten. Dazu startet es mehrere Prozesse gleichzeitig, und die Ausgabe des einen wird zur Eingabe des nächsten. Solch eine Kette nennt man Pipeline oder auch kurz Pipe.

»ls« listet den Inhalt des aktuellen Ordners, und »sort« sortiert eine Reihe von Zeilen alphabetisch. Das Kommando »ls:sort« gibt somit den Inhalt des aktuellen Ordners alphabetisch sortiert aus. Sinnvollere Anwendungen von Pipes sind zum Beispiel das Hintereinanderschalten von Textformatierern, die aufeinander aufbauen. Ein Programm setzt so leicht auf einem anderen auf, der Programmierer braucht nicht jedesmal das Rad neu zu erfinden.

Ein Superuser für das System

Unter Minix sind alle Benutzer gleich. Alle? Nein, ein Benutzer am oberen Ende des Dateisystems leistet erbitterten Widerstand. Es ist der Superuser, auch »root« genannt. Dieser Benutzer genießt alle Rechte und ist mit der Pflege und Wartung des Systems beauftragt. Normale Benutzer haben dagegen nur eingeschränkte Rechte, können sich aber gegen andere Benutzer schützen.

Bevor ein Benutzer mit Minix arbeiten kann, muß er sich dem System mit seinem Namen und einem Paßwort zu erkennen geben. Legt er dann während einer Sitzung neue Dateien an, so erhalten diese Dateien seine Kennung. Sie gehören ihm. Weiterhin kann er für andere Benutzer Zugriffsrechte vergeben. Diese Rechte umfassen den Schreibund Lesezugriff und legen fest, ob ein anderer ein Programm ausführen darf. Diese Rechte gibt es auch für Ordner. Jeder Benutzer erhält seinen Platz im Dateisystem; dies stellt ein problemloses Nebeneinander von mehreren Benutzern sicher.

Neben der normalen Benutzerkennung erhält jeder Benutzer zusätzlich noch eine Gruppenkennung. Mehrere Benutzer, die an einem meist sehr umfangreichen gemeinsamen Projekt arbeiten, schützen so ihre Dateien vor anderen, ohne untereinander Einschränkungen in Kauf nehmen zu müssen.

Nach dieser Vorstellung von Minix im allgemeinen machen wir Sie jetzt mit der Implementation auf dem Atari ST vertraut. Zusammen mit dem knappen Manual (die zusätzliche Anschaffung des 69 Mark teueren Tanenbaum-Buchs wird dringend empfohlen) bekommt man neun einseitig formatierte Disketten. Sie enthalten ein minimales Dateisystem (das man auf einer RAM-Disk resident hält), die Standard-Utilities, einen C-Compiler, Quelltexte des Betriebssystems und aller Tools sowie eine TOS-Diskette mit allem Handwerkszeug, um Minix mit TOS-C-Compilern neu zu com-pilieren.

Kommen wir zur Installation. Im Handbuch beschweren sich die Autoren über die Tatsache, daß sie bei der Portierung auf den ST so viele verschiedene Hardwarevarianten berücksichtigen mußten — verschiedene Tastaturlayouts je nach Land, verschieden viel Speicher, Hardware-Uhr oder nicht. Da erscheint es fast wie ein Wunder, daß es auch eine Minix-Version für PCs gibt...

Entsprechend umständlich ist die Installation des Ganzen. Hält man sich aber genau an das Handbuch, kann nicht viel schiefgehen. Nichtsdestotrotz wäre ein Installationsprogramm eine echte Erleichterung. Minix ST läuft auf jeder ST-Konfiguration — vom 512-KByte-Computer mit einseitigem Laufwerk bis zur Maximalkonfiguration. Und da sage noch mal jemand, für echtes Multitasking brauche man viel Hardware...

Zwei Dateisysteme zum Booten

Zum Booten von Minix benötigt man zwei physikalische Dateisysteme: eines für das »Root-Dateisystem«, das beim Start auf eine RAM-Disk kopiert wird, und ein zweites für die eigentliche Arbeit. Zu gut Deutsch bedeutet dies: Will man Minix von seiner Festplatte laden, muß man schon zwei der vier ersten Partitionen opfern. Hier könnte sicherlich noch einiges optimiert werden.

Für alle, die Minix in erster Linie als Studienobjekt betrachten, sind natürlich die mitgelieferten Quelltexte von besonderem Interesse. Immerhin umfassen sie nicht nur die Sourcen des Betriebssystems, sondern auch die aller Standard-Utilities (Crypt, Make, Compress und wie sie alle heißen). Nur beim ACK-C-Compiler (»Amsterdam Compiler Kit«) fehlen die Quelltexte — schade, denn er ist in Sachen Codequalität eigentlich der Schwachpunkt im Paket. Glücklicherweise wird genau beschrieben, wie man mit dem Alcyon-C-Compiler aus dem Atari-Entwicklungspaket Minix-Programme erzeugt (und sogar das gesamte Minix neu compiliert).

Arbeit für Bastler

Für den Bastler gibt es also noch genug zu tun. Dennoch wartet die Minix-Gemeinde bereits sehnsüchtig auf die Portierung eines angemesseneren Compilers: Eine Version von GNU-C ist bereits angekündigt.

Für den eingefleischten ST-Fan kommt die erste Berührung mit Minix einem wahren Kulturschock gleich: Zunächst wird der Bildschirm invertiert (pfui: weiß auf schwarz...), dann muß man sich auch noch mit kryptischen Shellkommandos herumschlagen. Auch hier ist Abhilfe in Sicht: In naher Zukunft soll die Public Domain-Benutzeroberfläche »MGR« (speziell für Unix und Unix-ähnliche Betriebssysteme) die Arbeit wesentlich erleichtern.

Minix — nur ein interessantes Spielzeug? Es ist natürlich unwahrscheinlich, daß Minix im praktischen Einsatz auf dem ST viel Bedeutung gewinnen wird. Aber immerhin steht mit dem 228 Mark teueren Minix bereits eine preiswerte Entwicklungsplattform für TT-Unix-Software zur Verfügung. Ein weiteres Argument für Minix ist, daß Unix allem Anschein nach das Betriebssystem der Zukunft sein wird. Es kann also auf keinen Fall schaden, Minix als eine Art »Trainingsplatz« für Unix zu betrachten, um hier Wissen zu erwerben, daß einem später nützlich ist. In den nächsten Monaten behalten wir auf jeden Fall Minix im Auge und berichten über neue Entwicklungen, (uh)

eMedia GmbH, Bissendorfer Str. 8, 3000 Hannover 61

Literatur: Andrew S. Tanenbaum, »Operating Systems: Design and Implementation«, Prentice-Hall International Editions, 1987



Links

Copyright-Bestimmungen: siehe Über diese Seite