Programmbibliothek

aus Wikipedia, der freien Enzyklopädie
Wechseln zu: Navigation, Suche

Eine Programmbibliothek bezeichnet in der Programmierung eine Sammlung von Unterprogrammen, die Lösungswege für thematisch zusammengehörende Problemstellungen anbieten. Bibliotheken sind im Unterschied zu Programmen keine eigenständig lauffähigen Einheiten, sondern sie enthalten Hilfsmodule, die von Programmen angefordert werden.

In erweitertem Sinn gelten als Programmbibliotheken jedoch alle Arten von Bibliotheken, die Programmcode bereitstellen/enthalten. Insofern unterscheidet man Programmbibliotheken u. a. nach dem Typ des Programmcodes, z. B. Quelltexte, Makros, Object- oder Bytecode, Maschinencode etc. Dementsprechend werden Bibliotheken auch zu unterschiedlichen Zeitpunkten benutzt, manche nur im Rahmen der Softwareentwicklung (von Werkzeugen der Entwicklungsumgebung), andere nur zur Ausführung von Programmen, wieder andere als Mischform von beiden.

Manche proprietären Programmbibliotheken werden nicht im Quelltext veröffentlicht, da sie Firmengeheimnisse darstellen. Zum Schutz gegen Dekompilieren werden dann oft ein Obfuscator eingesetzt sowie alle Symbole (Variablen- und Sprungadressnamen) entfernt.

Quelltextbibliotheken[Bearbeiten]

Quelltextbibliotheken enthalten Sammlungen von Wertedefinitionen, Deklarationen, Funktionen, Klassen, generischen Bestandteilen, usw. (siehe auch: Programmierschnittstelle, Standard C Library, C++-Standardbibliothek). Im Unterschied zu den anderen Arten von Programmbibliotheken sind die zugehörigen Dateien noch nicht kompiliert, daher der Quelltext im Namen.

Statische und dynamische Bibliotheken sind bereits vorab kompilierter Code.

Statische Bibliotheken[Bearbeiten]

Eine statische Bibliothek wird nach dem Kompiliervorgang des (Haupt-) Programms durch einen so genannten Linker (von englisch „to link”: verknüpfen) mit dem Kompilat verbunden. Meistens setzt der Linker aus dem (Haupt-) Programm und den relevanten Komponenten aus der Bibliothek ein ausführbares (vollständiges) Programm oder auch eine andere (ausführbare) Komponente zusammen. Meist erzeugt ein Compiler zunächst aus den Programmquelltexten sogenannte Objekt-Module. Genauso sind (vorher) die Programmquelltexte der Bibliotheksroutinen zu einem Objekt-Modul kompiliert worden. Vom Linker werden diese Module im nächsten Schritt zum lauffähigen Programm zusammengefügt.

Ein optimierender Linker sucht aus den zugeordneten Objekt-Modulen (Bibliotheksdateien) nur jene Komponenten (Unterprogramme oder Daten) heraus, die vom Programm auch tatsächlich aufgerufen (referenziert) werden (und für die es im Programm keine überschreibende Implementierung gibt), und hängt sie dann an das Programm an. Die so entstehende Datei wird entsprechend größer. Einfache Linker fügen einfach das komplette Objekt-Modul (bzw. die komplette Bibliothek) hinzu und vergrößern das Programm dadurch noch mehr.

Dynamische Bibliotheken[Bearbeiten]

Aus dynamischen Bibliotheken werden Komponenten erst während der Laufzeit eines Programms über einen sogenannten Lader in den Arbeitsspeicher geladen. Das geschieht entweder durch eine explizite Anweisung durch das Programm oder implizit durch einen so genannten Laufzeit-Lader, wenn das Programm dynamisch gebunden wurde. Heutzutage geschieht dies meist mit Betriebssystem-Unterstützung, da (siehe unten) dynamische Bibliotheken gesondert behandelt werden bzgl. Swapping und Einblendung in den Programm-Adressraum. Der Lader ist somit heute meist zu weiten Teilen eine Betriebssystem-Komponente.

Dynamisch gebundene Programme müssen sich um das Laden der benötigten dynamischen Komponenten nicht selbst kümmern. Beim dynamischen Binden werden Bibliothek und Kompilat nur lose verknüpft. Statt die notwendigen Symbole (Variablen- und Sprungadressnamen) zu kopieren, wie beim statischen Binden der Fall, werden sie nur referenziert. Um das Laden der dynamischen Bibliotheken und Suchen der Symbole kümmert sich ein sogenannter Laufzeit-Binder. Das Auflösen der referenzierten Symbole geschieht entweder sofort (noch vor Start des eigentlichen Programms bzw. beim Laden aus der Bibliothek) oder faul, bei der ersten Verwendung des Symbols.

Ein typischer Anwendungsfall dafür, Bibliotheken explizit zu öffnen (die Adresse von Symbolen nach deren Namen zu suchen, um das Symbol zu verwenden), sind Plug-in-Architekturen. Ein weiteres Szenario ist eine optionale Bibliothek, die verwendet wird, falls vorhanden, deren Fehlen aber keinen Fehler darstellt.

Ein Vorteil von dynamischen Bibliotheken ist, dass Programme, die eine dynamische Bibliothek verwenden, von Fehlerbehebungen in der Bibliothek profitieren, ohne neu übersetzt werden zu müssen. Wird beispielsweise ein Fehler in der OpenSSL-Bibliothek gefunden und behoben (die entsprechende Bibliothek wurde ersetzt), so genügt ein Neustart der Programme, die diese Bibliothek verwenden, um den Fehler auch in diesen Programmen zu beheben.

Da die Dateien, in denen dynamische Bibliotheken gespeichert werden, bei der Benutzung nur gelesen und ausgeführt, aber nicht verändert werden, müssen Betriebssysteme mit virtuellem Speicher dynamische Bibliotheken nur einmal laden und können sie dann in den Adressraum aller verwendenden Prozesse einblenden. Dies ist beispielsweise bei Multitasking-Systemen vorteilhaft, wenn die Bibliotheken insgesamt sehr groß sind und von vielen Prozessen gleichzeitig verwendet werden. (Sollte die DLL interne Zustände oder gespeicherte Daten besitzen, kann dies mit Copy-On-Write abgefangen werden.)

Viele moderne Betriebssysteme laden die DLLs jedoch nicht sofort, sondern blenden sie bei Bedarf direkt von der Festplatte aus ein – sie werden wie ausgelagerte Pages behandelt. Analog werden nicht benötigte Pages, die zu einer DLL gehören und nicht verändert wurden, einfach verworfen. Bei Bedarf können sie ja aus der Festplatte (der DLL-Datei) wieder geladen werden.

Speicherungsform[Bearbeiten]

Programmbibliotheken und ihre Inhalte können, abhängig vom Betriebssystem und der Entwicklungsumgebung, in unterschiedlichen Formen und Strukturen gespeichert werden, zum Beispiel:

  • Die Bibliothek ist ein Dateiverzeichnis*), ihre Elemente/Komponenten sind einzelne Dateien*).
  • Die Bibliothek ist eine Datei*), die einzelnen Komponenten werden von einer speziellen Verwaltungssoftware identifiziert und verarbeitet. Beispiel: Eine sogenannte 'DLL' bei Microsoft.
  • Unterschiedliche Arten von Bibliotheken werden in einer gemeinsamen Datei*) verwaltet, die Entwicklungsumgebung kann dies unterscheiden/verarbeiten. Beispiel: eine 'MDB' bei MS Access

(*) verwaltet vom üblichen Dateiverwaltungssystem des Betriebssystems

In Bibliotheken können nicht nur Unterprogramme verwaltet werden: Je nach Betriebssystem werden auch Hauptprogramme aus Bibliotheken geladen, während sie in anderen Systemen z. B. EXE-Dateien sein können, die nicht in einer Bibliothek zusammengefasst sind.

Beispiele[Bearbeiten]

Programmbibliotheken stellen Komponenten in bestimmten Zusammenhängen bereit, zu denen sie bezüglich ihrer Konstruktion und ihrer Schnittstellen passen (müssen). Dementsprechend gibt es Programmbibliotheken zum Beispiel in folgenden Zusammenhängen:

Bibliotheken in verschiedenen Programmiersprachen[Bearbeiten]

Bibliotheken in Programmiersprachen enthalten Dienste, die nicht im Compiler implementiert sind, sondern in der Sprache selbst programmiert sind und mit dem Compiler zusammen oder völlig von ihm getrennt dem Programmierer zur Verfügung stehen. Im ersten Fall ist die Bibliothek meist in der Sprachbeschreibung festgelegt. Im zweiten Fall spricht man von einer externen Bibliothek.

In der Sprachbeschreibung festgelegte Bibliotheken unterscheiden sich teilweise stark im Umfang.

Sprache Teile/Pakete Header/Klassen Funktionen/Methoden/Konstruktoren
C (C89+Amendments) 1 18 142
C (C99) 1 24 482
C++ 1 32 + 18 (C89)
Java 2 (JDK 1.2) 62 1.287 ca 18.000
Java 6 202 3.850 21.881
.Net 1.1 8.643 84.112

Bibliotheken bei verschiedenen Betriebssystemen[Bearbeiten]

Windows[Bearbeiten]

Bei den Betriebssystemen Windows (und auch bei OS/2) wird eine Bibliotheksdatei, die dynamisch bindet, als DLL (für Dynamic Link Library) bezeichnet. Entsprechend haben diese Dateien meist die Dateiendung .dll. Ihr Dateiformat ist Portable Executable.

Unter Windows kann noch zwischen zwei Arten von DLLs unterschieden werden: Einsprungs-DLLs und ActiveX-DLLs. Einsprungs-DLLs enthalten Funktionen, ActiveX-DLLs enthalten Klassen.

Problematisch ist, dass die DLLs bis Windows 98 und Windows NT 4.0 nicht kontrolliert werden – jedes Programm darf sie austauschen und kann dem Betriebssystem damit möglicherweise Schaden zufügen. Windows Me, Windows 2000 und die Folgeversionen hingegen verfügen über einen Systemschutz, der auch die DLLs einbezieht.

Vorteile[Bearbeiten]

  • Außer Code können auch Daten (zum Beispiel Dialog-Ressourcen) von mehreren Prozessen gemeinsam genutzt werden.
  • DLLs werden häufig statisch verlinkt, können aber auch dynamisch verlinkt werden. Dynamisch heißt hier, dass die DLL explizit vom Programm zur Laufzeit geladen wird und die Funktionen, die sich in der DLL befinden, „per Hand“ mit dem Programm verbunden werden. Dadurch wird es möglich, durch Austauschen der DLL die Funktionalität des Programms zur Laufzeit zu verändern.
  • DLLs können unabhängig vom Hauptprogramm gewartet werden. Das heißt, Funktionen in der DLL können ohne Wissen des Programms verändert werden. Danach wird die DLL einfach ausgetauscht (die alte DLL-Datei wird überschrieben), ohne dass das Hauptprogramm verändert werden muss.
  • Da die DLL als unabhängige Datei dem Hauptprogramm beiliegen muss, können Anbieter von Programmcode besser sicherstellen, dass Programmierer, die die Funktionen ihrer DLL nutzen, dafür auch bezahlen. Die Funktionalität der DLL verschwindet so nicht (wie bei einer Library) im Code des Programms. Dieser Vorteil wird von Befürwortern freier Software als Nachteil gesehen.

Nachteile[Bearbeiten]

Änderungen in DLLs ziehen oft auch Änderungen im Programm nach sich. Dadurch kommt es leicht zu Versionskonflikten, die oft nur sehr schwer aufzuspüren sind. Eine der Grundideen der DLLs war, Programmcode zwischen mehreren Programmen zu teilen, um so Speicher zu sparen. In der Praxis ist es jedoch dazu gekommen, dass viele Programme bei der Installation DLLs in das Windows-Systemverzeichnis schreiben, die außer diesem speziellen Programm kein anderes benutzen kann. Außerdem ist die Entwicklung und insbesondere die Anbindung im Vergleich aufwändiger als zur statischen Bibliothek.

Praktisch hat dies jedoch keine Bedeutung mehr, da seit mindestens Windows 2000 die allermeisten Applikationen private Bibliotheken[1] mit ihrer Installation mitführen, also ein Versionskonflikt im Sinn eines DLL-Konflikts ausgeschlossen ist. Unter Windows haben Bibliotheken, die im Programmordner der Anwendung abgelegt sind, eine höhere Priorität als die systemweit verfügbaren.[2]

Unixartige Systeme[Bearbeiten]

Auf unixartigen Betriebssystemen (wie z.B. Linux) verwenden statische Bibliotheken den Dateisuffix .a (von „Archiv”) und können mit den UNIX-Programmen ar und nm angesehen und bearbeitet werden. Bei Systemen, die eine Paketverwaltung anbieten, befinden sich statische Bibliotheken oft zusammen mit den Header-Dateien in einem separaten Entwicklungs-Paket.

Für dynamische Bibliotheken ist die Bezeichnung shared library (englisch für „gemeinsam genutzte Bibliothek”) gebräuchlich. Sehr verbreitet ist das Executable and Linking Format (ELF), das Standard-Format unter anderem von Linux, FreeBSD und Solaris. Für diese Dateien hat sich die Endung .so (von shared object, „gemeinsam genutztes Objekt”) etabliert. In der Regel folgt dem Bibliotheksnamen noch die Versionsnummer der Binärschnittstelle (ABI version), so dass mehrere Versionen einer Bibliothek gleichzeitig installiert sein können.

Shared libraries beginnen auf Linux (mit Ausnahme einiger Low-Level-Bibliotheken) üblicherweise mit dem Präfixlib“. Die eigentliche Bibliotheksdatei enthält die volle Versionsnummer. Symbolische Verknüpfungen ermöglichen den Zugriff über den so-Namen, sowie den Zugriff ganz ohne Versionsangabe. Beispiel:

  • libfoo.so -> libfoo.so.1
  • libfoo.so.1 -> libfoo.so.1.2.3
  • libfoo.so.1.2.3

Der so-Name ist in dem Fall „libfoo.so.1“. Die Zahl nach ".so." ändert sich nur dann, wenn sich in einer neuen Version die Schnittstelle der Bibliothek ändert.

Der Laufzeit-Linker wählt anhand von Versions-Informationen im auszuführenden Programm bzw. in der zu ladenden Bibliothek eine Version mit einer kompatiblen Schnittstelle aus. Da sowohl Programme von Bibliotheken abhängen können, als auch Bibliotheken von weiteren Bibliotheken, kann eine Software indirekt von vielen Bibliotheken abhängen. Da viele Unixoide Systeme die Bibliotheken zentral für Betriebssystem und Applikation zusammen verwalten, existieren Paketverwaltungen, die Abhängigkeiten berechnen und benötigte Bibliotheken automatisch installieren können. Beispiele für derartige Paketverwaltungs-Systeme sind APT und YUM.

Von welchen Bibliotheken ein Programm direkt abhängt, lässt sich auf einigen Systemen mit Hilfe des UNIX-Programms ldd herausfinden.

Bei Software, die in der Binärversion vertrieben werden soll, beispielsweise kommerzielle Closed-Source-Software oder portable/distributionunabhängige Software, muss gewährleistet werden, dass alle benötigten Bibliotheken vorliegen. Das Anwendersystem, auf dem diese Software verwendet werden soll, muss dann als kompatible Binärplattform agieren können. Dies kann folgendermaßen geschehen:

  • Angabe einer oder mehrerer kompatibler Betriebssystem-Distributionen, zum Beispiel: „Lauffähig unter Debian 5.0 (Lenny)”.
  • Verwendung eines distributionsspezifischen Paketes, so dass benötigte Bibliotheken vom Paketmanager nachgeladen werden. Nachteil ist der enorme Aufwand der Bereitstellung eines Paketes für die vielen existierenden Distributionen.[3][4]
  • Mitliefern aller dynamischen Bibliotheken. Diese werden, zusammen mit dem Programm, Dokumentation, etc. in ein separates Verzeichnis kopiert, um nicht mit Versionen, die von der Paketverwaltung systemweit installiert wurden, zu kollidieren. Anschließendes Anpassen des Bibliothekensuchpfads für das Programm mit LD_LIBRARY_PATH über ein Skript oder händisch.[5]
  • Verzicht auf dynamische Bibliotheken und Statisches Linken, was aber technisch schwierig ist[6][7] oder durch Lizenzkonflikte verhindert werden kann. Außerdem verliert man damit Distribution-basierenden Updatesupport.[8]
  • Über private Bibliotheken[1] die über einen relativen Bibliothekspfad eingebunden werden. Hierzu muss die Applikation mit der Linker-Option $ORIGIN erzeugt werden.[5]

Objekt- und komponentenorienterte Ansätze können hier realisiert werden, indem in einer Funktion das entsprechende Objekt oder die Komponente instantiiert und zurückgegeben werden.

Vorteile[Bearbeiten]

  • Gemeinsame Nutzung von Ressourcen
  • Vermeidung von redundanten privaten Libraries
  • Eine enorme Anzahl von freien und mitgelieferten Bibliotheken stehen zur Verfügung
  • Bibliotheken können unabhängig vom Hauptprogramm ausgebessert werden[8] und Programmupdates werden kleiner
  • Optimierungen an einer Stelle können das ganze System beschleunigen
  • Können schnell veränderliche Systemaufrufe verbergen
  • Abstrahieren von low-level Problemen

Nachteile[Bearbeiten]

  • Probleme in essentiellen Bibliotheken machen Systeme unbenutzbar. Durch entsprechende Skripte der Paketverwaltung ist bei modernen Distributionen jedoch meist sichergestellt, dass dies nicht passieren kann.
  • Bei inkompatiblen Veränderungen der Binärschnittstelle müssen alle abhängigen Programme neu kompiliert werden. Durch Versionsnummern und parallele Verwendung von Paketen reduziert sich das Problem der Binärkompatiblität, wird jedoch nicht gelöst.[9]
  • Bei inkompatiblen Veränderungen der Programmierschnittstelle müssen alle abhängigen Programme entsprechend angepasst werden.
  • Die Erstellung portabler oder distributionsübergreifender Programme wird schwieriger.[10][11]

Java[Bearbeiten]

Java ist eine eigene Plattform und verwendet ein Bibliothekskonzept, welches nicht an das Betriebssystem gebunden ist. Im Grunde wird nicht zwischen Programm und Bibliothek unterschieden. Alle Klassen liegen kompiliert in Form von .class-Dateien vor und werden bei Bedarf geladen. In der Regel sind Bibliotheken, wenn sie aus mehreren Klassen bestehen, in einem Java-Archiv zusammengefasst. Die Java-API selbst liegt ebenfalls in Form von Java-Archiven vor.

Da die einzelnen Klassen einer Bibliothek erst zur Laufzeit geladen werden, kann es bei der ersten Verwendung zu Verzögerungen kommen, bis die Klasse geladen und initialisiert wurde. In Java für Echtzeitsysteme, aber auch in Java SE kann der Klassenlader durch entsprechende Methodenaufrufe bestimmte oder alle notwendigen Bibliotheken beim Starten laden und sie auch nicht mehr entladen, so dass bei der Benutzung keine unerwartete Verzögerung entsteht.

Bibliotheken unter z/OS[Bearbeiten]

Bei z/OS werden alle Partitioned Data Sets (PDS) als Bibliotheken bezeichnet. Die einzelnen Elemente dieser Bibliotheken werden allgemein Member oder, wenn es sich um ausführbaren Programmcode handelt, auch Module genannt.

Statische Module müssen in einer Bibliothek liegen, die dem Linkage Editor als SYSLIB bekanntgegeben wird, dynamische Module werden zur Laufzeit entweder aus der STEPLIB oder aus der JOBLIB geladen und wenn sie hier nicht gefunden werden, aus einer Bibliothek in der LINKLIST.

Bibliotheken unter Amiga OS[Bearbeiten]

Beim AmigaOS werden alle Bibliotheken als shared Library verwendet. Sie werden also zur Laufzeit vom Programm beim System angefordert, das dann die Basisadresse der Bibliothek im Speicher (bis OS3.9) oder die entsprechende Schnittstelle (ab OS4.0) zur Verfügung stellt. Das Programm verwendet dann relative Adressen, um über eine Sprungtabelle vor der Basisadresse an die eigentlichen Funktionen (hinter der Basisadresse) zu kommen. Diese Funktionen sind eintrittsinvariant (reentrant).

Selbst bei Änderungen der Bibliothek sind die bestehenden Einträge der Sprungtabellen immer gleich. Es kommen ggf. lediglich neue Einträge hinzu am Ende der Tabellen. Somit ist eine Abwärtskompatibilität gegeben.

Als Besonderheit kann bei AmigaOS beim Öffnen einer Library eine Mindest-Versionsnummer angegeben und so sichergestellt werden, dass eine gewünschte Funktionalität auch wirklich verfügbar ist. Wird diese Version nicht vorgefunden, kann das aufrufende Programm sicher auf eine einfachere Funktionalität, wie sie in der älteren Library-Version bereitgestellt wird, zurückschalten.

Bibliotheksdateien tragen die Endung .library und befinden sich meist im Verzeichnis LIBS: der Systempartition. Das Betriebssystem überprüft bei der Suche nach einer Bibliothek auch das Programmverzeichnis des anfragenden Programms.

Siehe auch[Bearbeiten]

Weblinks[Bearbeiten]

Einzelnachweise[Bearbeiten]

  1. a b Rick Anderson: The End of DLL Hell. microsoft.com. 11. Januar 2000. Archiviert vom Original am 5. Juni 2001. Abgerufen am 15. Januar 2012: „Private DLLs are DLLs that are installed with a specific application and used only by that application.
  2. Arnaud Desitter: Using static and shared libraries across platforms; Row 9: Library Path (englisch) ArnaudRecipes. 20. Juli 2011. Archiviert vom Original am 20. Juli 2011. Abgerufen am 26. Januar 2012: „win32 runtime library path: . and then PATH
  3. Eric Brown: LSB 4.0 certifications aim to heal Linux fragmentation (englisch) linuxfordevices.com. 8. Dezember 2010. Archiviert vom Original am 6. September 2012. Abgerufen am 16. November 2011: „[...] LSB helps to reduce fragmentation, it does not eliminate it. "The issue of packaging and broader dependencies is still a big one (for me) at least," writes Kerner. "The same RPM that I get for Fedora won't work on Ubuntu, and Ubuntu DEB packages won't work on SUSE etc etc."[...]
  4. Eskild Hustvedt: Playing well with distros (englisch) Linux Game Publishing. 24. November 2009. Abgerufen am 15. Januar 2012.
  5. a b Eskild Hustvedt: Our new way to meet the LGPL (englisch) 8. Februar 2009. Abgerufen am 9. März 2011: „You can use a special keyword $ORIGIN to say ‘relative to the actual location of the executable’. Suddenly we found we could use -rpath $ORIGIN/lib and it worked. The game was loading the correct libraries, and so was stable and portable, but was also now completely in the spirit of the LGPL as well as the letter!
  6. Evan Jones: Portable Linux Binaries (englisch) 13. Februar 2008. Abgerufen am 10. Januar 2012: „Linux is not well known for its binary portability. Libraries vary from system to system, and the kernel interfaces have a tendency to change. [...] Recently, I needed to build a binary on one system, and run it on another. It only used standard C library functions, so I expected it to be easy. It was not. [...]
  7. Christoph Baus: Yet another Unix nightmare: statically linking libstdc++ (englisch) 31. Mai 2005. Archiviert vom Original am 10. Februar 2010. Abgerufen am 15. Januar 2012.
  8. a b Ulrich Drepper: Static Linking Considered Harmful (englisch) redhat.com. Archiviert vom Original am 27. Mai 2010. Abgerufen am 13. Januar 2012: „There are still too many people out there who think (or even insist) that static linking has benefits. This has never been the case and never will be the case. [...]
  9. Mike Hearn: Random Collection of Current Linux Problems Binary Portability (englisch) Autopackage.org. 2006. Archiviert vom Original am 18. Mai 2009. Abgerufen am 23. Januar 2012: „This page was prepared for the OSDL meeting in December 2005. It describes many of the problems inherent to Linux we've encountered whilst distributing complex software in binary form to end users. It also offers a few suggestions for improvements.
  10. Troy Hepfner: Linux Game Development Part 2 - Distributable Binaries (englisch) gamedev.net. 1. Oktober 2007. Archiviert vom Original am 13. Oktober 2007. Abgerufen am 19. Dezember 2011: „Creating an executable that works on almost all Linux distributions is a challenge. There are a number of factors that contribute to the problem [...]
  11. Simon Peter: AppImageKit Documentation 1.0 (englisch, pdf; 38 kB) PortableLinuxApps.org. S. 2-3. 2010. Abgerufen am 29. Juli 2011: „Not easy to move an app from one machine to another