| Axel Rogat |
| Betriebssysteme und betriebssystemnahes Programmieren |
|   |
2.6: Windows
| Kapitel 2 |
3: Grundkonzepte 
|
|   |
|   | 2.7 UNIX |   |
|   |
UNIX ist jedes Betriebssystem, das einem der Standards entspricht, die
im ersten Kapitel erwähnt wurden (hauptsächlich
SVR4,
BSD,
POSIX).
Die Definitionen sind völlig unabhängig von der Rechnerarchitektur.
Vorgeschrieben wird ein Satz von Systemaufrufen und deren
Funktionalität. Ein direkter Zugriff auf die Hardware ist normalerweise
nicht möglich.
Es gibt wohl keinen Fall, in dem ein UNIX schrittweise für wachsende
Hardware angepaßt werden mußte, wie das bei MS-DOS und Windows
der Fall war. Daher ergeben sich auch nicht so komplizierte
Strukturen von Zusammenarbeit mehrerer Teile unterschiedlicher
Herkunft und Geschichte.
Abstraktere Funktionalität wie Kommandozeilen-Interpreter und Editor sind
aus dem Kern ausgegliedert. Der Kern selbst ist allerdings sehr groß
und beinhaltet viel Funktionalität (Prozesse, Speicher, Filesystem).
Bei vielen UNIXen ist er in sich fast völlig ungegliedert und entsprechend
unübersichtlich.
Die beiden angegebenen Schnittstellen müssen deutlich voneinander
unterschieden werden! Bibliotheks-Aufrufe erfolgen durch normale
Unterprogramm-Aufrufe, bei denen der User-Mode der Programme nicht
verlassen wird. Aufrufe des Kernels erfolgen nur innerhalb dieser
Bibliotheken - meist durch Belegen von Registern mit Werten und Auslösen
eines Software-Interrupts.
Die meisten normalen Benutzer identifizieren UNIX eher mit der
Benutzerschnittstelle, d.h. mit der Shell und den Standard-Utilities,
wie sie von der Shell aus aufgerufen werden. Diese hat zwar Parallelen
in der Bibliotheks-Schnittstelle, liegt aber auf einer ganz anderen Ebene.
Die Systemaufruf-Schnittstelle kann völlig anders geartet sein.
Die verschiedenen Standardisierungs-Versuche legen dabei immer nur
das Bibliotheks-Interface fest. Das Kernel-Interface ist hardwareabhängig
und kann von UNIX zu UNIX völlig unterschiedlich sein. Oft gibt es
aber zwischen Bibliotheksfunktionen und Systemaufrufen starke
Entsprechungen in der Funktionalität.
Als Beispiel geben wir fünf Bibliotheks-Routinen an, wie sie im
System V definiert sind:
| create(name,amode) | |
|   | Legt eine (leere) neue Datei mit dem Namen name (ggf. inklusive Pfad) an mit den Zugriffsrechten, die amode beschreibt. |
| link(f1,f2) | |
| Legt einen Hard Link an, d.h. einen "neuen Namen" f2 für die schon existierende Datei mit dem Namen f1. | |
| mount(filesys,dir,rflag) | |
| Das neue Dateisystem filesys wird an der Stelle dir in den Dateibaum eingehängt. Wenn rflag nicht 0 ist, wird das System Read-Only. | |
| kill(pid,sig) | |
| Schickt dem Prozeß Nummer pid das Signal sig (z.B. das Signal zum Abbruch). | |
| _exit(status) | |
| Beendet den aktuellen Prozeß und gibt den Wert status an den erzeugenden Prozeß zurück, der ihn mit wait oder waitpid abfragen kann. (Nicht verwechseln: Die C-Funktion exit räumt vor dem Aufruf dieser Routine noch Dateien und Speicher auf.) | |
Das Booten des Rechners erledigt (aus offensichtlichen technischen
Gründen) auch bei Linux das BIOS. Danach können mehrere
Wege beschritten werden:
Die Nummern aller Systemaufrufe findet man beispielsweise als Konstanten
definiert in der Datei include/asm/unistd.h. Ein Auszug:
Von C aus kann man einen der syscall-Makros (aus derselben
Datei) verwenden, die automatisch Inline-Assemblercode erzeugen.
Es wird eine Kernel-Routine ab der Adresse _system_call aufgerufen.
Üblicherweise ist sie in Assembler geschrieben (z.B. in der
Datei arch/i386/kernel/entry.S).
Sie rettet zunächst alle Prozessor-Register auf den Stack:
Ansonsten werden zum Schluß die Register des Prozesses restauriert,
und es wird aus der Interrupt-Routine zurückgekehrt:
2.7.2 Linux-Systemaufrufe
Unter Linux auf PCs wird für Systemaufrufe ebenfalls ein Interrupt
bemüht, nämlich der mit der Nummer 0x80=128. In Prozessorregistern
werden die Nummer der gewünschten Funktion (EAX) und die
eigentlichen Argumente übergeben. Auf diese Weise gelangt man am
bequemsten in den privilegierten Modus.
Danach überprüft sie, ob die angegebene Nummer einen gültigen
Aufruf darstellt (sonst wird abgebrochen), und ermittelt dann die
tatsächliche Adresse der aufzurufenden Routine, wobei
sie auf die Tabelle sys_call_table zugreift. Schließlich ruft sie
diese Routine auf:
Die Tabelle ist auch in entry.S definiert:
Es sind einige zusätzliche Arbeiten notwendig, wenn sich der aufrufende
Prozeß gerade im Einzelschritt-Modus befindet. Außerdem werden ggf.
an dieser Stelle "aufgeschobene" Aktionen aus zurückliegenden
Interrupts ("bottom halves") ausgeführt -- die Details würden
hier zu weit führen. Falls der Prozeß durch den Aufruf nicht mehr
weiterlaufen kann (wait, sleep) oder zufällig seine Zeit
um ist, wird hier außerdem der Scheduler aufgerufen.
Durch Ergänzung der Aufruftabelle sys_call_table, die
Anpassung von NR_syscalls und Neuübersetzung des
Kernels kann man natürlich leicht zusätzliche Aufrufe implementieren.
Das entstehende Linux ist aber nicht mehr mit anderen Versionen
kompatibel!
| 0x01234567: | Es wird sofort ein Prozessor-Reset ausgelöst (gefährlich, da nicht aufgeräumt wird). |
| 0x89abcdef: | Control-Alt-Delete wird zugelassen. Es wird nicht gebootet. |
| 0: | Control-Alt-Delete wird abgeschaltet. Es wird nicht gebootet. |
| 0xcdef0123: | Das System wird regulär heruntergefahren, indem der Startprozeß das KILL-Signal erhält. |
|   |
2.6: Windows
| Startseite |
3: Grundkonzepte 
|
|   |