Ein Linux- beziehungsweise Unix-System besteht aus sehr vielen
Komponenten. Dabei lässt sich ein solches System auf mehrere Arten
in Komponenten zerlegen. Die Art der Zerlegung reflektiert eine
bestimmte Sicht auf das System. Eine Zerlegung könnte man aus
Sicht der Hardware vornehmen. Ein Linuxsystem besteht nach dieser
Sicht aus Monitor, Tastatur und Rechner. Der Rechner besteht
wiederum aus Einsteckkarten, Festplatten und einem Netzteil. Eine
Einsteckkarte besteht aus einer Leiterplatte und etlichen
Schaltkreisen.
Je nach Ebene, auf der man sich bei einer Systemanalyse befindet,
sind mehr oder weniger Details von Interesse. Von ganz oben
gesehen besteht im Beispiel das System aus Rechner und Monitor,
die über ein Kabel verbunden sind. Dass dies überhaupt nur
funktioniert, weil eine Grafikkarte in den Rechner eingebaut ist,
die Speicherschaltkreise, Oszillatoren und viele andere Bauteile
enthält, und wie nun eigentlich ein Speicherschaltkreis
funktioniert, ist auf dieser Ebene unerheblich. Jedoch ist es
von elementarer Bedeutung, dass eine Grafikkarte
Speicherschaltkreise hat, wenn man sich auf einer Ebene befindet,
die den Aufbau von Grafikkarten zeigt.
Das Interessante an den Ebenen des Systems ist, das jede
Systemkomponente auch als System angesehen werden kann (daher
auch der Name Subsytem). Für Grafikkartenhersteller ist eine
VGA-Karte ein vollständiges System. Für einen Linuxanwender ist
ein Computersystem (Rechner, Monitor usw.) ein vollständiges
System. Diese Struktur lässt sich jedoch auch nach oben hin
weiterführen: Für einen Mitarbeiter einer IT Abteilung ist erst ein
Netzwerk mit vielen daran angeschlossenen Computersystemen ein
vollständiges System; einzelne Server (also Computersysteme) sind
für ihn eine Komponente. Auch hier gilt der
Komponentensichtbereich: Sicherlich ist einem IT Mitarbeiter
klar, dass ein Server üblicherweise über Speicherschaltkreise
verfügt, aber es interessiert ihn überhaupt nicht.
Dieses Strukturierungsbeispiel reflektiert die Hardware-Sicht.
Neben vielen weiteren kann man eine solche Strukturierung auch
aus Software-Sicht vornehmen. Hier spricht man von Anwendungen
als Systemen, die aus Unteranwendungen oder Programmen bestehen.
Diese wiederum bestehen vielleicht aus Funktionen. Anwendungen
können auch andere Anwendungen verwenden.
Hier findet man eine weitere interessante Eigenschaft: Die
einzelnen Sichten untereinander interessieren sich in der Regel
nicht für andere Sichten. Betrachtet man eine Anwendung, so ist
sicherlich klar, dass diese irgendwo wieder in
Speicherschaltkreisen lebt, aber das interessiert in dieser
Sichtweise überhaupt nicht. Das gibt die Möglichkeit, sich auf
die essentiellen Aspekte von Systemebenen zu konzentrieren.
Dadurch kann man auch komplexe Systeme verstehen. Eine typische
X-Anwendung verwendet möglicherweise Millionen von
Maschineninstruktionen, die in Millionen von Speicherstellen von
Speicherschaltkreisen gespeichert sind. Das ist viel zu komplex,
um von Menschen überblickt werden zu können. Aber jede Ebene für
sich betrachtet kann verstanden und beherrscht werden.
Auch bei der Softwaresicht lässt sich die Strukturierung nach oben
hin erweitern: Man spricht beispielsweise von verteilten
Systemen, deren Komponenten verschiedene Anwendungen (zum
Beispiel Datenbanken und Webserver) sind, die auf verschiedenen
Servern laufen (wobei das mit den Servern jedoch schon wieder
uninteressant ist).
Im Folgenden wird die Softwaresicht etwas detaillierter beschrieben.
Betrachtet man eine Anwendung, kann man Komponenten finden, aus
denen die Anwendung besteht. Ein KDE Programm
beispielsweise besteht aus Fenstern, die Menüs besitzen können
und einen Rahmen haben. Ein Menü besteht aus Einträgen und
Ereignisverarbeitungsfunktionen, die dafür sorgen, dass auch etwas
passiert, wenn man eine Menüfunktion auswählt. Ein Rahmen besteht
nun aus einer Titelleiste, einigen Knöpfen und vielleicht einer
Laufleiste.
Betrachtet man das System genauer, stellt man schnell fest, dass
etliche dieser Komponenten bei allen KDE Anwendungen
sehr ähnlich sind. So hat fast jedes KDE Programm ein
Fenster mit einem Titel, und wenn zwei Fenster Laufleisten haben,
sehen diese sehr ähnlich aus. Diese Eigenschaften sind also bei allen
KDE Programmen ähnlich.
Geht man eine weitere Ebene tiefer, stellt man fest, dass man
viele Eigenschaften findet, die bei den eben beschriebenen
Komponenten ähnlich sind. Zum Beispiel besteht eine Titelleiste
und eine Laufleiste aus Bildpunkten, die über Grafikkarten
dargestellt werden. Das hört sich zunächst banal an, aber wenn
man bedenkt, dass diese Grafikkarten in verschiedenen Systemen
eingebaut sein können und über Netzwerke hinweg dargestellt
werden können, kommt man zu der Erkenntnis, dass es gar nicht so
banal ist, einen Bildpunkt in die richtige Grafikkarte zu
schreiben. Auf der Ebene der Bildpunkte ist dies jedoch eine
interessante Frage: Es muss etwas geben, das Informationen und
Netzwerkverbindungen verwaltet. Dabei spielen
Zugriffsbeschränkungen und viele andere Fragen eine Rolle.
Neben grafischen Elementen gibt es noch weitere ähnliche
Eigenschaften. Viele KDE Programme können zum Beispiel
verschiedene Arten von Konfigurationsdateien verwenden. Diese
Dateien müssen irgendwie geöffnet und eingelesen werden. Die
Daten müssen dann weiterverarbeitet werden, es müssen Fehler
erkannt werden, zum Beispiel wenn das Dateiformat ungültig ist.
Hier gibt es Komponenten, welche die Konfigurationsoptionen aus
Dateien einlesen und verarbeiten.
Diese Komponenten verwenden Dateioperationen. Auch viele andere
Komponenten verwenden Dateioperationen, darunter
selbstverständlich auch viele Anwendungen oder Komponenten, die
überhaupt nichts mit dem KDE zu tun haben.
Eine Dateioperation wiederum verwendet Systemfunktionen, um auf
die Datenbytes einer Datei nach dem Öffnen zugreifen zu können.
Diese Systemfunktionen verwenden Systemtreiber, um die Daten
beispielsweise von Festplatten lesen zu können. Die Systemtreiber
kann man intern auch als System betrachten. Diese Sicht wird
vermutlich von einem Festplattentreiberprogrammierer verwendet
werden. Für ihn verwendet sein System (der Treiber) viele
Funktionen, darunter auch andere Systemfunktionen.
Die Treiber, die Teil des Systemkerns (Kernels) sind, verwenden
Maschineninstruktionen und Maschinenkommunikationsmittel. Dies
ist die unterste Softwareebene, die es bei Personalcomputern
(PCs) gibt. Der zentrale Schaltkreis, das Herz eines PCs, ist die
CPU (Central Processing Unit). Sie kann ganz einfache Maschinenbefehle
ausführen und über Steuerleitungen mit anderen Geräten kommunizieren.
|
Nun stellt sich möglicherweise dennoch die Frage: Wenn eine
CPU mit einer Festplatte kommunizieren kann, warum dann diese
ganzen Systemebenen? Warum verwendet man Treiber, wenn eine
Anwendung doch auch direkt mit der Festplatte kommunizieren kann?
Mindestens eine Antwort sollte nach kurzer Überlegung gefunden
werden können. Natürlich würde die Anwendung viel zu komplex.
Anstatt einer Komponente zu sagen "lade mir mal den Parameter x
aus der Datei y" müsste sie Hunderte von Maschineninstruktionen
verwenden, um allein die Datei zu finden. Weiterhin müsste sie
alle Geräte direkt unterstützen; kommt eine neue Festplatte auf
den Markt, müssten unter Umständen sämtliche Anwendungen angepasst
werden!
Alle Festplattentreiber haben eines gemeinsam: Im
Wesentlichen können sie Datenpakete an bestimmte Stellen
schreiben oder von diesen lesen. Diese Stellen haben nichts mit
Unterverzeichnissen oder Dateien zu tun: Es sind einfach nur
Nummern. Ein Datenpaket hat auch eine feste Größe. Auf dieser
Ebene ist es sehr umständlich, Dateien zu verwalten: Man muss sich
um Listen mit den Speicherstellennummern kümmern, alle diese
einlesen und so weiter. Wie man eine solche Speicherstelle liest,
ist zudem noch für verschiedene Geräte (wie IDE oder SCSI
Festplatten) unterschiedlich. Daher fasst man diese Funktionalität
in Treibern zusammen.
Hat man solche Treiber, die sich um die gesamte
Gerätekommunikation kümmern, kann man darauf aufsetzen und ein
Dateisystem bauen. Das sorgt dafür, dass man sich nicht mehr um
Tausende Details kümmern muss, sondern einfach beliebig lange
Daten (anstatt Datenpakete fester, jedoch geräteabhängiger Größe)
über Namen (anstatt Nummern) lesen und schreiben kann.
Man kann nun auch Dateisysteme bauen, die nicht direkt auf
Gerätetreiber zugreifen, sondern die Lese- und Schreibanfragen
über Netzwerkgerätetreiber an andere Server weiterreichen und
hier über Gerätetreiber ausgeführt werden. Damit kann man auch
auf Dateien zugreifen, die im Netzwerk liegen.
All diese Funktionen spielen sich im Kernel ab.
Anwendungsprogrammierer interessieren sich nicht dafür, wie ein
Kernel eine Datei öffnet. Sie interessieren sich nur dafür, wie
sie diese Kernelfunktion aufrufen können. Hierzu bietet der Kernel
Schnittstellenfunktionen an, um zum Beispiel eine Datei mit einem bestimmten
Namen zu öffnen und Daten aus ihr zu lesen.
Diese Programmierschnittstelle ist jedoch immer noch
unkomfortabel. Zum Beispiel ist es sehr langsam, eine solche
Funktion aufzurufen. Möchte man 100 Zeichen einzeln aus einer
Datei lesen, so muss man sinnvollerweise sofort 100 Zeichen lesen,
irgendwo zwischenspeichern und dann erst verarbeiten. Die
Kernelschnittstelle umfasst auch nur relativ wenige, ziemlich
"dumme" Funktionen.
Da alle Programmierer diese Probleme haben, gibt es fertige
Funktionen, die diese lösen. Diese Funktionen sind bequemer zu
verwenden und bieten weitere Vorteile.
Nun könnte natürlich jedes Programm diese Funktionen als Kopie
mitbringen. Die Nachteile hierfür sind wieder klar: Bei
Änderungen müsste man alle Programme anpassen, die Programme
würden sehr groß und kompliziert werden und so weiter.
Also fasst man diese grundlegenden Funktionen mit vielen anderen,
die indirekten, aber komfortableren Zugriff auf Kernelfunktionen
bieten, zusammen. Solche Funktions-Zusammenfassungen nennt man
Bibliotheken. Die Zusammenfassungen von den hier genannten
Basisfunktionen nennt man C-Bibliothek. "C" ist eine
hardwarenahe Programmiersprache, in welcher der Unix-Kernel und die
C-Bibliothek geschrieben sind. Dieser Name deutet an, dass hier
eine C-Programmierschnittstelle bereitgestellt wird, also können
in C geschriebene Programme einfach darauf zugreifen. Da
C-Programme ja auch "nur" in Maschineninstruktionen übersetzt
werden (eine CPU kann ja nur diese Maschineninstruktionen
ausführen), kann auch jede andere Sprache, die in solche
Maschineninstruktionen übersetzt wird, hierauf zugreifen. In der
Praxis sind das demzufolge eigentlich alle Sprachen (da eine CPU
ja nur eine Maschinensprache kennt!). Eigentlich zeigt, dass es
auch Ausnahmen gibt, wie zum Beispiel Java. Java läuft auf einer
sogenannten virtuellen Maschine, die eine eigene Maschinensprache
hat. Natürlich wird diese indirekt auch wieder "nur" auf
Maschineninstruktionen abgebildet, doch eine genauere
Betrachtung würde den Rahmen dieses Dokumentes sprengen.
Diese C-Bibliothek ist so grundsätzlich, dass in der Praxis so
gut wie jedes Programm direkt oder indirekt auf sie zugreift.
In unserem Beispiel sahen wir aber, dass eine Anwendung auf noch
speziellere und komfortablere Funktionen zurückgreift. Wenn man
beispielsweise Konfigurationsdateien verarbeiten möchte, kann man
natürlich in jede Anwendung die vielen hundert erforderlichen
Schritte einprogrammieren. Aber es gibt ja eine bessere Lösung:
Man baut eine Bibliothek dafür! Diese Bibliothek liest intern
einzelne Zeichen aus Dateien und verarbeitet diese. Verwendet
man Funktionen dieser Bibliothek, muss man sich gar nicht mehr
darum kümmern, dass man mehrere Funktionen benötigt, um die Daten
zu lesen. Man ruft einfach eine Funktion auf, die das Weitere
erledigt.
|
|