Dynamic Shared Object (DSO) Support

El Servidor HTTP Apache es un programa modular en el que el administrador puede elegir la funcionalidad a incluir en el servidor seleccionando un conjunto de módulos. Los módulos serán compilados como Objetos Dinámicos Compartidos (DSOs) que existen por separado del fichero binario de httpd. Los módulos DSO pueden ser generados en el momento en que el servidor se compila, o pueden compilarse y añadirse posteriormente usando la Herramienta de Extensión de Apache (apxs).

Alternativamente, los módulos se pueden compilar estáticamente en el binario httpd cuando se compila el servidor.

Este documento describe cómo utilizar los módulos DSO, así como la teoría en la que se basan.

Implementación mod_so LoadModule

El soporte DSO para cargar módulos httpd individuales de Apache se basa en un módulo llamado mod_so que debe estar compilado estáticamente en el núcleo de Apache httpd. Es el único módulo además de core que no puede ser puesto en DSO. Prácticamente todos los demás módulos httpd distribuidos de Apache se colocarán en un DSO llamado mod_foo.so puede usar la LoadModule directive de mod_so en su fichero httpd.conf para cargar este módulo al iniciar el servidor o al reiniciar.

Se pueden desactivar las construcciones DSO para módulos individuales con la opción configure --enable-mods-static tal y como se comenta en la documentación de instalación.

Para simplificar esta creación de archivos DSO para módulos httpd de Apache (especialmente para módulos de terceros) un programa de apoyo llamado apxs (APache eXtenSion) está disponible. Se puede usar para generar módulos basados en DSO fuera del árbol de código fuente de Apache httpd. La idea es sencilla: Cuando se instala el Servidor Apache HTTP el procedimiento make install de configure instala los ficheros de cabecera en C de Apache httpd y pone el compilador que depende de la plataforma y los indicadores de enlazador para generar ficheros DSO en el programa apxs. De esta manera el usuario puede usar apxs para compilar sus fientes de módulos de Apache httpd sin el código fuente de la distribución de Apache httpd y sin tener que tratar con el compilador dependiente de plataforma y indicadores de enlazador para el soporte de DSO.

Usage Summary

Para dar una visión general de las características DSO de Apache HTTP Server 2.x, aquí tenemos un resumen breve y conciso:

  1. Build and install a distributed Apache httpd module, say mod_foo.c, into its own DSO mod_foo.so:

    $ ./configure --prefix=/path/to/install --enable-foo
    $ make install
  2. Configura Apache HTTP Server con todos los módulos habilitados. Solo unos pocos de ellos se cargarán en el inicio del servidor. Se puede cambiar el conjunto de módulos cargados activando o desactivando la directiva LoadModule en httpd.conf.

    $ ./configure --enable-mods-shared=all
    $ make install
  3. Algunos módulos sólo son útiles para desarrolladores y no se generarán. cuando se usa la opción de módulos all. Para generar todos los módulos disponibles incluyendo los de desarrolladorhay que usar reallyall. Además las directivas LoadModule para todos los módulos generados puede activarse a través de la opción de ¨configure¨ --enable-load-all-modules.

    $ ./configure --enable-mods-shared=reallyall --enable-load-all-modules
    $ make install
  4. Genera e instala un módulo Apache HTTPD de terceros, convirtiendo mod_foo.c, en su propio DSO mod_foo.so fuera del árbol de código de Apache httpd usando apxs: $ cd /path/to/3rdparty
    $ apxs -cia mod_foo.c

En todos los casos, una vez se ha compilado el módulo compartido, se debe usar una directiva LoadModule en httpd.conf para indicarle a Apache httpd que active el módulo.

Ver la documentacióin apxs para más detalles.

Contexto

En derivados modernos de Unix existe un mecanismo llamado enlace/carga dinámico de Objetos Dinámicos Compartidos (DSO) que facilita una forma de generar una parte de código de programa en un formato espacial para cargar en tiempo real en el espacio de direcciones de un programa ejecutable.

Esta carga generalmente se puede hacer de dos maneras: automáticamente desde programa de sistema llamado ld.so cuando se arranca un programa ejecutable o manualmente desde dentro del programa que se ejecuta a través de un interfaz de sistema programático del cargador de Unix a través de las llamadas de sistema dlopen()/dlsym().

En la primera manera los DSO se llaman generalmente librerías compartidas o librerías DSO y llamadas libfoo.so o libfoo.so.1.2. Residen en un directorio del sistema (generalmente /usr/lib) y el enlace con el programa ejecutable se establece en tiempo de compilación especificando -lfoo al comando enlazador. Esto codifica de forma rígida referencias de librería en el fichero de programa ejecutable de manera que en el arranque el cargador Unix es capaz de localizar libfoo.so en /usr/lib, en rutas con codificación rígida a través de opciones-de-enlazador como -R o en rutas configuradas por variables de entorno LD_LIBRARY_PATH. Entonces resuelve símbolos (todavía sin uresolver) en el programa ejecutable que están disponibles en el DSO.

Los símbolos en el programa ejecutable no se referencian generalmente por el DSO (porque es una biblioteca reutilizable de código general) y, por lo tanto, no es necesario hacer más resolución. El programa ejecutable no tiene necesidad de hacer nada por sí mismo para usar los símbolos del DSO porque la resolución completa la realiza el cargador de Unix. (De hecho, el código para invocar ld.so es parte del código de arranque en tiempo real que se enlaza dentro de cada programa ejecutable que se ha generado como no-estático). La ventaja de la carga dinámica de librerías de código común es obvía: el código de librería necesita guardarse solo una vez, en un sistema de librería como libc.so, ahorrando espacio en disco para cada programa.

En la segunda manera los DSO se llaman generalmente objetos compartidos o ficheros DSO y pueden ser nombrados con una extensión aribtraria (aunque el nombre canónico es foo.so). Estos ficheros generalmente permanencen dentro de un directorio de programa específico y no hay enlace establecido automáticamente al programaejecutable donde se están usando. En su lugar el programa ejecutable carga manualmente el DSO en tiempo-real en su espacio de direcciones a través de dlopen(). En este momento no se realiza resolución de símbolos del DSO para el programa ejecutable. Perp en su lugar se resuelve automáticamente cualquier (todavía sin resolver) símbolo en el DSO del conjunto de símbolos exportado por el programa ejecutable y sus ya cargadas librerías DSO (especialmente todos los símbolos del ubícuo libc.so). De esta forma el DSO obtiene conocimiento del símbolo del programa ejecutable como si hubiera servidor enlazado estáticamente dentro de él mismo en primer lugar.

Por último, para aprovechar la API del DSO, el programa ejecutable tiene que resolver determinados símbolos de la DSO a través de dlsym() para su uso posterior dentro de las tablas de envío etc. En otras palabras: El programa ejecutable tiene que resolver manualmente cada símbolo que necesita para poder utilizarlo. La ventaja de este mecanismo es que no es necesario cargar las partes opcionales del programa (y por lo tanto no gastan memoria) hasta que sean necesarias para el programa en cuestión. Cuando se necesitan, estas partes del programa se pueden cargar dinámicamente para ampliar la funcionalidad del programa base.

Aunque este mecanismo DSO parece sencillo, hay al menos un paso difícil: La resolución de símbolos de del programa ejecutable para el DSO cuando se usa un DSO para extender un programa (la segunda manera). ¿Por qué? Porque la "resolución inversa" de símbolos DSO desde el conjunto de símbolos del programa ejecutable va en contra del diseño de la biblioteca (donde la biblioteca no tiene conocimiento de los programas que la utilizan) y no está disponible en todas las plataformas ni está estandarizada. En la práctica, los símbolos programa ejecutable a menudo no se reexportan y, por lo tanto, no están disponibles para su uso en un DSO. Encontrar una forma de forzar al enlazador a exportar todos los símbolos globales es el principal problema que uno tiene que resolver cuando se utiliza DSO para extender un programa en tiempo de ejecución.

El método de librería compartida es el típico, porque es para lo que se diseñó el mecanismo DSO, de ahí que se utilice para casi todos los tipos de librerías que el sistema operativo proporciona.

Ventajas y Desventajas

Las características anteriores basadas en DSO tienen las siguientes ventajas:

DSO tiene las siguientes desventajas: