Cosa significano "link staticamente" e "link dinamicamente"?


229

Sento spesso i termini "staticamente collegati" e "dinamicamente collegati", spesso in riferimento al codice scritto in C , C ++ o C # . Cosa sono, di cosa stanno esattamente parlando e cosa stanno collegando?

Risposte:


445

Ci sono (nella maggior parte dei casi, l'attualizzazione del codice interpretato) due fasi per passare dal codice sorgente (ciò che scrivi) al codice eseguibile (ciò che esegui).

La prima è la compilazione che trasforma il codice sorgente in moduli oggetto.

Il secondo, il collegamento, è ciò che combina i moduli oggetto insieme per formare un eseguibile.

La distinzione è fatta, tra l'altro, per consentire alle librerie di terze parti di essere incluse nell'eseguibile senza che tu veda il loro codice sorgente (come le librerie per l'accesso al database, le comunicazioni di rete e le interfacce utente grafiche) o per compilare il codice in lingue diverse ( C e codice assembly per esempio) e quindi collegandoli tutti insieme.

Quando si collega staticamente un file in un eseguibile, i contenuti di quel file vengono inclusi al momento del collegamento. In altre parole, il contenuto del file viene fisicamente inserito nell'eseguibile che verrà eseguito.

Quando si collega dinamicamente , un puntatore al file in collegamento (il nome del file, ad esempio) è incluso nell'eseguibile e il contenuto di tale file non è incluso al momento del collegamento. È solo quando in seguito esegui l'eseguibile che questi file collegati dinamicamente vengono acquistati e acquistati solo nella copia in memoria dell'eseguibile, non quella sul disco.

È fondamentalmente un metodo di collegamento differito. Esiste un metodo ancora più differito (chiamato late binding su alcuni sistemi) che non porta il file collegato dinamicamente fino a quando non si tenta effettivamente di chiamare una funzione al suo interno.

I file collegati staticamente vengono "bloccati" nell'eseguibile al momento del collegamento, quindi non cambiano mai. Un file collegato dinamicamente a cui fa riferimento un eseguibile può cambiare semplicemente sostituendo il file sul disco.

Ciò consente aggiornamenti della funzionalità senza dover ricollegare il codice; il caricatore ricollega ogni volta che lo si esegue.

Questo è sia positivo che negativo - da un lato, consente aggiornamenti e correzioni di bug più facili, dall'altro può portare a programmi che smettono di funzionare se gli aggiornamenti sono incompatibili - questo a volte è responsabile del temuto "inferno DLL" che alcune persone menzionare che le applicazioni possono essere interrotte se si sostituisce una libreria collegata dinamicamente con una libreria non compatibile (gli sviluppatori che lo fanno dovrebbero aspettarsi di essere cacciati e puniti severamente, comunque).


Ad esempio , diamo un'occhiata al caso in cui un utente compili il proprio main.cfile per collegamenti statici e dinamici.

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  |
          +---------+               +---------+

Potete vedere nel caso statico che il programma principale e la libreria di runtime C sono collegati insieme al momento del collegamento (dagli sviluppatori). Poiché l'utente in genere non è in grado di ricollegare l'eseguibile, sono bloccati con il comportamento della libreria.

Nel caso dinamico, il programma principale è collegato alla libreria di importazione runtime C (qualcosa che dichiara cosa c'è nella libreria dinamica ma non lo definisce effettivamente ). Ciò consente al linker di collegarsi anche se manca il codice effettivo.

Quindi, in fase di esecuzione, il caricatore del sistema operativo esegue un collegamento in ritardo del programma principale con la DLL di runtime C (libreria a collegamento dinamico o libreria condivisa o altra nomenclatura).

Il proprietario del runtime C può inserire una nuova DLL in qualsiasi momento per fornire aggiornamenti o correzioni di errori. Come affermato in precedenza, ciò presenta sia vantaggi che svantaggi.


11
Per favore, correggimi se sbaglio, ma su Windows il software tende a includere le proprie librerie con l'installazione, anche se sono collegati dinamicamente. Su molti sistemi Linux con un gestore di pacchetti, molte librerie collegate dinamicamente ("oggetti condivisi") sono effettivamente condivise tra software.
Paul Fisher,

6
@PaulF: cose come i controlli comuni di Windows, DirectX, .NET e così via, vengono distribuiti molto con le applicazioni, mentre su Linux tendi a usare apt o yum o qualcosa del genere per gestire le dipendenze, quindi hai ragione in quel senso . Vinci app che spediscono il proprio codice poiché le DLL tendono a non condividerle.
paxdiablo,

31
C'è un posto speciale riservato nel nono cerchio dell'inferno per coloro che aggiornano le loro DLL e rompono la compatibilità con le versioni precedenti. Sì, se le interfacce scompaiono o vengono modificate, il collegamento dinamico cadrà in un heap. Ecco perché non dovrebbe essere fatto. Sicuramente aggiungi una funzione2 () alla tua DLL ma non cambiare funzione () se le persone la usano. Il modo migliore per gestirlo è ricodificare la funzione () in modo tale da chiamare la funzione2 (), ma non modificare la firma della funzione ().
paxdiablo,

1
@Paul Fisher, so che è tardi ma ... la libreria fornita con una DLL di Windows non è la libreria completa, è solo un gruppo di stub che dicono al linker cosa contiene la DLL. Il linker può quindi inserire automaticamente le informazioni in .exe per il caricamento della DLL e i simboli non vengono visualizzati come non definiti.
Mark Ransom,

1
@Santropedro, hai ragione sotto tutti gli aspetti riguardo al significato dei nomi lib, import e DLL. Il suffisso è solo una convenzione, quindi non leggere troppo in quello (ad esempio, la DLL può avere un'estensione .dllo .so) - pensa alla risposta come spiegando i concetti piuttosto che essere una descrizione esatta. E, come per il testo, questo è un esempio che mostra collegamenti statici e dinamici solo per i file di runtime C, quindi sì, questo è ciò che `crt indica in tutti loro.
paxdiablo,

221

Penso che una buona risposta a questa domanda dovrebbe spiegare che cosa il collegamento è .

Quando compili un codice C (ad esempio), viene tradotto in linguaggio macchina. Solo una sequenza di byte che, quando eseguito, fa sì che il processore aggiunga, sottragga, confronti, "vada", legga memoria, scriva memoria, quel genere di cose. Questo materiale è archiviato in file oggetto (.o).

Ora, molto tempo fa, gli informatici hanno inventato questa cosa "subroutine". Esegui-questo-pezzo-di-codice-e-ritorno-qui. Non passò molto tempo prima che si rendessero conto che le subroutine più utili potevano essere archiviate in un posto speciale e utilizzate da qualsiasi programma che ne avesse bisogno.

Ora nei primi tempi i programmatori avrebbero dovuto inserire l'indirizzo di memoria in cui si trovavano queste subroutine. Qualcosa del genere CALL 0x5A62. Questo era noioso e problematico se quegli indirizzi di memoria dovessero mai essere cambiati.

Quindi, il processo è stato automatizzato. Scrivi un programma che chiama printf()e il compilatore non conosce l'indirizzo di memoria di printf. Quindi il compilatore scrive CALL 0x0000e aggiunge una nota al file oggetto dicendo "deve sostituire questo 0x0000 con la posizione di memoria di printf ".

Collegamento statico significa che il programma linker (quello GNU è chiamato ld ) aggiunge printfil codice macchina direttamente al file eseguibile e cambia lo 0x0000 all'indirizzo di printf. Questo succede quando viene creato il tuo eseguibile.

Il collegamento dinamico significa che il passaggio precedente non si verifica. Il file eseguibile ha ancora una nota che dice "deve sostituire 0x000 con il percorso di memoria di printf". Il caricatore del sistema operativo deve trovare il codice printf, caricarlo in memoria e correggere l'indirizzo CALL ogni volta che si esegue il programma .

È comune per i programmi chiamare alcune funzioni che saranno collegate staticamente (le funzioni di libreria standard come printfsono normalmente collegate staticamente) e altre funzioni che sono collegate dinamicamente. Quelli statici "diventano parte" dell'eseguibile e quelli dinamici "si uniscono" quando viene eseguito l'eseguibile.

Ci sono vantaggi e svantaggi per entrambi i metodi e ci sono differenze tra i sistemi operativi. Ma poiché non me l'hai chiesto, finirò qui.


4
Anch'io l'ho fatto, tuttavia posso scegliere solo 1 risposta.
UnkwnTech,

1
Artelius, sto approfondendo la tua spiegazione su come funzionano queste pazze cose di basso livello. per favore rispondi con quali libri dobbiamo leggere per ottenere una conoscenza approfondita di quanto sopra. grazie.
Mahesh,

1
Scusa, non posso suggerire libri. Dovresti prima imparare il linguaggio assembly. Quindi Wikipedia può fornire una panoramica decente di tali argomenti. Potresti voler consultare la lddocumentazione GNU .
Artelius,

31

Le librerie collegate staticamente sono collegate al momento della compilazione. Le librerie collegate dinamicamente vengono caricate in fase di esecuzione. Il collegamento statico inserisce il bit della libreria nel tuo eseguibile. Il collegamento dinamico viene eseguito solo in un riferimento alla libreria; i bit per la libreria dinamica esistono altrove e potrebbero essere sostituiti in un secondo momento.


16

Perché nessuno dei post sopra mostra effettivamente come collegare staticamente qualcosa e vedere che l'hai fatto correttamente, quindi affronterò questo problema:

Un semplice programma C.

#include <stdio.h>

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

Collegare dinamicamente il programma C.

gcc simpleprog.c -o simpleprog

Ed esegui filesul binario:

file simpleprog 

E questo mostrerà che è collegato dinamicamente qualcosa sulla falsariga di:

"simpleprog: eseguibile LSB a 64 bit ELF, x86-64, versione 1 (SYSV), collegato dinamicamente (usa librerie condivise), per GNU / Linux 2.6.26, BuildID [sha1] = 0xf715572611a8b04f686809d90d1c0d75c6028f0f, non rimosso"

Invece lasciaci collegare staticamente il programma questa volta:

gcc simpleprog.c -static -o simpleprog

L'esecuzione del file su questo binario collegato staticamente mostrerà:

file simpleprog 

"simpleprog: eseguibile LSB a 64 bit ELF, x86-64, versione 1 (GNU / Linux), collegato staticamente, per GNU / Linux 2.6.26, BuildID [sha1] = 0x8c0b12250801c5a7c7434647b7dc65a644d6132b, non rimosso"

E puoi vedere che è felicemente collegato staticamente. Purtroppo, tuttavia, non tutte le librerie sono semplici da collegare staticamente in questo modo e possono richiedere uno sforzo maggiore utilizzando libtoolo collegando manualmente il codice oggetto e le librerie C.

Fortunatamente molte librerie C integrate musloffrono opzioni di collegamento statico per quasi tutte se non tutte le loro librerie.

Ora straceil binario che hai creato e puoi vedere che non ci sono librerie accessibili prima dell'inizio del programma:

strace ./simpleprog

Ora confronta con l'output del straceprogramma collegato dinamicamente e vedrai che la sequenza della versione collegata staticamente è molto più breve!


2

(Non conosco C # ma è interessante avere un concetto di collegamento statico per un linguaggio VM)

Il collegamento dinamico implica sapere come trovare una funzionalità richiesta di cui hai solo un riferimento dal tuo programma. Il runtime della lingua o il sistema operativo ricercano un pezzo di codice nel filesystem, nella rete o nella cache del codice compilato, facendo corrispondere il riferimento, e quindi adottano diverse misure per integrarlo nell'immagine del programma nella memoria, come il trasferimento. Sono tutti eseguiti in fase di esecuzione. Può essere fatto manualmente o dal compilatore. C'è la possibilità di aggiornare con il rischio di incasinare (vale a dire, inferno DLL).

Il collegamento statico viene eseguito in fase di compilazione e indica al compilatore dove si trovano tutte le parti funzionali e gli indica di integrarle. Non ci sono ricerche, ambiguità, capacità di aggiornamento senza ricompilazione. Tutte le tue dipendenze sono fisicamente una con l'immagine del tuo programma.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.