Support des objets partagés dynamiques (DSO) - Serveur Apache HTTP

Serveur Apache HTTP Version 2.0

Apache > Serveur HTTP > Documentation > Version 2.0

# Support des objets partagés dynamiques (DSO)

Langues Disponibles: en | es | fr | ja | ko

Le serveur HTTP Apache est un programme modulaire permettant à l'administrateur de choisir les fonctionnalités qu'il souhaite activer, au moyen de modules. Les modules peuvent être intégrés dans le programme binaire `httpd` au moment de la compilation. Il est également possible de compiler à part des modules en tant qu'objets dynamiques partagés (Dynamic Shared Objects : DSOs) existant séparément du fichier binaire principal `httpd`. Les modules DSO peuvent être compilés en même temps que le serveur, ou après, au moyen de l'outil Apache pour les extensions (`apxs`).

Ce document décrit les principes de fonctionnement des modules DSO, et montre comment les utiliser.

## Implémentation

Modules Apparentés | Directives Apparentées
--- | ---
`mod_so` | `LoadModule`

Le support DSO servant à charger des modules Apache, est lui-même codé dans un module, nommé `mod_so`, qui doit être compilé dans le noyau d'Apache. Ce module, ainsi que le module `core`, sont les deux seuls modules qui ne peuvent être compilés séparément d'Apache. En pratique, tous les autres modules d'Apache peuvent être compilés en tant que modules DSO, en passant au script `configure` l'option `--enable-module=shared`, comme précisé dans la documentation d'installation. Après qu'un module ait été compilé en DSO (nommé `mod_monmodule.so`), il est possible d'utiliser la directive de `mod_so` : `LoadModule` dans le fichier `httpd.conf`, afin qu'Apache charge ledit module au démarrage ou redémarrage du serveur.

Afin de simplifier la création de fichiers DSO pour les modules Apache (et en particulier les modules tiers), un nouveau programme de support a été ajouté : apxs (APache eXtenSion). Ce programme peut être utilisé pour créer des modules DSO en se passant de l'arborescence source d'Apache. L'idée en est simple : lors de l'installation d'Apache, la commande `make install` positionne les fichiers d'en-têtes C d'Apache, ainsi que les options du compilateur et les options propres à la plate-forme dans le programme `apxs`. Ceci permet à l'utilisateur de compiler ses modules Apache, au moyen de `apxs`, sans disposer de l'arborescence source d'Apache et sans devoir manipuler les options de compilation ou les options propres à sa plate-forme.

## Résumé sur l'utilisation des DSO

Voici un résumé bref des fonctionnalités DSO d'Apache 2.0 :

1. Pour compiler et installer un module Apache distribué avec Apache, par exemple `mod_foo.c`, en tant que DSO, sous le nom `mod_foo.so` :

   `$ ./configure --prefix=/path/to/install --enable-foo=shared
   $ make install`

2. Pour compiler et installer un module Apache fourni par un tiers, par exemple `mod_foo.c`, en tant que DSO, sous le nom `mod_foo.so` :

   `$ ./configure --add-module=module_type:/chemin/vers/le/tiers/mod_foo.c --enable-foo=shared
   $ make install`

3. Pour configurer Apache afin qu'il puisse accepter les modules DSO :

   `$ ./configure --enable-so
   $ make install`

4. Pour compiler et installer un module Apache fourni par un tiers, par exemple `mod_foo.c`, en tant que DSO, et sans disposer de l'arborescence source d'Apache (utilisation d'apxs) :

   `$ cd /chemin/vers/le/tiers
   $ apxs -c mod_foo.c
   $ apxs -i -a -n foo mod_foo.la`

Dans tous les cas, une fois qu'un module a été compilé en tant que DSO, vous devrez utiliser la directive `LoadModule` dans le fichier `httpd.conf` afin qu'Apache active le module.

## Contexte

Sur les systèmes récents, dérivés d'Unix, il existe un procédé élégant, habituellement appelé chargement dynamique d'objets partagés DSO, permettant de compiler un morceau de code sous un format spécial, et de pouvoir le charger en temps réel dans l'espace d'adressage d'un programme exécutable.

Ce chargement peut être réalisé de deux manières : automatiquement, grâce à un programme système nommé `ld.so` lors du démarrage d'un exécutable, ou manuellement depuis un programme en exécution via une interface programmée au moyen des appels systèmes `dlopen()/dlsym()` du "chargeur" Unix

Dans le premier cas, il est courant d'appeler les DSO des bibliothèques partagées ou des bibliothèques DSO ; on les nomme `libfoo.so` ou `libfoo.so.1.2`. Elles sont toutes placées dans un répertoire système (souvent `/usr/lib`) et sont liées par les programmes exécutables lors de la compilation de ces derniers, en précisant au moment de la compilation l'option `-lfoo` à la commande de link (linker command). Cette manière de procéder insère les références des bibliothèques dans le coeur des programmes, afin qu'au moment du démarrage du programme, le "chargeur" Unix puisse trouver `libfoo.so` dans `/usr/lib`, ou bien dans les chemins codés en dur au moyen de l'option de link `-R`, ou dans un chemin configuré au moyen de la variable d'environnement `LD_LIBRARY_PATH`. Tous les symboles non résolus présents dans le programme sont alors résolus au moyen de DSO.

Les symboles propres au programme exécutable ne sont généralement pas référencés par le DSO (puisque c'est une bibliothèque de code générique), et donc aucune résolution ne doit être suivie au delà de ce point. Le programme exécutable n'a pas de travail particulier à faire pour résoudre les symboles des DSO, puisque c'est le "chargeur" Unix qui s'occupe de cette tâche. (En réalité, le code utilisé pour invoquer `ld.so` fait partie du code de démarrage run-time, qui est lié à chaque programme exécutable non statique). L'avantage du chargement dynamique des bibliothèques de code générique est évident : le code n'est conservé qu'à un seul endroit du disque, dans une bibliothèque système comme `libc.so`, ce qui permet de gagner de l'espace disque pour chaque programme.

Dans le second cas, les DSO sont appelés objets partagés ou fichiers DSO et on peut leur attribuer une extension au choix (bien que leur nom soit habituellement `foo.so`). Ces fichiers résident normalement dans un répertoire propre au programme qui les utilise, et ils ne sont pas liés de manière automatique au programme qui les appelle. Celui-ci les charge en temps réel lors de son exécution, au moyen de `dlopen()`. À cet instant, aucune résolution des symboles du DSO n'est réalisée. C'est le "chargeur" Unix qui réalise la tâche de résoudre les symboles non résolus du DSO, à partir du jeu de symboles exportés par le programme et ses bibliothèques DSO (en particulier, tous les symboles de l'omniprésente `libc.so`). Ainsi, le DSO gagne la connaissance des symboles du programme exécutable, comme s'il lui avait été lié statiquement au départ.

Enfin, pour tirer parti de l'API DSO, l'exécutable doit résoudre les symboles propres au DSO via `dlsym()`, pour les utiliser plus tard dans les tables de répartition (NdT : "dispatch tables"), etc. En d'autres termes, le programme exécutable doit résoudre lui-même chaque symbole pour utiliser chacun d'entre eux. L'avantage de ce mécanisme est que les parties optionnelles d'un programme ne sont pas chargées (et donc, n'encombrent pas la mémoire) avant que le programme n'en ait effectivement besoin. Quand elles deviennent nécessaires, ces parties du programme peuvent être chargées dynamiquement pour étendre les fonctionnalités du programme.

Bien que ce fonctionnement de DSO puisse paraître simple à comprendre, il existe au moins une difficulté d'implémentation : permettre au DSO de résoudre les symboles du programme quand un DSO est utilisé pour étendre un programme. Pourquoi cela ? Parce que la "résolution à l'envers" des symboles DSO à partir des symboles du programme exécutable est contraire au principe de conception des bibliothèques (où, rappelons-le, la bibliothèque ne sait rien du programme qui l'utilise) ; cette "résolution à l'envers" n'est pas standardisée, et n'existe pas sur toutes les plates-formes. En pratique, les symboles globaux d'un programme exécutable ne sont que rarement réexportés vers un DSO, et donc ne sont pas accessibles. Celui qui veut pouvoir étendre les fonctionnalités d'un programme dynamiquement, lors de l'exécution, doit trouver un moyen de forcer le programme de liaison à exporter tous les symboles globaux de ce programme.

L'approche par bibliothèques partagées est de loin la plus courante parce que c'est celle pour laquelle les mécanismes DSO ont été conçus ; elle est donc utilisée par presque toutes les bibliothèques du système d'exploitation. De l'autre coté, l'utilisation des objets partagés reste une approche marginale.

Depuis 1998, seules quelques solutions logiciels existantes utilisent le mécanisme des DSO pour étendre leurs fonctionnalités en cours exécution : Perl 5 (via son "XS mechanism" et le module DynaLoader), Netscape Server, etc. Depuis la version 1.3, Apache a rejoint ce groupe, car Apache utilise une approche modulaire pour étendre ses fonctionnalités, et utilise de manière interne des mécanismes de répartition par liste pour lier des modules externes à son noyau. Apache était vraiment prédestiné, par concept, à utiliser les DSO pour charger ses modules en temps réel.

## Avantages et Inconvénients

Les possibilités des DSO décrites ci-avant présentent les avantages suivants :

- Le paquetage du serveur est plus flexible lors de son exécution, car les processus du serveur central peuvent être étendus pendant son exécution, au moyen de la directive `LoadModule`, dans `httpd.conf`, plutôt que forcer les utilisateurs à recompiler le serveur pour modifier ses fonctionnalités. Par exemple, ce mode de fonctionnement permet de faire tourner plusieurs instances du serveur (version standard & SSL version, version minimaliste & étendue [mod_perl, PHP3], etc.) au moyen d'une seule installation d'Apache.
- Il est très facile d'étendre les fonctionnalités du serveur de base, même après son installation. Ceci est d'un grand secours aux mainteneurs des paquets qui peuvent facilement créer des paquets pour l'installation de base d'Apache et d'autres paquets différents pour les extensions, comme PHP3, mod_perl, mod_fastcgi, etc.
- Facilité de développement des modules Apache ; grâce aux outils DSO/`apxs`, ce travail est faisable sans l'arborescence source d'Apache, et ne nécessite qu'une commande `apxs -i` suivi d'un `apachectl restart` pour ajouter au serveur déjà en marche les fonctionnalités du module développé.

Les inconvénients liés à l'utilisation des DSO :

- Les mécanismes de DSO ne sont pas portables sur toutes les plates-formes, car tous les systèmes d'exploitation ne supportent pas le chargement dynamique de code dans l'espace d'adressage d'un programme en marche.
- Le serveur est à peu près 20% plus lent au démarrage, à cause de la charge prise par le "chargeur" Unix de la résolution des symboles.
- Le serveur est à peu près 5% plus lent en fonctionnement sur certaines plates-formes parce que le code indépendant de la position ("position independent code" - PIC) requiert parfois des tours de passe-passe en assembleur pour l'adressage relatif, ce qui n'est pas toujours aussi rapide que l'adressage absolu.
- Les modules DSO ne pouvant pas être liés à d'autres bibliothèques DSO (`ld -lfoo`) sur toutes les plates-formes (par exemple, les plates-formes basées sur a.out ne le permettent pas, alors que celles basées sur ELF le permettent), il n'est pas possible d'utiliser les mécanismes de DSO pour tous les modules. En d'autres termes, les modules compilés en tant que fichiers DSO sont limités à l'utilisation des symboles exportés par le noyau d'Apache, par la bibliothèque C (`libc`) et toute autre bibliothèque dynamique ou statique utilisée par le noyau d'Apache, ou par des archives de bibliothèques statiques (`libfoo.a`) qui contiennent du code indépendant de la position. Les seuls moyens d'utiliser du code à l'extérieur d'un fichier DSO sont, soit de s'assurer que le noyau d'Apache contienne une référence vers ce code, soit de charger ce code au moyen de `dlopen()`.

Langues Disponibles: en | es | fr | ja | ko