Quando utilizzare le librerie dinamiche vs. statiche


Risposte:


299

Le librerie statiche aumentano la dimensione del codice nel tuo binario. Vengono sempre caricati e qualsiasi versione del codice compilato è la versione del codice che verrà eseguita.

Le librerie dinamiche vengono archiviate e versionate separatamente. È possibile caricare una versione della libreria dinamica che non era quella originale fornita con il codice se l'aggiornamento è considerato binario compatibile con la versione originale.

Inoltre le librerie dinamiche non sono necessariamente caricate - di solito vengono caricate quando vengono chiamate per la prima volta - e possono essere condivise tra i componenti che utilizzano la stessa libreria (più caricamenti di dati, un caricamento di codice).

Le librerie dinamiche erano considerate l'approccio migliore per la maggior parte del tempo, ma in origine avevano un grosso difetto (inferno DLL di Google), che è stato quasi completamente eliminato dai più recenti sistemi operativi Windows (in particolare Windows XP).


71
Su Windows / Mac (nessun gestore di pacchetti) non c'è davvero alcun buon motivo per usare librerie dinamiche su statiche. Poiché le DLL di Windows non sono trasferibili, la condivisione del codice spesso non funziona (e di solito ogni app viene fornita e utilizza comunque le proprie versioni della libreria). L'unico vero vantaggio è che è più semplice aggiornare la libreria.
Zifre,

5
sul mac, uso molte librerie dinamiche. ad esempio, mac os x ha sqlite3 incorporato. ho creato un programma che ha una funzione di database sqlite3 per l'archiviazione delle prestazioni. tuttavia, poiché viene raramente utilizzato il collegamento dinamico consente di risparmiare sui tempi di compilazione, rende i test più facili / veloci, tuttavia, se dovessi creare una versione di rilascio, penso che utilizzerei sempre la libreria statica solo in caso di problemi di compatibilità
ReachConnection

6
@Zifre: relocatable = può essere caricato su diversi indirizzi virtuali. DLL sicuramente supporta questo.
dma_k,

20
@dma_k: le DLL di Windows possono essere caricate su indirizzi diversi, ma solo perché il linker copia tutto il codice e modifica i numeri di indirizzo. Con gli oggetti condivisi, tutti i riferimenti di indirizzo sono relativi, quindi più processi possono condividere la stessa memoria per l'oggetto condiviso. In altre parole, su Windows, una DLL da 1 MB utilizzata da 3 programmi = 3 MB. Su Linux, un SO SO utilizzato da 3 programmi = 1 MB.
Zifre,

7
Sia Windows che Linux hanno il concetto di riposizionamento load-timte delle librerie condivise eli.thegreenplace.net/2011/08/25/… La cosa più grande che ha permesso a Position Independent Code non era qualcosa di speciale per Linux, piuttosto ha aggiunto l'indirizzamento relativo al RIP con il set di istruzioni x64; sia Windows che Linux possono utilizzare l'indirizzamento relativo RIP, riducendo il numero di correzioni durante il trasferimento delle librerie.
Clemahieu,

194

Altri hanno spiegato adeguatamente cos'è una libreria statica, ma vorrei sottolineare alcune delle avvertenze sull'uso delle librerie statiche, almeno su Windows:

  • Singletons: se qualcosa deve essere globale / statico e unico, fai molta attenzione a metterlo in una libreria statica. Se più DLL sono collegate a quella libreria statica, ognuna otterrà la propria copia del singleton. Tuttavia, se l'applicazione è un singolo EXE senza DLL personalizzate, questo potrebbe non essere un problema.

  • Rimozione di codice senza riferimento: quando si effettua il collegamento a una libreria statica, solo le parti della libreria statica a cui fa riferimento la DLL / EXE verranno collegate alla DLL / EXE.

    Per esempio, se mylib.libcontiene a.obje b.obje la DLL / EXE solo riferimenti funzioni o variabili dal a.obj, la totalità della b.objandranno scartato dal linker. Se b.objcontiene oggetti globali / statici, i loro costruttori e distruttori non verranno eseguiti. Se quei costruttori / distruttori hanno effetti collaterali, potresti essere deluso dalla loro assenza.

    Allo stesso modo, se la libreria statica contiene punti speciali speciali, potrebbe essere necessario assicurarsi che siano effettivamente inclusi. Un esempio di ciò nella programmazione integrata (ok, non in Windows) potrebbe essere un gestore di interrupt contrassegnato come associato a un indirizzo specifico. È inoltre necessario contrassegnare il gestore di interrupt come un punto di accesso per assicurarsi che non venga scartato.

    Un'altra conseguenza di ciò è che una libreria statica può contenere file oggetto che sono completamente inutilizzabili a causa di riferimenti non risolti, ma non causerà un errore del linker fino a quando non si fa riferimento a una funzione o variabile da tali file oggetto. Ciò può accadere molto tempo dopo la scrittura della libreria.

  • Simboli di debug: è possibile che si desideri un PDB separato per ciascuna libreria statica oppure si consiglia di posizionare i simboli di debug nei file oggetto in modo che vengano inseriti nel PDB per la DLL / EXE. La documentazione di Visual C ++ spiega le opzioni necessarie .

  • RTTI: potresti finire con più type_infooggetti per la stessa classe se colleghi una singola libreria statica in più DLL. Se il tuo programma presume che type_infosiano dati "singleton" e utilizzi &typeid()o type_info::before(), potresti ottenere risultati indesiderati e sorprendenti.


23
Per quanto riguarda il punto sui singleton, non dimenticare che una DLL potrebbe essere caricata più volte (stessa versione o versioni multiple) e non esiste ancora alcuna garanzia singleton.
Orion Adrian,

Punto aggiuntivo sulla rimozione del codice senza riferimento: le chiamate effettuate alle DLL richiedono anche una chiamata effettiva per forzare il caricamento della DLL di riferimento. Aggiungendolo come riferimento, ma non includendo alcuna chiamata che fa riferimento, otterrai comunque lo stesso risultato di avere una libreria statica che non chiama nulla. L'unica differenza è ciò che effettivamente spedisce. In entrambi i casi i costruttori statici e i distruttori non sparano.
Orion Adrian,

@ bk1e Non dovrebbe succedere. il .a conterrà sempre tutti i simboli con cui è stato costruito. Quando è staticamente collegato all'applicazione, sì, verranno collegati solo i simboli utilizzati.
Miles Rout,

62

Una lib è un'unità di codice inclusa nel file eseguibile dell'applicazione.

Una dll è un'unità autonoma di codice eseguibile. Viene caricato nel processo solo quando viene effettuata una chiamata in quel codice. Una dll può essere utilizzata da più applicazioni e caricata in più processi, pur mantenendo una sola copia del codice sul disco rigido.

Professionisti Dll : possono essere utilizzati per riutilizzare / condividere il codice tra diversi prodotti; caricare nella memoria di processo su richiesta e può essere scaricato quando non necessario; può essere aggiornato indipendentemente dal resto del programma.

Contro dll : impatto delle prestazioni del caricamento dll e del rebasing del codice; problemi di versioning ("dll hell")

Pro Lib : nessun impatto sulle prestazioni in quanto il codice viene sempre caricato nel processo e non viene riformulato; nessun problema di versioning.

Contro : eseguibile / processo "bloat" - tutto il codice è nell'eseguibile e viene caricato all'avvio del processo; nessun riutilizzo / condivisione: ogni prodotto ha la propria copia del codice.


Il rebasing può anche essere eseguito in fase di compilazione utilizzando rebase.exe o passando l'opzione / BASE a link.exe. L'efficacia dipende dal fatto che ci siano conflitti imprevisti nello spazio degli indirizzi durante l'esecuzione.
bk1e,

24

Oltre alle implicazioni tecniche delle librerie statiche e dinamiche (i file statici raggruppano tutto in una grande libreria binaria rispetto alle librerie dinamiche che consentono la condivisione del codice tra diversi eseguibili), ci sono implicazioni legali .

Ad esempio, se si utilizza il codice con licenza LGPL e si collega staticamente a una libreria LGPL (e quindi si crea un grosso binario), il codice diventa automaticamente codice LGPL di origine aperta ( gratuito come in libertà) . Se si collega a oggetti condivisi, è necessario solo LGPL i miglioramenti / correzioni di errori apportati alla libreria LGPL stessa.

Questo diventa un problema molto più importante se stai decidendo come compilare le tue applicazioni mobili, ad esempio (in Android hai una scelta tra statica e dinamica, in iOS non lo fai - è sempre statica).


23

I programmi C ++ sono costruiti in due fasi

  1. Compilazione: produce codice oggetto (.obj)
  2. Collegamento: produce codice eseguibile (.exe o .dll)

La libreria statica (.lib) è solo un pacchetto di file .obj e quindi non è un programma completo. Non ha subito la seconda fase (collegamento) di costruzione di un programma. Le DLL, d'altra parte, sono come exe e quindi sono programmi completi.

Se costruisci una libreria statica, questa non è ancora collegata e quindi i consumatori della tua libreria statica dovranno usare lo stesso compilatore che hai usato (se hai usato g ++, dovranno usare g ++).

Se invece hai creato una dll (e l'hai costruita correttamente ), hai creato un programma completo che tutti i consumatori possono usare, indipendentemente dal compilatore che stanno usando. Esistono tuttavia alcune restrizioni sull'esportazione da una dll, se si desidera la compatibilità tra compilatori.


1
Questa è una novità per me. Quali sono le restrizioni con i compilatori incrociati quando si utilizzano DLL? Avere il programmatore compilato senza la stessa toolchain sembra un grande vantaggio per le DLL
Dan

1
Questa risposta è informativa. Aggiunta di un avvertimento minore: consumers of your static library will have to use the same compiler that you usedse la libreria statica utilizza una libreria C ++, ad esempio #include <iostream>.
truthadjustr

uno non può consumare una DLL c ++ a meno che non venga utilizzato lo stesso compilatore (poiché non esiste un abi c ++ standard, i simboli sono alterati in modi diversi). Sia la dll che il modulo client devono usare lo stesso compilatore e le stesse impostazioni di compilazione
kcris

19

Creazione di una libreria statica

$$:~/static [32]> cat foo.c
#include<stdio.h>
void foo()
{
printf("\nhello world\n");
}
$$:~/static [33]> cat foo.h
#ifndef _H_FOO_H
#define _H_FOO_H

void foo();

#endif
$$:~/static [34]> cat foo2.c
#include<stdio.h>
void foo2()
{
printf("\nworld\n");
}
$$:~/static [35]> cat foo2.h
#ifndef _H_FOO2_H
#define _H_FOO2_H

void foo2();

#endif
$$:~/static [36]> cat hello.c
#include<foo.h>
#include<foo2.h>
void main()
{
foo();
foo2();
}
$$:~/static [37]> cat makefile
hello: hello.o libtest.a
        cc -o hello hello.o -L. -ltest
hello.o: hello.c
        cc -c hello.c -I`pwd`
libtest.a:foo.o foo2.o
        ar cr libtest.a foo.o foo2.o
foo.o:foo.c
        cc -c foo.c
foo2.o:foo.c
        cc -c foo2.c
clean:
        rm -f foo.o foo2.o libtest.a hello.o

$$:~/static [38]>

creando una biblioteca dinamica

$$:~/dynamic [44]> cat foo.c
#include<stdio.h>
void foo()
{
printf("\nhello world\n");
}
$$:~/dynamic [45]> cat foo.h
#ifndef _H_FOO_H
#define _H_FOO_H

void foo();

#endif
$$:~/dynamic [46]> cat foo2.c
#include<stdio.h>
void foo2()
{
printf("\nworld\n");
}
$$:~/dynamic [47]> cat foo2.h
#ifndef _H_FOO2_H
#define _H_FOO2_H

void foo2();

#endif
$$:~/dynamic [48]> cat hello.c
#include<foo.h>
#include<foo2.h>
void main()
{
foo();
foo2();
}
$$:~/dynamic [49]> cat makefile
hello:hello.o libtest.sl
        cc -o hello hello.o -L`pwd` -ltest
hello.o:
        cc -c -b hello.c -I`pwd`
libtest.sl:foo.o foo2.o
        cc -G -b -o libtest.sl foo.o foo2.o
foo.o:foo.c
        cc -c -b foo.c
foo2.o:foo.c
        cc -c -b foo2.c
clean:
        rm -f libtest.sl foo.o foo

2.o hello.o
$$:~/dynamic [50]>

13

Una libreria statica viene compilata nel client. Un file .lib viene utilizzato in fase di compilazione e il contenuto della libreria diventa parte dell'eseguibile di consumo.

Una libreria dinamica viene caricata in fase di runtime e non compilata nell'eseguibile client. Le librerie dinamiche sono più flessibili poiché più eseguibili client possono caricare una DLL e utilizzarne le funzionalità. Ciò consente inoltre di ridurre al minimo le dimensioni e la manutenibilità complessive del codice client.


13

Dovresti riflettere attentamente sui cambiamenti nel tempo, sul controllo delle versioni, sulla stabilità, sulla compatibilità, ecc.

Se ci sono due app che usano il codice condiviso, vuoi forzare queste app a cambiare insieme, nel caso in cui debbano essere compatibili tra loro? Quindi utilizzare la dll. Tutti gli exe useranno lo stesso codice.

Oppure vuoi isolarli l'uno dall'altro, in modo da poterne cambiare uno e avere la certezza di non aver rotto l'altro. Quindi utilizzare la lib statica.

L'inferno DLL è quando probabilmente DOVREBBE AVERE usato una lib statica, ma invece hai usato una dll, e non tutti gli ex sono compatibili.


9

Una libreria statica deve essere collegata all'eseguibile finale; diventa parte dell'eseguibile e lo segue ovunque vada. Una libreria dinamica viene caricata ogni volta che viene eseguito l'eseguibile e rimane separata dall'eseguibile come file DLL.

Si utilizzerà una DLL quando si desidera poter modificare la funzionalità fornita dalla libreria senza ricollegare l'eseguibile (basta sostituire il file DLL, senza sostituire il file eseguibile).

Utilizzeresti una libreria statica ogni volta che non hai un motivo per usare una libreria dinamica.


È inoltre possibile utilizzare una DLL quando più altre applicazioni utilizzano la stessa funzionalità: ciò può ridurre il footprint.
Tim

Inoltre, estendendo il concetto iniziale, l'architettura "plug-in" in cui si desidera consentire funzionalità aggiunte / sconosciute in un secondo momento senza dover ricostruire o rilasciare nuovamente può essere eseguita solo con librerie dinamiche.
Tim

8

L'articolo di Ulrich Drepper su " Come scrivere librerie condivise " è anche una buona risorsa che descrive in dettaglio come sfruttare al meglio le librerie condivise o quelli che egli chiama "oggetti condivisi dinamici" (DSO). Si concentra maggiormente sulle librerie condivise nel formato binario ELF , ma alcune discussioni sono adatte anche per le DLL di Windows.


5

Per un'eccellente discussione su questo argomento, leggi questo articolo di Sun.

Comprende tutti i vantaggi tra cui la possibilità di inserire librerie di interposizione. Maggiori dettagli sull'interposizione sono disponibili in questo articolo qui .


4

In realtà il trade off che stai facendo (in un grande progetto) è nel tempo di caricamento iniziale, le librerie verranno collegate in un momento o nell'altro, la decisione che deve essere presa sarà il collegamento impiegherà abbastanza tempo di cui il compilatore ha bisogno per mordere il proiettile e farlo in anticipo, oppure il linker dinamico può farlo al momento del caricamento.


3

Se la tua libreria sarà condivisa tra diversi eseguibili, spesso ha senso renderla dinamica per ridurre le dimensioni degli eseguibili. Altrimenti, rendilo sicuramente statico.

Ci sono diversi svantaggi nell'uso di una dll. C'è un sovraccarico aggiuntivo per il caricamento e lo scaricamento. C'è anche una dipendenza aggiuntiva. Se modifichi la dll per renderla incompatibile con i tuoi execalbes, smetteranno di funzionare. D'altra parte, se si modifica una libreria statica, i file eseguibili compilati che utilizzano la versione precedente non saranno interessati.


3

Se la libreria è statica, al momento del collegamento il codice è collegato al tuo eseguibile. Questo rende il tuo eseguibile più grande (che se avessi seguito il percorso dinamico).

Se la libreria è dinamica, al momento del collegamento i riferimenti ai metodi richiesti sono integrati nell'eseguibile. Questo significa che devi spedire il tuo eseguibile e la libreria dinamica. Dovresti anche considerare se l'accesso condiviso al codice nella libreria è sicuro, preferisci l'indirizzo di caricamento tra le altre cose.

Se riesci a vivere con la libreria statica, vai con la libreria statica.


3

Usiamo molte DLL (> 100) nel nostro progetto. Queste DLL hanno dipendenze reciproche e quindi abbiamo scelto l'installazione del collegamento dinamico. Tuttavia presenta i seguenti svantaggi:

  • avvio lento (> 10 secondi)
  • Le DLL dovevano essere aggiornate, poiché Windows carica i moduli in base all'unicità dei nomi. I componenti scritti propri otterrebbero altrimenti la versione errata della DLL (ovvero quella già caricata anziché il proprio set distribuito)
  • L'ottimizzatore può ottimizzare solo entro i limiti della DLL. Ad esempio, l'ottimizzatore tenta di posizionare i dati e il codice utilizzati di frequente uno accanto all'altro, ma ciò non funziona oltre i limiti della DLL

Forse una configurazione migliore è stata quella di rendere tutto una libreria statica (e quindi hai solo un eseguibile). Funziona solo se non si verifica alcuna duplicazione del codice. Un test sembra supportare questo presupposto, ma non sono riuscito a trovare un preventivo MSDN ufficiale. Quindi ad esempio crea 1 exe con:

  • exe utilizza shared_lib1, shared_lib2
  • shared_lib1 usa shared_lib2
  • shared_lib2

Il codice e le variabili di shared_lib2 dovrebbero essere presenti nell'eseguibile unito finale solo una volta. Qualcuno può supportare questa domanda?


Non hai intenzione di usare alcune direttive pre-compilatore in qualche modo per evitare la duplicazione del codice?
Paceman,

La precompilazione alfaiac funziona solo su una base per modulo (exe / dll / lib). La precompilazione ha lo scopo primario di accelerare la compilazione sebbene prevenga anche inclusioni multiple all'interno di un'unità di compilazione. Tuttavia, includere le protezioni è il modo migliore per ottenere questo effetto.
gast128,

2

Le librerie statiche sono archivi che contengono il codice oggetto per la libreria, quando collegato a un'applicazione che il codice viene compilato nell'eseguibile. Le librerie condivise sono diverse in quanto non sono compilate nell'eseguibile. Invece il linker dinamico cerca alcune directory cercando le librerie necessarie, quindi le carica in memoria. Più di un eseguibile può utilizzare la stessa libreria condivisa contemporaneamente, riducendo l'utilizzo della memoria e le dimensioni dell'eseguibile. Tuttavia, ci sono quindi più file da distribuire con l'eseguibile. Devi assicurarti che la libreria sia installata sul sistema degli usi da qualche parte dove il linker può trovarla, il collegamento statico elimina questo problema ma si traduce in un file eseguibile più grande.


2

Se lavori su progetti incorporati o librerie statiche su piattaforme specializzate sono l'unica strada da percorrere, anche molte volte sono meno complicate da compilare nella tua applicazione. Anche avere progetti e makefile che includono tutto rende la vita più felice.


2

Darei una regola empirica generale che se si dispone di una base di codice di grandi dimensioni, il tutto costruito su librerie di livello inferiore (ad esempio un framework Utils o Gui), che si desidera suddividere in librerie più gestibili, quindi renderle librerie statiche. Le biblioteche dinamiche non ti comprano davvero nulla e ci sono meno sorprese, ad esempio ci sarà solo un'istanza di singoli.

Se si dispone di una libreria completamente separata dal resto della base di codice (ad esempio una libreria di terze parti), considerare di renderla una dll. Se la libreria è LGPL, potrebbe essere necessario utilizzare comunque una dll a causa delle condizioni di licenza.

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.