Was bedeuten "statisch verknüpft" und "dynamisch verknüpft"?

Antworten:

444

Es gibt (in den meisten Fällen Diskontierung des interpretierten Codes) zwei Schritte, um vom Quellcode (was Sie schreiben) zum ausführbaren Code (was Sie ausführen) zu gelangen.

Die erste ist die Kompilierung, bei der Quellcode in Objektmodule umgewandelt wird.

Die zweite Verknüpfung kombiniert Objektmodule zu einer ausführbaren Datei.

Die Unterscheidung wird unter anderem dadurch getroffen, dass Bibliotheken von Drittanbietern in Ihre ausführbare Datei aufgenommen werden können, ohne dass deren Quellcode angezeigt wird (z. B. Bibliotheken für Datenbankzugriff, Netzwerkkommunikation und grafische Benutzeroberflächen), oder dass Code in verschiedenen Sprachen kompiliert werden muss (z. C und Assembler-Code zum Beispiel) und dann alle miteinander verknüpfen.

Wenn Sie statisch eine Datei einer ausführbaren Datei verknüpfen, wird der Inhalt dieser Datei zum Zeitpunkt der Verknüpfung eingeschlossen. Mit anderen Worten, der Inhalt der Datei wird physisch in die ausführbare Datei eingefügt, die Sie ausführen werden.

Wenn Sie dynamisch verknüpfen , ist ein Zeiger auf die Datei, in der verknüpft wird (z. B. der Dateiname der Datei), in der ausführbaren Datei enthalten, und der Inhalt dieser Datei ist zum Zeitpunkt der Verknüpfung nicht enthalten. Es ist nur , wenn Sie später laufen die ausführbare Datei , dass diese dynamisch verknüpften Dateien in gekauft und sie sind nur in die im Speicher befindlichen Kopie der ausführbaren Datei, nicht die auf der Festplatte gekauft.

Es ist im Grunde eine Methode der verzögerten Verknüpfung. Es gibt noch mehr latente Methode (genannt auf einigen Systemen der späten Bindung) , die in der dynamisch verknüpften Datei bringt nicht , bis Sie tatsächlich versuchen , eine Funktion innerhalb es zu nennen.

Statisch verknüpfte Dateien sind zum Zeitpunkt der Verknüpfung für die ausführbare Datei "gesperrt", sodass sie sich nie ändern. Eine dynamisch verknüpfte Datei, auf die eine ausführbare Datei verweist, kann sich ändern, indem die Datei auf der Festplatte ersetzt wird.

Dies ermöglicht Aktualisierungen der Funktionalität, ohne dass der Code erneut verknüpft werden muss. Der Loader verbindet sich jedes Mal neu, wenn Sie ihn ausführen.

Dies ist sowohl gut als auch schlecht - einerseits ermöglicht es einfachere Updates und Fehlerbehebungen, andererseits kann es dazu führen, dass Programme nicht mehr funktionieren, wenn die Updates nicht kompatibel sind - dies ist manchmal für die gefürchtete "DLL-Hölle" verantwortlich, die manche Leute haben Erwähnen Sie, dass Anwendungen beschädigt werden können, wenn Sie eine dynamisch verknüpfte Bibliothek durch eine nicht kompatible Bibliothek ersetzen (Entwickler, die dies tun, sollten übrigens damit rechnen, dass sie streng gejagt und bestraft werden).


Betrachten wir als Beispiel den Fall eines Benutzers, der seine main.cDatei für statische und dynamische Verknüpfungen kompiliert .

Phase     Static                    Dynamic
--------  ----------------------    ------------------------
          +---------+               +---------+
          | main.c  |               | main.c  |
          +---------+               +---------+
Compile........|.........................|...................
          +---------+ +---------+   +---------+ +--------+
          | main.o  | | crtlib  |   | main.o  | | crtimp |
          +---------+ +---------+   +---------+ +--------+
Link...........|..........|..............|...........|.......
               |          |              +-----------+
               |          |              |
          +---------+     |         +---------+ +--------+
          |  main   |-----+         |  main   | | crtdll |
          +---------+               +---------+ +--------+
Load/Run.......|.........................|..........|........
          +---------+               +---------+     |
          | main in |               | main in |-----+
          | memory  |               | memory  |
          +---------+               +---------+

Sie können im statischen Fall sehen, dass das Hauptprogramm und die C-Laufzeitbibliothek zur Verknüpfungszeit (von den Entwicklern) miteinander verknüpft sind. Da der Benutzer die ausführbare Datei normalerweise nicht erneut verknüpfen kann, bleibt er beim Verhalten der Bibliothek hängen.

Im dynamischen Fall ist das Hauptprogramm mit der C-Laufzeitimportbibliothek verknüpft (etwas, das deklariert, was sich in der dynamischen Bibliothek befindet, es aber nicht definiert ). Dadurch kann der Linker verknüpfen, obwohl der eigentliche Code fehlt.

Zur Laufzeit führt der Betriebssystemlader eine späte Verknüpfung des Hauptprogramms mit der C-Laufzeit-DLL (Dynamic Link Library oder Shared Library oder eine andere Nomenklatur) durch.

Der Besitzer der C-Laufzeit kann jederzeit eine neue DLL einfügen, um Updates oder Fehlerbehebungen bereitzustellen. Wie bereits erwähnt, hat dies sowohl Vor- als auch Nachteile.

paxdiablo
quelle
11
Bitte korrigieren Sie mich, wenn ich falsch liege. Unter Windows enthält die Software jedoch in der Regel eigene Bibliotheken, auch wenn diese dynamisch verknüpft sind. Auf vielen Linux-Systemen mit einem Paketmanager werden viele dynamisch verknüpfte Bibliotheken ("gemeinsam genutzte Objekte") tatsächlich von der Software gemeinsam genutzt.
Paul Fisher
6
@PaulF: Dinge wie die allgemeinen Windows-Steuerelemente, DirectX, .NET usw. werden häufig mit den Anwendungen geliefert, während Sie unter Linux dazu neigen, apt oder yum oder ähnliches zum Verwalten von Abhängigkeiten zu verwenden - also haben Sie in diesem Sinne Recht . Gewinnen Sie Apps, die ihren eigenen Code als DLLs versenden, und teilen Sie diese normalerweise nicht.
Paxdiablo
31
Es gibt einen besonderen Platz im neunten Kreis der Hölle für diejenigen, die ihre DLLs aktualisieren und die Abwärtskompatibilität brechen. Ja, wenn Schnittstellen verschwinden oder geändert werden, fällt die dynamische Verknüpfung in einen Haufen. Deshalb sollte es nicht gemacht werden. Fügen Sie auf jeden Fall eine function2 () zu Ihrer DLL hinzu, aber ändern Sie function () nicht, wenn Benutzer sie verwenden. Der beste Weg, dies zu handhaben, besteht darin, function () so neu zu codieren, dass es function2 () aufruft, aber die Signatur von function () nicht ändert.
Paxdiablo
1
@ Paul Fisher, ich weiß, dass dies spät ist, aber ... die mit einer Windows-DLL gelieferte Bibliothek ist nicht die vollständige Bibliothek, sondern nur eine Reihe von Stubs, die dem Linker mitteilen, was die DLL enthält. Der Linker kann dann die Informationen zum Laden der DLL automatisch in die EXE-Datei einfügen, und die Symbole werden nicht als undefiniert angezeigt.
Mark Ransom
1
@ Santropedro, Sie sind in jeder Hinsicht korrekt, was die Bedeutung der lib-, import- und DLL-Namen betrifft. Das Suffix ist nur Konvention, lesen Sie also nicht zu viel hinein (zum Beispiel kann die DLL eine .dlloder eine .soErweiterung haben) - stellen Sie sich die Antwort als Erklärung der Konzepte vor, anstatt eine genaue Beschreibung zu sein. Und laut Text ist dies ein Beispiel, das statische und dynamische Verknüpfungen nur für die C-Laufzeitdateien zeigt. Ja, das ist es, was `crt in allen angibt.
Paxdiablo
221

Ich denke, eine gute Antwort auf diese Frage sollte erklären, was Verknüpfung ist .

Wenn Sie beispielsweise C-Code kompilieren, wird dieser in die Maschinensprache übersetzt. Nur eine Folge von Bytes, die beim Ausführen bewirkt, dass der Prozessor addiert, subtrahiert, vergleicht, "gehe", Speicher liest, Speicher schreibt, so etwas. Dieses Zeug wird in Objektdateien (.o) gespeichert.

Vor langer Zeit haben Informatiker dieses "Unterprogramm" erfunden. Führen Sie diesen Codeblock aus und geben Sie ihn hier zurück. Es dauerte nicht lange, bis ihnen klar wurde, dass die nützlichsten Unterprogramme an einem bestimmten Ort gespeichert und von jedem Programm verwendet werden konnten, das sie benötigte.

Früher mussten Programmierer die Speicheradresse eingeben, unter der sich diese Unterprogramme befanden. So etwas wie CALL 0x5A62. Dies war mühsam und problematisch, falls diese Speicheradressen jemals geändert werden müssen.

Der Prozess wurde also automatisiert. Sie schreiben ein Programm, das aufruft printf(), und der Compiler kennt die Speicheradresse von nicht printf. Der Compiler schreibt also nur CALL 0x0000und fügt der Objektdatei eine Notiz hinzu, die besagt, dass "diese 0x0000 durch den Speicherort von ersetzen muss printf ".

Statische Verknüpfung bedeutet, dass das Linkerprogramm (das GNU-Programm heißt ld ) printfden Maschinencode direkt zu Ihrer ausführbaren Datei hinzufügt und 0x0000 in die Adresse von ändert printf. Dies geschieht, wenn Ihre ausführbare Datei erstellt wird.

Dynamische Verknüpfung bedeutet, dass der obige Schritt nicht ausgeführt wird. Die ausführbare Datei enthält weiterhin den Hinweis "0x000 muss durch den Speicherort von printf ersetzt werden". Der Loader des Betriebssystems muss bei jedem Programmstart den printf-Code finden, in den Speicher laden und die CALL-Adresse korrigieren .

Es ist üblich, dass Programme einige Funktionen aufrufen, die statisch verknüpft werden (Standardbibliotheksfunktionen, wie sie printfnormalerweise statisch verknüpft sind), und andere Funktionen, die dynamisch verknüpft werden. Die statischen "werden Teil" der ausführbaren Datei und die dynamischen "machen mit", wenn die ausführbare Datei ausgeführt wird.

Beide Methoden haben Vor- und Nachteile, und es gibt Unterschiede zwischen den Betriebssystemen. Aber da du nicht gefragt hast, werde ich das hier beenden.

Artelius
quelle
4
Ich habe es auch getan, aber ich kann nur 1 Antwort auswählen.
UnkwnTech
1
Artelius, ich schaue mir Ihre Erklärung, wie diese verrückten Dinge auf niedriger Ebene funktionieren, eingehend an. Bitte antworten Sie mit den Büchern, die wir lesen müssen, um gründliche Kenntnisse über die oben genannten Dinge zu erhalten. Danke.
Mahesh
1
Entschuldigung, ich kann keine Bücher vorschlagen. Sie sollten zuerst die Assemblersprache lernen. Dann kann Wikipedia einen anständigen Überblick über solche Themen geben. Vielleicht möchten Sie sich die GNU- ldDokumentation ansehen .
Artelius
31

Statisch verknüpfte Bibliotheken werden zur Kompilierungszeit verknüpft. Dynamisch verknüpfte Bibliotheken werden zur Laufzeit geladen. Durch statische Verknüpfung wird das Bibliotheksbit in Ihre ausführbare Datei eingebrannt. Die dynamische Verknüpfung backt nur in einem Verweis auf die Bibliothek. Die Bits für die dynamische Bibliothek sind an anderer Stelle vorhanden und können später ausgetauscht werden.

John D. Cook
quelle
16

Da keiner der oben genannten Beiträge tatsächlich zeigt, wie man etwas statisch verknüpft und sieht, dass Sie es richtig gemacht haben, werde ich dieses Problem ansprechen:

Ein einfaches C-Programm

#include <stdio.h>

int main(void)
{
    printf("This is a string\n");
    return 0;
}

Verknüpfen Sie das C-Programm dynamisch

gcc simpleprog.c -o simpleprog

Und laufen Sie fileauf der Binärdatei:

file simpleprog 

Und das wird zeigen, dass es dynamisch mit etwas verbunden ist, das wie folgt aussieht:

"simpleprog: ELF 64-Bit-LSB-ausführbare Datei, x86-64, Version 1 (SYSV), dynamisch verknüpft (verwendet gemeinsam genutzte Bibliotheken), für GNU / Linux 2.6.26, BuildID [sha1] = 0xf715572611a8b04f686809d90d1c0d75c6028f0f, nicht entfernt"

Lassen Sie uns stattdessen das Programm diesmal statisch verknüpfen:

gcc simpleprog.c -static -o simpleprog

Wenn Sie eine Datei auf dieser statisch verknüpften Binärdatei ausführen, wird Folgendes angezeigt:

file simpleprog 

"simpleprog: ELF 64-Bit-LSB-ausführbare Datei, x86-64, Version 1 (GNU / Linux), statisch verknüpft, für GNU / Linux 2.6.26, BuildID [sha1] = 0x8c0b12250801c5a7c7434647b7dc65a644d6132b, nicht entfernt"

Und Sie können sehen, dass es glücklich statisch verknüpft ist. Leider sind jedoch nicht alle Bibliotheken auf diese Weise einfach statisch zu verknüpfen und erfordern möglicherweise einen längeren Aufwand bei der Verwendung libtooloder Verknüpfung des Objektcodes und der C-Bibliotheken von Hand.

Glücklicherweise muslbieten viele eingebettete C-Bibliotheken statische Verknüpfungsoptionen für fast alle, wenn nicht alle ihrer Bibliotheken.

Jetzt stracedie von Ihnen erstellte Binärdatei und Sie können sehen, dass vor Beginn des Programms keine Bibliotheken aufgerufen werden:

strace ./simpleprog

Vergleichen Sie nun mit der Ausgabe des stracedynamisch verknüpften Programms und Sie werden sehen, dass die Strace der statisch verknüpften Version viel kürzer ist!


quelle
2

(Ich kenne C # nicht, aber es ist interessant, ein statisches Verknüpfungskonzept für eine VM-Sprache zu haben.)

Bei der dynamischen Verknüpfung müssen Sie wissen, wie Sie eine erforderliche Funktionalität finden, auf die Sie nur eine Referenz aus Ihrem Programm haben. Ihre Sprachlaufzeit oder Ihr Betriebssystem suchen im Dateisystem, im Netzwerk oder im kompilierten Code-Cache nach einem Code, der mit der Referenz übereinstimmt, und ergreifen dann verschiedene Maßnahmen, um ihn in Ihr Programmabbild im Speicher zu integrieren, z. B. das Verschieben. Sie sind alle zur Laufzeit fertig. Dies kann entweder manuell oder vom Compiler erfolgen. Es besteht die Möglichkeit, Aktualisierungen mit dem Risiko von Fehlern durchzuführen (nämlich DLL-Hölle).

Die statische Verknüpfung erfolgt zum Zeitpunkt der Kompilierung. Sie teilen dem Compiler mit, wo sich alle Funktionsteile befinden, und weisen ihn an, sie zu integrieren. Es gibt keine Suche, keine Mehrdeutigkeit, keine Möglichkeit zum Aktualisieren ohne Neukompilierung. Alle Ihre Abhängigkeiten sind physisch eins mit Ihrem Programmabbild.

künstlicher Idiot
quelle