SelfLinux Programmierung Bibliotheken Abschnitt 1 | SelfLinux-0.13.0

| Bibliotheken | Autor Formatierung GFDL

#### 1 Einleitung

Dieses Kapitel erklärt Sinn, Zweck und Handhabung von Systembibliotheken für Anwender. Softwareentwickler können diesen Artikel als Einführung in die Materie verwenden, werden jedoch keine Details über Bibliotheken aus Programmierer- oder Entwicklersicht finden.

Benutzer und Anwender von Linuxsystemen finden hier einen verständnisfördernden Überblick. Systemadministratoren gewinnen einen Einblick in die Zusammenhänge unter den für sie interessanten Gesichtspunkten, werden jedoch weitere Lektüre benötigen, um Einzelheiten zu den sie interessierenden Punkten zu erfahren.

#### 2 Komplexe Systeme

Ein Komplexes System besteht in der Regel aus mehreren, kleineren Systemkomponenten oder Subsystemen. Diese können wiederum aus kleineren Komponenten bestehen. Nur durch das Zusammenspiel vieler kleiner Komponenten lassen sich große Systeme beherrschen. Häufig ergibt es sich, dass einfach kleine Komponenten von mehreren komplexeren Komponenten benutzt werden.

Sieht man sich zum Beispiel verschiedene X-Anwendungen an, so fallen viele Ähnlichkeiten unter den Anwendungen auf. So kann beispielsweise jede Anwendung durch Maus oder Tastatur gesteuert werden, Anwendungen haben Fenster mit Rahmen und Funktionssymbolen und reagieren auf verschiedene Ereignisse (zum Beispiel Mausklicks).

Hier erkennt man, dass selbst grundverschiedene Anwendungen häufig in Details ähnliche Funktionalitäten haben.

#### 2.1 Systemkomponenten

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.

#### 2.1.1 Systemkomponenten von oben gesehen

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.

#### 2.1.2 Systemkomponenten von unten gesehen

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.