Risposte:
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.c
file 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.
.dll
o .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.
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 0x0000
e 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 printf
il 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 printf
sono 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.
ld
documentazione GNU .
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.
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 file
sul 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 libtool
o collegando manualmente il codice oggetto e le librerie C.
Fortunatamente molte librerie C integrate musl
offrono opzioni di collegamento statico per quasi tutte se non tutte le loro librerie.
Ora strace
il 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 strace
programma collegato dinamicamente e vedrai che la sequenza della versione collegata staticamente è molto più breve!
(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.