c# - Was bedeuten "statisch verknüpft" und "dynamisch verknüpft"?




c++ linker static-linking dynamic-linking (5)

Ich höre oft die Begriffe "statisch verknüpft" und "dynamisch verknüpft", oft in Bezug auf Code in C , C++ oder C# , aber ich weiß auch nicht viel über beides. Worüber genau sprechen sie und was verbinden sie?


Answers

Es gibt (in den meisten Fällen, diskontieren interpretierten Code) zwei Stufen in Bezug auf den Quellcode (was Sie schreiben) in ausführbaren Code (was Sie ausführen).

Die erste ist die Kompilierung, die Quellcode in Objektmodule verwandelt.

Die zweite, Verknüpfung, verbindet die Objektmodule zu einer ausführbaren Datei.

Sie unterscheiden unter anderem, dass Bibliotheken von Drittanbietern in Ihre ausführbare Datei eingeschlossen werden können, ohne dass ihr Quellcode angezeigt wird (z. B. Bibliotheken für Datenbankzugriff, Netzwerkkommunikation und grafische Benutzeroberflächen) oder Code in verschiedenen Sprachen kompiliert wird ( C und Assemblercode zum Beispiel) und verbinden sie dann alle zusammen.

Wenn Sie eine Datei statisch mit einer ausführbaren Datei verknüpfen, wird der Inhalt dieser Datei zur Verknüpfungszeit angezeigt. Mit anderen Worten, der Inhalt der Datei wird physisch in die ausführbare Datei eingefügt, die Sie ausführen.

Wenn Sie dynamisch verknüpfen, ist ein Zeiger auf die Datei, in die verlinkt wird (z. B. der Dateiname der Datei), in der ausführbaren Datei enthalten, und der Inhalt dieser Datei ist nicht zur Verknüpfungszeit enthalten. Nur wenn Sie die ausführbare Datei später ausführen, werden diese dynamisch verknüpften Dateien gekauft und sie werden nur in der In-Memory-Kopie der ausführbaren Datei erworben, nicht auf der Festplatte.

Es ist im Grunde eine Methode der verzögerten Verknüpfung. Es gibt eine noch mehr verzögerte Methode (auf einigen Systemen als späte Bindung bezeichnet), die die dynamisch verknüpfte Datei erst dann einbringt, wenn Sie tatsächlich versuchen, eine Funktion darin aufzurufen.

Statisch verknüpfte Dateien werden zur Verknüpfungszeit 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 die Aktualisierung der Funktionalität, ohne den Code neu verknüpfen zu müssen. Der Loader verbindet sich jedes Mal neu, wenn Sie ihn ausführen.

Dies ist sowohl gut als auch schlecht - auf der einen Seite erlaubt es einfachere Updates und Bugfixes, auf der anderen Seite kann es dazu führen, dass Programme nicht funktionieren, wenn die Updates inkompatibel sind - das ist manchmal für die gefürchtete "DLL-Hölle" verantwortlich Erwähnen Sie, dass Anwendungen gebrochen werden können, wenn Sie eine dynamisch verknüpfte Bibliothek durch eine ersetzen, die nicht kompatibel ist (Entwickler, die dies tun sollten, sollten erwartet werden, dass sie gejagt und streng bestraft werden).

Betrachten wir als Beispiel den Fall eines Benutzers, der seine main.c Datei für die statische und dynamische Verknüpfung 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 Verbindungszeit (von den Entwicklern) miteinander verknüpft sind. Da der Benutzer die ausführbare Datei normalerweise nicht verknüpfen kann, bleibt das Verhalten der Bibliothek hängen.

Im dynamischen Fall ist das Hauptprogramm mit der C-Laufzeit-Importbibliothek verbunden (etwas, das deklariert, was in der dynamischen Bibliothek ist, aber es nicht tatsächlich definiert ). Dies ermöglicht dem Linker zu verknüpfen, obwohl der tatsächliche Code fehlt.

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

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


Da keiner der oben genannten Posts tatsächlich zeigt, wie man etwas statisch verknüpft und sieht, dass du es richtig gemacht hast, werde ich dieses Problem angehen:

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 file auf der Binärdatei ausführen:

file simpleprog 

Und das wird zeigen, dass es dynamisch verknüpft ist mit:

"simpleprog: ELF 64-bit LSB ausführbare Datei, x86-64, Version 1 (SYSV), dynamisch verlinkt (verwendet gemeinsame libs), für GNU / Linux 2.6.26, BuildID [sha1] = 0xf715572611a8b04f686809d90d1c0d75c6028f0f, nicht entfernt"

Lassen Sie uns stattdessen dieses Programm 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 verbunden ist. Leider sind jedoch nicht alle Bibliotheken einfach auf diese Art und Weise statisch miteinander zu verknüpfen und erfordern möglicherweise einen längeren Einsatz von libtool oder die Verknüpfung von Objektcode und C-Bibliotheken von Hand.

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

Jetzt strace die von Ihnen erstellte Binärdatei und Sie können sehen, dass vor dem Programmstart keine Bibliotheken aufgerufen werden:

strace ./simpleprog

Vergleichen Sie nun mit der Ausgabe von strace auf dem dynamisch verknüpften Programm und Sie werden feststellen, dass die strace der statisch verknüpften Version viel kürzer ist!


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

Bei der dynamischen Verknüpfung müssen Sie wissen, wie Sie eine erforderliche Funktionalität finden, die Sie nur aus Ihrem Programm beziehen. Ihre Sprachlaufzeit oder Ihr Betriebssystem sucht nach einem Code im Dateisystem-, Netzwerk- oder kompilierten Code-Cache, der mit der Referenz übereinstimmt, und nimmt dann mehrere Maßnahmen vor, um sie in das Programmbild im Speicher zu integrieren, z. Sie sind alle zur Laufzeit fertig. Dies kann entweder manuell oder durch den Compiler geschehen. Es gibt die Möglichkeit, mit einem Risiko der Unordnung zu aktualisieren (DLL DLL Hölle).

Die statische Verknüpfung erfolgt zur Kompilierungszeit, indem Sie dem Compiler mitteilen, wo sich alle funktionalen Teile befinden, und ihn anweisen, sie zu integrieren. Es gibt keine Suche, keine Mehrdeutigkeit, keine Möglichkeit, ohne eine Neukompilierung zu aktualisieren. Alle Ihre Abhängigkeiten sind physikalisch eins mit Ihrem Programmbild.


Statisch verknüpfte Bibliotheken werden zur Kompilierungszeit verknüpft. Dynamisch verknüpfte Bibliotheken werden zur Laufzeit geladen. Die statische Verknüpfung baktet das Bibliotheksbit in Ihre ausführbare Datei. Die dynamische Verknüpfung backt nur einen Verweis auf die Bibliothek; Die Bits für die dynamische Bibliothek existieren an anderer Stelle und könnten später ausgetauscht werden.


getline , stream operators, scanf , kann praktisch sein, wenn Sie sich nicht um das Laden von Dateien kümmern oder wenn Sie kleine Textdateien laden. Aber wenn die Leistung etwas ist, das Ihnen wichtig ist, sollten Sie nur die gesamte Datei in den Speicher puffern (vorausgesetzt, es passt).

Hier ist ein Beispiel:

//open file in binary mode
std::fstream file( filename, std::ios::in|::std::ios::binary );
if( !file ) return NULL;

//read the size...
file.seekg(0, std::ios::end);
size_t length = (size_t)file.tellg();
file.seekg(0, std::ios::beg);

//read into memory buffer, then close it.
char *filebuf = new char[length+1];
file.read(filebuf, length);
filebuf[length] = '\0'; //make it null-terminated
file.close();

Wenn Sie möchten, können Sie einen Stream um diesen Puffer wickeln, um bequemeren Zugriff zu erhalten:

std::istrstream header(&buffer[0], length);

Wenn Sie die Datei steuern, sollten Sie auch ein flaches binäres Datenformat anstelle von Text verwenden. Es ist zuverlässiger zu lesen und zu schreiben, weil Sie nicht mit allen Unklarheiten von Leerzeichen fertig werden müssen. Es ist auch kleiner und viel schneller zu analysieren.





c# c++ linker static-linking dynamic-linking