Quand utiliser des bibliothèques dynamiques ou statiques

437

Lors de la création d'une bibliothèque de classes en C ++, vous pouvez choisir entre les bibliothèques dynamiques ( .dll, .so) et statiques ( .lib, .a). Quelle est la différence entre eux et quand est-il approprié de les utiliser?

Morten Christiansen
la source
2
Il convient de noter qu'il existe également quelque chose qui s'appelle "Import Library" check stackoverflow.com/questions/3573475/…
Wakan Tanka

Réponses:

299

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é, c'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 de charger une version de la bibliothèque dynamique qui n'était pas celle d'origine livrée avec votre code si la mise à jour est considérée comme binaire compatible avec la version d'origine.

De plus, les bibliothèques dynamiques ne sont pas nécessairement chargées - elles sont généralement chargées lors du premier appel - et peuvent être partagées entre les composants qui utilisent la même bibliothèque (plusieurs chargements de données, un seul chargement de code).

Les bibliothèques dynamiques étaient considérées comme la meilleure approche la plupart du temps, mais à l'origine, elles présentaient un défaut majeur (enfer de DLL Google), qui a pratiquement été éliminé par les systèmes d'exploitation Windows plus récents (Windows XP en particulier).

Orion Adrian
la source
71
Sur Windows / Mac (pas de gestionnaire de paquets), il n'y a vraiment aucune bonne raison d'utiliser des bibliothèques dynamiques sur statique. Étant donné que les DLL Windows ne sont pas déplaçables, le partage de code ne fonctionne souvent pas (et généralement, chaque application est livrée et utilise de toute façon ses propres versions de la bibliothèque). Le seul véritable avantage est qu'il est plus facile de mettre à jour la bibliothèque.
Zifre
5
sur le mac, j'utilise beaucoup de bibliothèques dynamiques. par exemple, mac os x a une intégration sqlite3. j'ai créé un programme qui a une fonction de base de données sqlite3 pour le stockage des performances. cependant, parce qu'il est rarement utilisé, la liaison dynamique économise du temps de compilation, rend les tests plus faciles / plus rapides cependant, si je devais créer une version
ReachConnection
6
@Zifre: relocatable = peut être chargé à différentes adresses virtuelles. DLL prend certainement cela en charge.
dma_k
20
@dma_k: les DLL Windows peuvent être chargées à différentes adresses, mais uniquement parce que l'éditeur de liens copie tout le code et modifie les numéros d'adresse. Avec les objets partagés, toutes les références d'adresse sont relatives, donc plusieurs processus peuvent partager la même mémoire pour l'objet partagé. En d'autres termes, sous Windows, une DLL de 1 Mo utilisée par 3 programmes = 3 Mo. Sous Linux, un MB SO utilisé par 3 programmes = 1 MB.
Zifre
7
Windows et Linux ont tous deux le concept de relocalisation en temps réel des bibliothèques partagées eli.thegreenplace.net/2011/08/25/… La chose la plus importante qui a permis à Code indépendant de position n'était pas quelque chose de spécial pour Linux, mais plutôt un adressage relatif au RIP ajouté avec le jeu d'instructions x64; Windows et Linux peuvent utiliser l'adressage relatif RIP, ce qui réduit le nombre de corrections lors du déplacement des bibliothèques.
clemahieu
194

D'autres ont bien expliqué 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 sous Windows:

  • Singletons: 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. Toutefois, si votre application est un seul EXE sans DLL personnalisée, cela ne peut pas être un problème.

  • Suppression de code non référencé: lorsque vous établissez une liaison avec une bibliothèque statique, seules les parties de la bibliothèque statique référencées par votre DLL / EXE seront liées à votre DLL / EXE.

    Par exemple, si mylib.libcontient a.objet b.objet que votre DLL / EXE ne fait référence qu'à des fonctions ou des variables de a.obj, l'intégralité de b.objsera supprimée par l'éditeur de liens. Si b.objcontient des objets globaux / statiques, leurs constructeurs et destructeurs ne seront pas exécutés. Si ces constructeurs / destructeurs ont des effets secondaires, vous pouvez être déçu de 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 cela dans la programmation intégrée (d'accord, 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 sera pas supprimé.

    Une autre conséquence de cela est qu'une bibliothèque statique peut contenir des fichiers objets qui sont complètement inutilisables en raison de références non résolues, mais cela ne provoquera pas d'erreur de l'éditeur de liens tant que vous ne référencerez pas une fonction ou une variable à partir de ces fichiers objets. Cela peut se produire longtemps après l'écriture de la bibliothèque.

  • Symboles de débogage: vous souhaiterez peut-être une PDB distincte pour chaque bibliothèque statique, ou vous souhaiterez peut-être que les symboles de débogage soient placés dans les fichiers objet afin qu'ils soient roulés dans la PDB pour la DLL / EXE. La documentation de Visual C ++ explique les options nécessaires .

  • RTTI: vous pouvez vous retrouver avec plusieurs type_infoobjets pour la même classe si vous liez une seule bibliothèque statique à plusieurs DLL. Si votre programme suppose que ce type_infosont des données "singleton" et utilise &typeid()ou type_info::before(), vous pouvez obtenir des résultats indésirables et surprenants.

bk1e
la source
23
En ce qui concerne le point sur les singletons, n'oubliez pas qu'une DLL peut être chargée plusieurs fois (même version ou plusieurs versions) et il n'y a toujours pas de garantie singleton.
Orion Adrian
Point supplémentaire sur la suppression de code non référencé: les appels effectués aux DLL nécessitent également un appel réel pour forcer le chargement de la DLL référencée. En l'ajoutant comme référence, mais en n'incluant aucun appel qui y fait référence, vous obtiendrez toujours le même résultat que d'avoir une bibliothèque statique qui n'appelle rien. La seule différence est ce qui est réellement expédié. Dans les deux cas, les constructeurs statiques et les destructeurs ne se déclenchent pas.
Orion Adrian
@ bk1e Cela ne devrait pas se produire. le .a contiendra toujours tous les symboles avec lesquels il a été construit. Lorsqu'il est lié statiquement dans votre application, oui, seuls les symboles utilisés seront liés.
Miles Rout
62

Une lib est une unité de code intégrée à l'exécutable de votre application.

Une dll est une unité autonome de code exécutable. Il n'est chargé dans le processus que lorsqu'un appel est effectué dans ce 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 sur demande et peut être déchargé lorsqu'il n'est pas nécessaire; peut être mis à niveau indépendamment du reste du programme.

Contre la DLL: impact sur les performances du chargement de la DLL et du rebasage du code; problèmes de version ("dll hell")

Avantages de Lib : aucun impact sur les performances car le code est toujours chargé dans le processus et n'est pas rebasé; aucun problème de version.

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.

Franci Penov
la source
La redimensionnement peut également être effectuée au moment de la génération à l'aide de rebase.exe ou en passant l'option / BASE à link.exe. Son efficacité dépend de la présence de conflits d'espace d'adressage inattendus lors de l'exécution.
bk1e
24

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 vous liez statiquement à une bibliothèque LGPL (et créez ainsi un gros binaire), votre code devient automatiquement Open Source ( gratuit comme en toute liberté) code LGPL. Si vous vous liez à des objets partagés, vous n'avez besoin que de LGPL les améliorations / corrections de bogues que vous apportez à la bibliothèque LGPL elle-même.

Cela devient un problème beaucoup plus important si vous décidez comment compiler vos applications mobiles par exemple (dans Android, vous avez le choix entre statique et dynamique, dans iOS vous ne l'avez pas - c'est toujours statique).

rburhum
la source
23

Les programmes C ++ sont construits en deux phases

  1. Compilation - produit du code objet (.obj)
  2. Liaison - produit du code exécutable (.exe ou .dll)

La bibliothèque statique (.lib) n'est qu'un ensemble de fichiers .obj et n'est donc pas un programme complet. Il n'a pas subi la deuxième phase (de liaison) de construction d'un programme. Les DLL, d'autre part, sont comme des exes 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 devront utiliser le même compilateur que vous avez utilisé (si vous avez utilisé g ++, ils devront utiliser g ++).

Si au lieu de cela vous avez construit une DLL (et que vous l'avez construite correctement ), vous avez construit un programme complet que tous les consommateurs peuvent utiliser, quel que soit le compilateur qu'ils utilisent. Il existe cependant plusieurs restrictions, lors de l'exportation à partir d'une DLL, si la compatibilité entre compilateurs croisés est souhaitée.

tcb
la source
1
Ce sont des nouvelles pour moi. Quelles restrictions existe-t-il avec les compilateurs croisés lors de l'utilisation de DLL? Avoir le programmeur construit sans avoir besoin de la même chaîne d'outils semble être un énorme avantage pour les DLL
Dan
1
Cette réponse est informative. Ajout d'une mise en garde mineure: consumers of your static library will have to use the same compiler that you usedsi la bibliothèque statique utilise une bibliothèque C ++, telle que #include <iostream>.
truthadjustr
on ne peut consommer une dll c ++ que si le même compilateur est utilisé (car il n'y a pas d'abi c ++ standard, les symboles sont mutilés de différentes manières). La dll et le module client doivent utiliser le même compilateur et les mêmes paramètres de build
kcris
19

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]>
Vijay
la source
13

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.

Jordan Parmer
la source
13

Vous devez réfléchir attentivement aux changements au fil du temps, au versioning, à 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 devraient être compatibles entre elles? Utilisez ensuite la DLL. Tous les exes utiliseront le même code.

Ou voulez-vous les isoler les uns des autres, afin de pouvoir en changer un et être sûr de ne pas avoir cassé l'autre. Utilisez ensuite la bibliothèque statique.

L'enfer de DLL, c'est quand vous auriez probablement dû utiliser une bibliothèque statique, mais vous avez utilisé une DLL à la place, et tous les exes ne sont pas compatibles avec elle.

Corey Trager
la source
9

Une bibliothèque statique doit être liée à l'exécutable final; il fait partie de l'exécutable et le suit partout où il va. Une bibliothèque dynamique est chargée à chaque exécution de l'exécutable et reste distincte de l'exécutable sous forme de fichier DLL.

Vous utiliseriez une DLL lorsque vous souhaitez pouvoir modifier les fonctionnalités fournies par la bibliothèque sans avoir à lier à nouveau l'exécutable (il suffit de remplacer le fichier DLL, sans avoir à remplacer le fichier exécutable).

Vous utiliseriez une bibliothèque statique chaque fois que vous n'avez aucune raison d'utiliser une bibliothèque dynamique.

spotcatbug
la source
Vous pouvez également utiliser une DLL lorsque plusieurs autres applications utilisent les mêmes fonctionnalités, ce qui peut réduire l'encombrement.
Tim
En outre, l'extension de votre concept initial, l'architecture de "plug-in" dans laquelle vous souhaitez autoriser des fonctionnalités supplémentaires / inconnues ultérieurement sans avoir à reconstruire ou à republier, ne peut être effectuée qu'avec des bibliothèques dynamiques.
Tim
8

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 les "objets partagés dynamiques" (DSO). Il se concentre davantage sur les bibliothèques partagées au format binaire ELF , mais certaines discussions conviennent également aux DLL Windows.

Néant
la source
5

Pour une excellente discussion sur ce sujet, lisez cet article de Sun.

Il rentre dans tous les avantages, y compris la possibilité d'insérer des bibliothèques d'interposition. Plus de détails sur l'interposition peuvent être trouvés dans cet article ici .

Rob Wells
la source
4

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 qui doit être prise est que le lien prendra assez de temps pour que le compilateur ait besoin pour mordre la balle et le faire à l'avant, ou l'éditeur de liens dynamique peut-il le faire au moment du chargement.

pfranza
la source
3

Si votre bibliothèque va être partagée entre plusieurs exécutables, il est souvent judicieux de la rendre dynamique pour réduire la taille des exécutables. Sinon, rendez-le définitivement statique.

Il existe plusieurs inconvénients à utiliser une DLL. Il y a des frais supplémentaires pour le charger et le décharger. Il existe également une dépendance supplémentaire. Si vous modifiez la DLL pour la rendre incompatible avec vos exécutables, ils cesseront de fonctionner. D'un autre côté, si vous changez une bibliothèque statique, vos exécutables compilés utilisant l'ancienne version ne seront pas affectés.

Dima
la source
3

Si la bibliothèque est statique, au moment de la liaison, le code est lié à votre exécutable. Cela rend votre exécutable plus grand (que si vous aviez choisi la route dynamique).

Si la bibliothèque est dynamique, au moment de la liaison, des 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 déterminer si l'accès partagé au code dans la bibliothèque est une adresse de chargement sûre et préférée, entre autres.

Si vous pouvez vivre avec la bibliothèque statique, optez pour la bibliothèque statique.

Seb Rose
la source
3

Nous utilisons beaucoup de DLL (> 100) dans notre projet. Ces DLL ont des dépendances les unes des 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 devaient être versionnées, car Windows charge les modules sur l'unicité des noms. Autrement, les composants écrits obtiendraient la mauvaise version de la DLL (c'est-à-dire celle déjà chargée au lieu de son propre ensemble distribué)
  • L'optimiseur ne peut optimiser que dans les limites des DLL. Par exemple, l'optimiseur essaie de placer les données et le code fréquemment utilisés les uns à côté des autres, mais cela ne fonctionnera pas au-delà des limites des DLL

Peut-être qu'une meilleure configuration était de faire de tout 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 devis MSDN officiel. 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 devraient être présents qu'une seule fois dans l'exécutable fusionné final. Quelqu'un peut-il soutenir cette question?

gast128
la source
N'êtes-vous pas censé utiliser certaines directives de précompilateur d'une manière ou d'une autre pour éviter la duplication de code?
Paceman
La précompilation Afaiac ne fonctionne que sur une base par module (exe / dll / lib). La précompilation est principalement destinée à accélérer la compilation, mais elle empêche également plusieurs inclusions dans une unité de compilation. Cependant, les gardes sont la meilleure façon d'obtenir cet effet.
gast128
2

Les bibliothèques statiques sont des archives qui contiennent le code objet de la bibliothèque, lorsqu'elles sont liées à une application, ce code est compilé dans l'exécutable. Les bibliothèques partagées sont différentes en ce 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 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 a alors plus de fichiers à distribuer avec l'exécutable. Vous devez vous assurer que la bibliothèque est installée sur le système utilise 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.

Terence Simpson
la source
2

Si votre travail sur des projets intégrés ou des bibliothèques statiques de plates-formes spécialisées est la seule façon de procéder, il est également souvent moins compliqué de les compiler dans votre application. Le fait d'avoir des projets et un makefile comprenant tout rend la vie plus heureuse.

Robert Gould
la source
2

Je donnerais une règle générale que si vous avez une grande base de code, toutes construites sur des bibliothèques de niveau inférieur (par exemple un framework Utils ou Gui), que vous souhaitez partitionner en 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 qui est entièrement séparée du reste de la base de code (par exemple une bibliothèque tierce), 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.

the_mandrill
la source