Domanda

Quando si crea una libreria di classi in C ++, è possibile scegliere tra dinamico ( .dll , .so ) e statico ( .lib , librerie .a ). Qual è la differenza tra loro e quando è appropriato usare quale?

È stato utile?

Soluzione

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

Le librerie dinamiche vengono archiviate e versionate separatamente. È possibile che venga caricata 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 (l'inferno di google DLL), che è stato quasi completamente eliminato dai più recenti sistemi operativi Windows (in particolare Windows XP).

Altri suggerimenti

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 non referenziato: quando ti colleghi a una libreria statica, solo le parti della libreria statica a cui fa riferimento la tua DLL / EXE verranno collegate alla tua DLL / EXE.

    Ad esempio, se mylib.lib contiene a.obj e b.obj e la tua DLL / EXE fa riferimento solo a funzioni o variabili da a.obj , l'intero b.obj verrà scartato dal linker. Se b.obj contiene 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 ingressi 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 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. Questo può accadere molto tempo dopo la scrittura della libreria.

  • Simboli di debug: potresti volere un PDB separato per ogni libreria statica, oppure potresti voler 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ù oggetti type_info per la stessa classe se colleghi una singola libreria statica in più DLL. Se il tuo programma presuppone che type_info sia " singleton " dati e utilizza & amp; typeid () o type_info :: before () , potresti ottenere risultati indesiderati e sorprendenti.

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.

Pro dll : può essere utilizzato 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 sulle prestazioni del caricamento delle 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 lib : eseguibile / processo " bloat " - tutto il codice è nell'eseguibile e viene caricato all'avvio del processo; nessun riutilizzo / condivisione: ogni prodotto ha una propria copia del codice.

Oltre alle implicazioni tecniche delle librerie statiche vs dinamiche (i file statici raggruppano tutto in una grande libreria binaria vs librerie dinamiche che consentono la condivisione del codice tra diversi eseguibili), ci sono le 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 Open Sourced ( gratuito come in libertà) codice LGPL. 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 puoi scegliere tra statico e dinamico, in iOS no - è sempre statico).


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

creazione di una libreria 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]>

I programmi C ++ sono costruiti in due fasi

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

La libreria statica (.lib) è solo un insieme 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 costruita correttamente ), hai creato un programma completo che tutti i consumatori possono utilizzare, indipendentemente dal compilatore che stanno utilizzando. Esistono tuttavia alcune restrizioni sull'esportazione da una dll, se si desidera la compatibilità tra compilatori.

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.

Inferno DLL è quando probabilmente DOVREI AVERE usato una lib statica, ma invece hai usato una dll, e non tutti gli ex sono compatibili con essa.

Una libreria statica viene compilata nel client. Un file .lib viene utilizzato al momento della compilazione e il contenuto della libreria diventa parte dell'eseguibile consumabile.

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.

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.

Dovresti usare una DLL quando vuoi essere in grado di cambiare la funzionalità fornita dalla libreria senza dover ricollegare l'eseguibile (basta sostituire il file DLL, senza dover sostituire il file eseguibile).

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

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 ciò che definisce "oggetti condivisi dinamici" (DSO). Si concentra maggiormente sulle librerie condivise nel formato binario ELF , ma alcune discussioni sono adatte per le DLL di Windows come bene.

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 .

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 è il collegamento richiederà abbastanza tempo che il compilatore deve mordere il proiettile e farlo in anticipo, oppure il linker dinamico può farlo al momento del caricamento.

Se la tua libreria sarà condivisa tra più 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.

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 puoi vivere con la libreria statica, vai con la libreria statica.

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.

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.

Nel nostro progetto usiamo molte DLL (> 100). 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 sottoposte a versione, poiché Windows carica i moduli in base all'unicità dei nomi. In caso contrario, i componenti scritti propri otterrebbero 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 questo non funzionerà 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?

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

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

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