Question

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

Était-ce utile?

La solution

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 que vous avez compilé est la version du code qui sera exécuté.

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 autre que celle fournie avec votre code si est considéré comme compatible binaire avec la version d'origine.

En outre, 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 par des composants utilisant la même bibliothèque (charges de données multiples, chargement d'un code).

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

Autres conseils

D'autres ont bien expliqué ce qu'est une bibliothèque statique, mais j'aimerais souligner certaines des mises en garde liées à l'utilisation de bibliothèques statiques, du moins sous Windows:

  • Singletons: si un élément doit être global / statique et unique, veillez à ne pas le placer dans une bibliothèque statique. Si plusieurs DLL sont liées à cette bibliothèque statique, elles recevront chacune leur propre copie du singleton. Toutefois, si votre application est un seul fichier EXE sans DLL personnalisée, cela ne pose aucun problème.

  • Suppression du code non référencé: Lorsque vous créez un lien avec une bibliothèque statique, seules les parties de la bibliothèque statique référencées par votre DLL / EXE sont liées dans votre DLL / EXE.

    Par exemple, si mylib.lib contient a.obj et b.obj et que votre DLL / EXE ne référence que des fonctions ou des variables à partir de a.obj , l'intégralité de b.obj sera ignorée par l'éditeur de liens. Si b.obj contient des objets globaux / statiques, leurs constructeurs et leurs destructeurs ne seront pas exécutés. Si ces constructeurs / destructeurs ont des effets secondaires, vous serez peut-ê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 ceci dans la programmation intégrée (d'accord, pas Windows) serait un gestionnaire d'interruptions marqué comme étant à une adresse spécifique. Vous devez également marquer le gestionnaire d’interruptions en tant que point d’entrée pour vous assurer qu’il ne sera pas ignoré.

    Une autre conséquence de cette situation 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 de l'éditeur de liens tant que vous ne référencerez pas 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 pouvez définir un fichier PDB distinct pour chaque bibliothèque statique ou définir les symboles de débogage dans les fichiers objet afin qu'ils soient intégrés dans le fichier PDB. la DLL / EXE. La documentation de 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 à plusieurs DLL. Si votre programme suppose que type_info est & sing; singleton " data et utilise & typeid () ou type_info :: before () , vous pouvez obtenir des résultats indésirables et surprenants.

Une bibliothèque 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 passé dans ce code. Une dll peut être utilisée par plusieurs applications et chargée dans plusieurs processus, tout en ne conservant qu'une seule copie du code sur le disque dur.

Dll pros : peuvent être utilisés pour réutiliser / partager du code entre plusieurs produits; charge dans la mémoire du processus à la demande et peut être déchargé lorsqu'il n'est pas nécessaire; peut être mis à niveau indépendamment du reste du programme.

Inconvénients des DLL : impact sur les performances du chargement de la DLL et de la modification du code; Problèmes de versioning ("dll hell")

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

Contre les lib : 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 seule grande bibliothèque binaire vs dynamique qui permet le partage de code entre plusieurs exécutables différents), il existe également des implications légales .

>

Par exemple, si vous utilisez un code sous licence LGPL et que vous établissez un lien statique avec une bibliothèque LGPL (et créez ainsi un gros fichier binaire), votre code devient automatiquement Open Sourced ( gratuit (en liberté) code LGPL. Si vous vous associez à des objets partagés, il ne reste plus qu'à LGPL les améliorations / corrections de bogues apportées à la bibliothèque LGPL elle-même.

Cela devient un problème beaucoup plus important si vous décidez par exemple de compiler vos applications mobiles (sous Android, vous avez le choix entre statique et dynamique, ce qui n’est pas le cas pour iOS - 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éer 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]>

Les programmes C ++ sont construits en deux phases

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

La bibliothèque statique (.lib) est juste un paquet de fichiers .obj et n'est donc pas un programme complet. Il n'a pas franchi la deuxième phase (de liaison) de la construction d'un programme. Les dlls, en revanche, sont comme des exes et sont donc des programmes complets.

Si vous construisez une bibliothèque statique, celle-ci n'est pas encore liée et les utilisateurs de votre bibliothèque statique devront donc 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 l'avez construite correctement ), vous avez construit un programme complet que tous les consommateurs peuvent utiliser, quel que soit le compilateur utilisé. Il existe cependant plusieurs restrictions lors de l’exportation depuis une dll, si la compatibilité entre compilateurs croisés est souhaitée.

Vous devez bien réfléchir aux changements dans le temps, aux versions, à la stabilité, à la compatibilité, etc.

.

Si deux applications utilisent le code partagé, voulez-vous les forcer à changer ensemble, au cas où elles auraient besoin d'être compatibles l'une avec l'autre? Ensuite, utilisez la DLL. Tous les exe utiliseront le même code.

Ou voulez-vous les isoler les uns des autres afin de pouvoir en changer un et avoir la certitude de ne pas avoir cassé l'autre? Ensuite, utilisez la bibliothèque statique.

L’enfer de la DLL, c’est quand vous devriez probablement avoir utilisé une bibliothèque statique, mais vous avez utilisé une dll à la place, et tous les exes ne sont pas compatibles avec elle.

Une bibliothèque statique est compilée dans le client. Une .lib est utilisée au moment de la compilation et le contenu de la bibliothèque devient une partie de l’exécutable consommateur.

Une bibliothèque dynamique est chargée à l'exécution et n'est pas compilée dans l'exécutable du client. Les bibliothèques dynamiques sont plus flexibles car plusieurs exécutables clients peuvent charger une DLL et utiliser ses fonctionnalités. Cela permet également de réduire au minimum la taille globale et la maintenabilité de votre code client.

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 passe. 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 souhaitez pouvoir modifier les fonctionnalités fournies par la bibliothèque sans avoir à relier l'exécutable (remplacez simplement le fichier DLL, sans avoir à remplacer le fichier exécutable).

Vous utiliseriez 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 " C'est également une bonne ressource qui explique comment tirer le meilleur parti des bibliothèques partagées, ou ce qu'il appelle "objets partagés dynamiques". (DSOs). Il se concentre davantage sur les bibliothèques partagées au format ELF , mais certaines discussions conviennent aux DLL Windows bien.

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

Il englobe tous les avantages, notamment celui de pouvoir insérer des bibliothèques interposées. Vous trouverez plus de détails sur l'interposition dans cet article ici .

En réalité, le compromis que vous faites (dans un grand projet) réside dans le temps de chargement initial, les bibliothèques vont être reliées à un moment ou à un autre, la décision à prendre est la suivante: le lien durera-t-il assez longtemps pour que le compilateur doit mordre la balle et le faire à l’avance, ou l’éditeur de liens dynamique peut le faire au moment du chargement.

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, rendez-le définitivement statique.

Il y a plusieurs inconvénients à utiliser une dll. Il y a des frais généraux supplémentaires pour le chargement et le déchargement. Il y a aussi une dépendance supplémentaire. Si vous changez la dll pour la rendre incompatible avec votre exécutalbes, ils cesseront de fonctionner. Par contre, 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 de la liaison, le code est lié à votre exécutable. Cela rend votre exécutable plus grand (que si vous utilisiez 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 déterminer si l’accès partagé au code de la bibliothèque est sûr, l’adresse de chargement préférée, entre autres.

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

Les bibliothèques statiques sont des archives contenant le code objet de la bibliothèque. Lorsque ce dernier est lié à 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 cherche dans certains répertoires la (les) bibliothèque (s) 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 uses où le lieur peut la trouver. La liaison statique élimine ce problème mais génère un fichier exécutable plus volumineux.

Si vous travaillez uniquement sur des projets incorporés ou sur des plates-formes spécialisées, les bibliothèques statiques sont la seule solution, mais elles sont souvent moins fastidieuses à compiler dans votre application. De plus, avoir des projets et des makefiles qui incluent tout rend la vie plus heureuse.

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

Une meilleure configuration consistait peut-être à faire de tout une bibliothèque statique (vous ne disposez donc que d'un seul exécutable). Cela ne fonctionne que si aucune duplication de code n'a lieu. Un test semble confirmer cette hypothèse, mais je n’ai pas pu trouver de devis MSDN officiel. Donc par exemple faire 1 exe avec:

  • exe utilise shared_lib1, shared_lib2
  • shared_lib1 utilise shared_lib2
  • shared_lib2

Le code et les variables de shared_lib2 ne doivent figurer qu'une seule fois dans l'exécutable fusionné final. Quelqu'un peut-il soutenir cette question?

Je donnerais comme règle générale que si vous avez une grande base de code, toutes construites au-dessus de bibliothèques de niveau inférieur (par exemple, un framework Utils ou Gui), que vous souhaitez partitionner en bibliothèques plus faciles à gérer, puis les rendre statiques bibliothèques. Les bibliothèques dynamiques ne vous achètent pas vraiment 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), envisagez de la transformer en une DLL. Si la bibliothèque est LGPL, vous devrez peut-être quand même utiliser une dll en raison des conditions de licence.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top