Programmerstellung

Wenn man ein Programm in einer interpretierten Sprache erstellen will, dann ist prinzipielle Vorgehensweise recht einfach: Mit einem Editor (siehe Texterstellung, Dokumentation) wird der Quelltext erstellt. Mithilfe des Interpreters läßt man das erstellte Programm ausführen.

Deutlich umständlicher erscheint zumindest anfangs die Erstellung von Programmen, die letztlich in Maschinencode vorliegen und damit direkt ausführbar sein sollen:

  1. Ebenfalls mit einem Editor wird ein Quelltext erstellt (beziehungsweise bei größeren Projekten mehrere Quelltexte).

  2. Eventuell wird der Quelltext von einem Präprozessor in eine andere Datei umgesetzt, die formal wieder ein Quelltext für einen Compiler darstellt. Bei C und C++ ist dies generell der Fall. Unter Umständen wird dies aber auch bei Programmen in anderen Sprachen gemacht (und bei C/C++ gegebenenfalls zusätzlich von einem weiteren Präprozessor), beispielsweise um in Fortran eine Anbindung an SQL-Datenbanken zu vereinfachen.

  3. Der Quelltext beziehungsweise die Ausgabe des Präprozessors wird von einem Compiler in Maschinensprache und ergänzende Informationen umgesetzt.

    Bei manchen Systemen wird die Maschinensprache nicht wirklich als Binärcode erzeugt, sondern als lesbarer Text in Form der jeweiligen Assemblersprache.

    Ansonsten heißen die so aus dem Quelltext entstandenen Dateien Objektdateien, falls der Compiler tatsächlich bereits die binäre Darstellung des Programms erzeugt.

    Solche Objektdateien sind auf den meisten Systemen noch nicht ausführbar, sondern enthalten nur die Teile eines Programms, die konkret in diesem Projekt entwickelt wurden und im Quelltext abgelegt sind. Normalerweise werden aber noch weitere Programmteile benötigt, die auch von anderen Programmen genutzt werden können (Funktionen zur Ein- und Ausgabe, allgemeine mathematische Operationen und ähnliches). Diese noch fehlenden Teile liegen ebenfalls als Objektdateien in sogenannten Bibliotheken oder libraries vor.

  4. Wenn der Compiler Assemblercode erzeugt hat, dann muß dieser im nächsten Schritt von einem Assembler in eine Objektdatei übersetzt (,,assembliert``) werden.

  5. Die vom Compiler oder vom Assembler erzeugten Objektdateien und die benötigten Bibliotheken werden vom Binder oder linker zu einem ausführbaren Programm zusammengeschnürt, meistens in Form einer Datei.

    Derartige Bibliotheken wurden früher generell komplett oder teilweise (soweit vom jeweiligen Programm benötigt) zum ausführbaren Programm gebunden. Das hat natürlich den Nachteil, daß sowohl die ausführbare Datei auf dem Massenspeicher (Festplatte oder ähnliches) als auch nach dem Starten im Speicher Programmcode enthält, der vollkommen identisch in anderen Programmen enthalten ist. Um dieser Verschwendung Einhalt zu gebieten, wurde auf den modernen Systemen das Konzept von dynamischen Bibliotheken entwickelt. Dabei wird nicht mehr der komplette Programmcode aus den Bibliotheken zu einem ausführbaren Programm hinzugefügt, sondern stattdessen nur noch ein Verweis auf die zugehörige Bibliothek. In diesem Fall spricht man von dynamischen Binden (dynamic linking). Unter Unix heißen solche dynamischen Bibliotheken shared objects (oft mit der Endung .so im Dateinamen); unter Windows dynamic link libraries (meist endet der Dateiname auf .dll).

  6. Um ein solches Programm auszuführen, wird ein bestimmter Teil des Betriebssystems aktiv, der sogenannte Lader oder loader aktiv.

    Dieser lädt die Datei mit dem ausführbaren Programm in den Arbeitsspeicher, und führt dann das Relozieren (relocation) durch. Dies ist nötig, weil Compiler und Linker noch nicht wissen können, wo das Programm später bei seiner Ausführung im Speicher liegen wird. Deshalb nehmen sie jeweils für den Bereich (Segment), in dem der Maschinencode und die verschiedenen Datenbereiche untergebracht sind, irgendwelche Adressen an (beispielsweise die Adresse 0), und vermerken in den Objekt- beziehungsweise ausführbaren Dateien, an welchen Stellen solche vermuteten Adressen verwendet wurden. Beim Relozieren muß der Lader nun alle solchen vermuteten um die tatsächliche Anfangsadresse korrigieren.

    Gegebenenfalls muß außerdem im Falle von dynamischen Bibliotheken vom Lader dafür gesorgt werden, daß die entsprechenden Bibliotheken geladen werden (falls sie sich nicht schon durch ein anderes gestartetes Programm im Speicher befinden).

    Anmerkung: Dynamische Bibliotheken müssen nicht zwangsläufig beim Linken und Laden berücksichtigt werden, sondern es ist je nach Betriebssystem auch möglich, erst zur Laufzeit dynamische Bibliotheken nachzuladen. Dies kann genutzt werden, um vorhandene Programme, zu denen dem Endanwender kein Quelltext zur Verfügung steht, erweiterbar zu machen. Nach diesem Prinzip funktionieren die Plugins verschiedener Internetbrowser und Zeichenprogramme ebenso wie die Laufzeiterweiterungen von AutoCAD (ARX = AutoCAD run time extension) oder die meisten COM-Objekte alias ActiveX controls unter Windows.

Entweder innerhalb des Compilers oder als eigenes Hilfsprogramm kann als weiterer Zwischenschritt noch ein sogenannter Optimierer (optimizer) laufen. Dieser versucht, den Assembler- oder Maschinencode so umzuformen, daß er entweder schneller ausgeführt wird, oder kompakter wird (am besten natürlich beides). Gerade maschinell erzeugter Code enthält oft überflüssige Anweisungen, oder ungünstig formulierte Programmschritte. Beispielsweise muß für ein nur kurzfristig benötigtes Zwischenergebnis manchmal keine Variable angelegt werden, sondern es kann direkt in einem Register gehalten werden. Die Verwendung der Variable kann also einfach wegoptimiert werden. Ein anderes Beispiel für eine mögliche Optimierung ist die Berechnung von invarianten Ausdrücken (also Ausdrücke, deren Wert sich nicht ändert) nur einmalig vor Schleifen, anstatt mehrfach innerhalb von Schleifen.



Unterabschnitte
AnyWare@Wachtler.de