Domanda

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

È stato utile?

Soluzione

Vi 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 grafiche utente), o per compilare il codice in lingue diverse (ad esempio codice C e assembly) e quindi collegandole tutte insieme.

Quando staticamente si collega 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 eseguirai.

Quando si collega in modo dinamico , un puntatore al file da collegare (il nome del file, ad esempio) è incluso nell'eseguibile e i contenuti di tale file non sono inclusi nel collegamento tempo. È 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 più differito (chiamato associazione tardiva su alcuni sistemi) che non porta il file collegato dinamicamente finché non si tenta effettivamente di chiamare una funzione al suo interno.

I file collegati staticamente vengono "bloccati" all'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.

Questo 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 dell'inferno temuto " DLL hell " ; che alcune persone menzionano che le applicazioni possono essere rotte se si sostituisce una libreria collegata dinamicamente con una non compatibile (gli sviluppatori che lo fanno dovrebbero aspettarsi di essere cacciati e puniti severamente, comunque).


Come esempio , diamo un'occhiata al caso in cui un utente compili il suo file main.c 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  |
          +---------+               +---------+

Nel caso statico puoi vedere 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 ciò che è nella libreria dinamica ma non lo definisce ). 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.

Altri suggerimenti

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

Quando si compila un codice C (ad esempio), questo viene tradotto in linguaggio macchina. Solo una sequenza di byte che, quando eseguito, fa sì che il processore aggiunga, sottragga, confronti, "vai a", leggi memoria, scrivi memoria, quel genere di cose. Questo materiale è archiviato in file oggetto (.o).

Ora, molto tempo fa, gli informatici hanno inventato questa "subroutine" cosa. 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 giorni i programmatori avrebbero dovuto inserire l'indirizzo di memoria in cui si trovavano queste subroutine. Qualcosa come 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 semplicemente 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 nel file eseguibile e modifica 0x0000 nell'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 ancora ha una nota che dice "deve sostituire 0x000 con la posizione 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 viene eseguito il programma .

È comune per i programmi chiamare alcune funzioni che saranno collegate staticamente (le funzioni di libreria standard come printf sono generalmente collegate staticamente) e altre funzioni che sono collegate dinamicamente. Quelle statiche "diventano parte" dell'eseguibile e di quelli dinamici " unisciti a " 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.

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.

Poiché nessuno dei post precedenti 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;
}

Collega 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, stripped

Invece lasciaci collegare staticamente il programma questa volta:

gcc simpleprog.c -static -o simpleprog

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

strace ./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 stripped "

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

Fortunatamente molte librerie C incorporate come musl offrono opzioni di collegamento statico per quasi tutte le se non tutte delle loro librerie.

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

<*>

Ora confronta con l'output di strace sul programma collegato dinamicamente e vedrai che lo strace 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 cercano 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 al momento della compilazione, indicando al compilatore dove si trovano tutte le parti funzionali e gli viene chiesto di integrarle. Non ci sono ricerche, nessuna ambiguità, nessuna possibilità di aggiornamento senza ricompilazione. Tutte le tue dipendenze sono fisicamente una con l'immagine del tuo programma.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top