webentwicklung-frage-antwort-db.com.de

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

Ich höre oft die Begriffe "statisch verknüpft" und "dynamisch verknüpft", oft in Bezug auf Code, der in C , C++ oder geschrieben wurde C # . Was sind sie, worüber reden sie genau und was verbinden sie?

210
UnkwnTech

Der Übergang vom Quellcode (was Sie schreiben) zum ausführbaren Code (was Sie ausführen) erfolgt in den meisten Fällen in zwei Schritten.

Die erste ist die Kompilierung, die den Quellcode in Objektmodule umwandelt.

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

Die Unterscheidung erfolgt unter anderem, um zu ermöglichen, dass Bibliotheken von Drittanbietern in Ihre ausführbare Datei aufgenommen werden, ohne dass der Quellcode angezeigt wird (z. B. Bibliotheken für den Datenbankzugriff, die Netzwerkkommunikation und grafische Benutzeroberflächen), oder um Code in verschiedenen Sprachen zu kompilieren ( C und Assembler-Code zum Beispiel) und dann alle miteinander verknüpfen.

Wenn Sie statisch eine Datei mit 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, wird ein Zeiger auf die verknüpfte Datei (z. B. der Dateiname der Datei) in die ausführbare Datei aufgenommen, und der Inhalt dieser Datei wird zum Zeitpunkt der Verknüpfung nicht berücksichtigt. Erst wenn Sie später run die ausführbare Datei ausführen, werden diese dynamisch verknüpften Dateien eingekauft und sie werden nur in die speicherinterne Kopie der ausführbaren Datei eingekauft, nicht in die auf der Festplatte.

Dies ist im Grunde eine Methode zum verzögerten Verknüpfen. Es gibt eine sogar mehr verzögerte Methode (auf manchen Systemen als späte Bindung bezeichnet), die die dynamisch verknüpfte Datei erst dann einfügt, wenn Sie tatsächlich versuchen, eine darin enthaltene Funktion aufzurufen.

Statisch verknüpfte Dateien sind zum Zeitpunkt der Verknüpfung mit der ausführbaren Datei 'gesperrt', sodass sie sich nie ändern. Eine dynamisch verknüpfte Datei, auf die von einer ausführbaren Datei verwiesen wird, kann sich ändern, indem nur die Datei auf dem Datenträger ersetzt wird.

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

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


Betrachten wir als Beispiel den Fall eines Benutzers, der seine main.c - Datei 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 zum Zeitpunkt der Verknüpfung (von den Entwicklern) miteinander verbunden 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-Laufzeit-Importbibliothek verknüpft (etwas, das deklariert, was sich in der dynamischen Bibliothek befindet, aber nicht tatsächlich define it). Dadurch kann der Linker verknüpfen, obwohl der eigentliche Code fehlt.

Zur Laufzeit führt der Betriebssystem-Loader dann eine späte Verknüpfung des Hauptprogramms mit der C-Laufzeit durch DLL (Bibliothek für dynamische Verknüpfungen oder gemeinsam genutzte Bibliothek oder andere Nomenklatur).

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

411
paxdiablo

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 den Prozessor beim Ausführen veranlasst, Speicher zu addieren, zu subtrahieren, zu vergleichen, zu "gehen", zu lesen, Speicher zu schreiben, so etwas. Dieses Zeug wird in Objektdateien (.o) gespeichert.

Vor langer Zeit haben Informatiker dieses "Unterprogramm" erfunden. Führe diesen Code-Block aus und kehre hierher 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.

In den Anfängen mussten Programmierer nun 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 war also automatisiert. Sie schreiben ein Programm, das printf() aufruft, und der Compiler kennt die Speicheradresse von printf nicht. Der Compiler schreibt also nur CALL 0x0000 Und fügt der Objektdatei einen Hinweis hinzu, der besagt, dass "dieses 0x0000 durch den Speicherort von printf ersetzt werden muss. ".

Statische Verknüpfung bedeutet, dass das Linker-Programm (das GNU eins heißt ld ) den Maschinencode von printf direkt zu Ihrer ausführbaren Datei hinzufügt und 0x0000 ändert an die Adresse von 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 still enthält den Hinweis "0x000 muss durch den Speicherort von printf ersetzt werden". Der Loader des Betriebssystems muss den printf-Code finden, in den Speicher laden und die CALL-Adresse korrigieren, jedes Mal, wenn das Programm ausgeführt wird.

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

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

214
Artelius

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

29
John D. Cook

Da keiner der oben genannten Beiträge tatsächlich zeigt, wie etwas statisch zu verlinken und zu sehen, 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

Führen Sie file für die Binärdatei aus:

file simpleprog 

Und das wird zeigen, dass es dynamisch verknüpft ist im Sinne von:

"simpleprog: ELF 64-Bit-LSB-Programm, 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 dieses Mal statisch verlinken:

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-Programm, 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, es ist glücklich statisch verknüpft. Leider sind nicht alle Bibliotheken auf diese Weise einfach statisch zu verknüpfen und erfordern möglicherweise einen längeren Aufwand, indem Sie libtool verwenden oder den Objektcode und die C-Bibliotheken manuell verknüpfen.

Glücklicherweise bieten viele eingebettete C-Bibliotheken wie musl statische Verknüpfungsoptionen für fast alle wenn nicht alle ihrer Bibliotheken an.

Nun strace die Binärdatei, die Sie erstellt haben, und Sie können sehen, dass auf keine Bibliotheken zugegriffen wird, bevor das Programm beginnt:

strace ./simpleprog

Vergleichen Sie nun die Ausgabe von strace im dynamisch verknüpften Programm, und Sie werden feststellen, dass die statisch verknüpfte Version eine viel kürzere Strecke aufweist!

13
user4832408

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

Beim dynamischen Verknüpfen müssen Sie wissen, wie Sie eine erforderliche Funktionalität finden, für die Sie nur eine Referenz aus Ihrem Programm haben. Sie suchen zur Laufzeit oder im Betriebssystem nach einem Teil des Codes im Dateisystem, im Netzwerk oder im kompilierten Code-Cache, der mit der Referenz übereinstimmt, und ergreifen dann verschiedene Maßnahmen, um ihn in Ihr Programmimage im Speicher zu integrieren, z. Sie werden alle zur Laufzeit ausgeführt. Dies kann entweder manuell oder vom Compiler durchgeführt werden. Es besteht die Möglichkeit eines Updates mit dem Risiko eines Datenverlusts (d. H. DLL Hölle)).

Die statische Verknüpfung erfolgt zum Zeitpunkt der Kompilierung. Sie teilen dem Compiler mit, wo sich alle funktionalen Teile 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 Programmimage.

2
artificialidiot