Quand utiliser les bibliothèques dynamiques et statiques
Lors de la création d'une bibliothèque de classes en C++, vous pouvez choisir entre dynamic (.dll) et statique (.lib) des bibliothèques. Quelle est la différence entre eux et quand est-il approprié à utiliser?
18 réponses
Les bibliothèques statiques augmentent la taille du code dans votre binaire. Ils sont toujours chargés et quelle que soit la version du code avec laquelle vous avez compilé est la version du code qui s'exécutera.
Les bibliothèques dynamiques sont stockées et versionnées séparément. Il est possible qu'une version de la Bibliothèque dynamique soit chargée qui n'était pas celle d'origine livrée avec votre code Si la mise à jour est considérée comme compatible binaire avec la version originale.
Bibliothèques dynamiques supplémentaires ne sont pas nécessairement chargés - ils sont généralement chargés lors de leur premier appel - et peuvent être partagés entre les composants qui utilisent la même bibliothèque (plusieurs charges de données, un chargement de code).
Les bibliothèques dynamiques étaient considérées comme la meilleure approche la plupart du temps, mais à l'origine, elles avaient un défaut majeur (Google DLL hell), qui a presque été éliminé par les systèmes D'exploitation Windows plus récents (Windows XP en particulier).
D'autres ont expliqué de manière adéquate ce qu'est une bibliothèque statique, mais je voudrais souligner certaines des mises en garde de l'utilisation de bibliothèques statiques, au moins sur Windows:
Singletons: {[16] } si quelque chose doit être global/statique et unique, Faites très attention à le mettre dans une bibliothèque statique. Si plusieurs DLL sont liées à cette bibliothèque statique, elles obtiendront chacune leur propre copie du singleton. Cependant, si votre application est un EXE unique sans DLL personnalisées, ceci ne peut pas être un problème.
-
Suppression de code non référencé: lorsque vous liez une bibliothèque statique, seules les parties de la bibliothèque statique référencées par votre DLL / EXE seront liées dans votre DLL / EXE.
Par exemple, si
mylib.lib
contienta.obj
etb.obj
et que votre DLL / EXE ne fait référence qu'aux fonctions ou variables dea.obj
, l'intégralité deb.obj
sera supprimée par l'éditeur de liens. Sib.obj
contient des objets globaux/statiques, leurs constructeurs et destructeurs ne sera exécutée. Si ces constructeurs / destructeurs ont des effets secondaires, vous pourriez être déçu par leur absence.De même, si la bibliothèque statique contient des points d'entrée Spéciaux, vous devrez peut-être veiller à ce qu'ils soient réellement inclus. Un exemple de ceci dans la programmation embarquée (OK, Pas Windows) serait un gestionnaire d'interruption qui est marqué comme étant à une adresse spécifique. Vous devez également marquer le gestionnaire d'interruption comme un point d'entrée pour vous assurer qu'il ne reçoit pas jeter.
Une autre conséquence de ceci est qu'une bibliothèque statique peut contenir des fichiers objet qui sont complètement inutilisables en raison de références non résolues, mais elle ne provoquera pas d'erreur d'éditeur de liens tant que vous n'aurez pas référencé une fonction ou une variable à partir de ces fichiers objet. Cela peut se produire longtemps après l'écriture de la bibliothèque.
Symboles de débogage: Vous voudrez peut-être un PDB distinct pour chaque bibliothèque statique, ou vous voudrez peut-être que les symboles de débogage soient placés dans les fichiers objet afin qu'ils obtenez roulé dans le PDB pour la DLL / EXE. La documentation Visual C++ explique les options nécessaires .
RTTI: Vous pouvez vous retrouver avec plusieurs objets
type_info
pour la même classe si vous liez une seule bibliothèque statique en plusieurs DLL. Si votre programme suppose quetype_info
est des données "singleton" et utilise&typeid()
outype_info::before()
, Vous pouvez obtenir des résultats indésirables et surprenants.
Une lib est une unité de code qui est fournie dans l'exécutable de votre application.
Une dll est Une unité autonome de code exécutable. Il est chargé dans le processus que lorsqu'un appel est effectué dans le code. Une dll peut être utilisée par plusieurs applications et chargée dans plusieurs processus, tout en n'ayant qu'une seule copie du code sur le disque dur.
Dll pros : peut être utilisé pour réutiliser / partager du code entre plusieurs produits; charger dans la mémoire de processus à la demande et peut être déchargé lorsqu'il n'est pas nécessaire; peut être mis à jour indépendamment du reste du programme.
Dll cons : impact sur les performances du chargement de la dll et du rebasage du code; problèmes de gestion des versions ("dll hell")
Lib pros : aucun impact sur les performances car le code est toujours chargé dans le processus et n'est pas rebasé; pas de problèmes de gestion des versions.
Lib cons : exécutable / processus "bloat" - tout le code est dans votre exécutable et est chargé au démarrage du processus; pas de réutilisation / partage-chaque produit a sa propre copie du code.
Outre les implications techniques des bibliothèques statiques vs dynamiques (les fichiers statiques regroupent tout dans une grande bibliothèque binaire vs dynamique qui permet le partage de code entre plusieurs exécutables différents), il y a les implications juridiques .
Par exemple, si vous utilisez du code sous licence LGPL et que vous liez statiquement une bibliothèque LGPL (et créez ainsi un grand binaire), votre code devient automatiquement Open Source (libre comme dans freedom) code LGPL. Si vous liez contre a objets partagés, alors vous avez seulement besoin de LGPL les améliorations / corrections de bugs que vous faites à la bibliothèque LGPL elle-même.
Cela devient un problème beaucoup plus important si vous décidez comment vous compiler des applications mobiles par exemple (dans Android, vous avez le choix entre statique et dynamique, dans iOS, vous ne le faites pas - il est toujours statique).
Création d'une bibliothèque statique
$$:~/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]>
Création d'une bibliothèque dynamique
$$:~/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]>
Vous devriez réfléchir soigneusement aux changements au fil du temps, à la gestion des versions, à la stabilité, à la compatibilité, etc.
S'il y a deux applications qui utilisent le code partagé, voulez-vous forcer ces applications à changer ensemble, au cas où elles auraient besoin d'être compatibles les unes avec les autres? Ensuite, utilisez la dll. Tous les exe utiliseront le même code.
Ou voulez-vous les isoler les uns des autres, afin que vous puissiez en changer un et être sûr de ne pas avoir brisé l'autre. Ensuite, utilisez la statique lib.
DLL l'enfer est quand vous auriez probablement dû utiliser une lib statique, mais vous avez utilisé une dll à la place, et tous les exes ne sont pas compatibles avec elle.
Les programmes C++ sont construits en deux phases
- Compilation-produit le code objet (.obj)
- Reliant produit du code exécutable (.exe ou .dll)
Bibliothèque Statique (.lib) est juste un paquet de .obj fichiers et n'est donc pas un programme complet. Il n'a pas subi la deuxième phase (liaison) de la construction d'un programme. Dlls, d'autre part, sont comme exe et sont donc des programmes complets.
Si vous construisez une bibliothèque statique, elle n'est pas encore liée et donc les consommateurs de votre bibliothèque statique devrez utiliser le même compilateur que vous avez utilisé (si vous avez utilisé g++, ils auront à utiliser g++).
Si, au contraire, vous avez construit une dll (et construit correctement), vous avez construit un programme complet que tous les consommateurs peuvent utiliser, n'importe quel compilateur qu'ils utilisent. Il y a cependant plusieurs restrictions, sur l'exportation à partir d'une dll, si la compatibilité croisée du compilateur est souhaitée.
Une bibliothèque statique est compilée dans le client. Un .lib est utilisé au moment de la compilation et le contenu de la bibliothèque fait partie de l'exécutable consommateur.
Une bibliothèque dynamique est chargée au moment de l'exécution et n'est pas compilée dans l'exécutable client. Les bibliothèques dynamiques sont plus flexibles car plusieurs exécutables clients peuvent charger une DLL et utiliser ses fonctionnalités. Cela réduit également la taille globale et la maintenabilité de votre code client au minimum.
Une bibliothèque statique doit être liée à l'exécutable final; elle devient une partie de l'exécutable et la suit partout où elle va. Une bibliothèque dynamique est chargée chaque fois que l'exécutable est exécuté et reste séparée de l'exécutable en tant que fichier DLL.
Vous utiliseriez une DLL lorsque vous voulez pouvoir modifier les fonctionnalités fournies par la bibliothèque sans avoir à re-lier l'exécutable (il suffit de remplacer le fichier DLL, sans avoir à remplacer le fichier exécutable).
Vous utilisez une bibliothèque statique à chaque fois que vous n'avez pas de raison d'utiliser une bibliothèque dynamique.
L'article D'Ulrich Drepper sur " Comment écrire des bibliothèques partagées " est également une bonne ressource qui détaille la meilleure façon de tirer parti des bibliothèques partagées, ou ce qu'il appelle des "objets partagés dynamiques" (OSD). Il se concentre davantage sur les bibliothèques partagées au format binaire ELF, mais certaines discussions conviennent également aux DLL Windows.
Pour une excellente discussion sur ce sujet, lisez Cet article de Sun.
Il va dans tous les avantages, y compris être capable d'insérer des bibliothèques interposantes. Plus de détails sur l'interposition peuvent être trouvés dans Cet article ici .
Vraiment le compromis que vous faites (dans un grand projet) est dans le temps de chargement initial, les bibliothèques vont être liées à un moment ou à un autre, la décision à prendre est que le lien prendra assez de temps pour que le compilateur mord la balle et le fasse à l'avant, ou l'éditeur de liens dynamique peut-il le
Si votre bibliothèque doit être partagée entre plusieurs exécutables, il est souvent logique de la rendre dynamique pour réduire la taille des exécutables. Sinon, assurez-le définitivement statique.
L'utilisation d'une dll présente plusieurs inconvénients. Il y a des frais supplémentaires pour le charger et le décharger. Il y a aussi une dépendance. Si vous modifiez la dll pour la rendre incompatible avec votre executalbes, ils cesseront de fonctionner. D'autre part, si vous modifiez une bibliothèque statique, vos exécutables compilés utilisant l'ancienne version ne seront pas affectés.
Si la Bibliothèque est statique, alors au moment du lien, le code est lié à votre exécutable. Cela rend votre exécutable plus grand (que si vous avez suivi la route dynamique).
Si la Bibliothèque est dynamique, au moment du lien, les références aux méthodes requises sont intégrées à votre exécutable. Cela signifie que vous devez expédier votre exécutable et la Bibliothèque dynamique. Vous devez également considérer si l'accès partagé au code dans la Bibliothèque est sûr, l'adresse de chargement préférée entre autres truc.
Si vous pouvez vivre avec la bibliothèque statique, aller à la bibliothèque statique.
Les bibliothèques statiques sont des archives qui contiennent le code objet de la bibliothèque, lorsqu'elles sont liées dans une application, ce code est compilé dans l'exécutable. Les bibliothèques partagées sont différentes en ce sens qu'elles ne sont pas compilées dans l'exécutable. Au lieu de cela, l'éditeur de liens dynamique recherche certains répertoires à la recherche de la ou des bibliothèques dont il a besoin, puis les charge en mémoire. Plus d'un exécutable peut utiliser la même bibliothèque partagée en même temps, réduisant ainsi l'utilisation de la mémoire et la taille de l'exécutable. Cependant, il y sont alors plus de fichiers à distribuer avec l'exécutable. Vous devez vous assurer que la Bibliothèque est installée sur le système uses quelque part où l'éditeur de liens peut le trouver, la liaison statique élimine ce problème mais entraîne un fichier exécutable plus volumineux.
Si vous travaillez sur des projets embarqués ou des plates-formes spécialisées, les bibliothèques statiques sont la seule solution, elles sont souvent moins compliquées à compiler dans votre application. Avoir aussi des projets et makefile qui incluent tout rend la vie plus heureuse.
Nous utilisons beaucoup de DLL (> 100) dans notre projet. Ces DLL ont des dépendances les unes sur les autres et nous avons donc choisi la configuration de la liaison dynamique. Cependant, il présente les inconvénients suivants:
- démarrage lent (> 10 secondes)
- les DLL ont dû être versionnées, car windows charge les modules sur l'unicité des noms. Les composants propres écrits obtiendraient autrement la mauvaise version de la DLL (c'est-à-dire celle déjà chargée au lieu de son propre ensemble distribué)
- optimiseur ne peut optimiser dans les limites de DLL. Par exemple, l'optimiseur essaie de placer les données et le code fréquemment utilisés l'un à côté de l'autre, mais cela ne fonctionnera pas au-delà des limites de la DLL
Peut-être qu'une meilleure configuration était de faire de everything une bibliothèque statique (et donc vous n'avez qu'un exécutable). Cela ne fonctionne que si aucune duplication de code n'a lieu. Un test semble soutenir cette hypothèse, mais je n'ai pas trouvé de citation officielle MSDN. Donc, par exemple, faites 1 exe avec:
- exe utilise shared_lib1, shared_lib2
- shared_lib1 utiliser shared_lib2
- shared_lib2
Le code et les variables de shared_lib2 ne doivent être présents qu'une seule fois dans l'exécutable final fusionné. Quelqu'un peut-il soutenir cette question?
Je donnerais une règle générale que si vous avez une grande base de code, tous construits au-dessus des bibliothèques de niveau inférieur (par exemple un framework Utils ou gui), que vous voulez partitionner dans des bibliothèques plus gérables, puis en faire des bibliothèques statiques. Les bibliothèques dynamiques ne vous achètent vraiment rien et il y a moins de surprises-il n'y aura qu'une seule instance de singletons par exemple.
Si vous avez une bibliothèque entièrement séparée du reste de la base de code (par exemple une bibliothèque tierce) ensuite, envisagez d'en faire une dll. Si la Bibliothèque est LGPL, vous devrez peut-être utiliser une dll de toute façon en raison des conditions de licence.