Soporte de Objetos Dinamicos Compartidos (DSO) - Servidor HTTP Apache

Versión 2.0 del Servidor HTTP Apache

Apache > Servidor HTTP > Documentación > Versión 2.0

# Soporte de Objetos Dinamicos Compartidos (DSO)

Idiomas disponibles: en | es | fr | ja | ko

El servidor HTTP Apache es un programa modular en el que el administrador puede elegir qué funcionalidades se incluyen mediante la selección de un conjunto de módulos. En primer lugar, los módulos pueden compilarse de manera estática en el binario `httpd`. De forma alternativa, los módulos también pueden compilarse como Objetos Dinamicos Compartidos (DSOs) que existen de forma independiente del archivo binario `httpd`. Los módulos que se deseen usar como objetos dinámicos compartidos pueden compilarse al mismo tiempo que el servidor, o pueden compilarse en otro momento y ser añadidos después usando la Herramienta de Extensión de Apache (`apxs`).

Este documento describe cómo usar los módulos en forma de objeto dinámico compartido (DSO) así como los fundamentos teóricos que hay detrás para explicar su funcionamiento.

## Implementación

Módulos Relacionados | Directivas Relacionadas
--- | ---
`mod_so` | `LoadModule`

Cargar módulos de Apache individualmente como objetos dinámicos compartidos (DSO) es posible gracias a un módulo llamado `mod_so` que debe compilarse estáticamente en el núcleo (kernel) de Apache. Es el único módulo junto con el módulo `core` que no se puede usar como objeto dinámico compartido. Prácticamente todos los demás módulos distribuidos con Apache se pueden usar como objetos dinámicos compartidos individualmente siempre y cuando se haya activado la posibilidad de usarlos con la opción de `configure` `--enable-module=shared` tal y como se explicó en la documentación de instalación. Una vez que haya compilado un módulo como objeto dinámico compartido y le haya puesto un nombre del tipo `mod_foo.so`, puede cargarlo al iniciar o reiniciar el servidor usando el comando `LoadModule` de `mod_so` en el fichero `httpd.conf`.

Para simplificar la creación de objetos dinámicos compartidos para Apache (especialmente módulos de terceras partes) está disponible un nuevo programa de soporte llamado `apxs` (APache eXtenSion). Puede usar este programa para crear módulos como objetos dinámicos compartidos sin tener que crearlos al mismo tiempo que compila su servidor Apache. La idea es simple: cuando se instala Apache el procedimiento `make install` de `configure` @@@ installs the Apache C header files and puts the platform-dependent compiler and linker flags for building DSO files into the apxs program / instala los ficheros de cabecera de C de Apache y especifica las opciones de compilación y enlace dependientes de la plataforma para generar objetos dinámicos compartidos con `apxs`. De esta manera el usuario puede usar `apxs` para compilar el código fuente de módulos de Apache de manera independiente y sin tener que preocuparse por las opciones de compilación y enlace dependientes de la plataforma que soportan objetos dinámicos compartidos.

## Resumen de uso

Para que se haga una idea de lo que permite el soporte de objetos dinámicos compartidos en Apache 2.0, aquí tiene un resumen breve pero conciso:

1. Construir e instalar un módulo incluido en la distribución de Apache, digamos `mod_foo.c`, como un objeto dinámico compartido de nombre `mod_foo.so`:

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

2. Construir e instalar un módulo de Apache de una tercera parte, digamos `mod_foo.c`, como un objeto dinámico compartido de nombre `mod_foo.so`:

   `$ ./configure --add-module=module_type:/path/to/3rdparty/mod_foo.c --enable-foo=shared
   $ make install`

3. Configurar Apache para poder instalar después objetos dinámicos compartidos:

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

4. Construir e instalar un módulo de Apache de una tercera parte, digamos `mod_foo.c`, como un objeto dinámico compartido de nombre `mod_foo.so` fuera de la estructura de directorios de Apache usando `apxs`:

   `$ cd /path/to/3rdparty
   $ apxs -c mod_foo.c
   $ apxs -i -a -n foo mod_foo.la`

En todos los casos, una vez que se compila el objeto dinámico compartido, debe usar una directiva `LoadModule` en `httpd.conf` para activar dicho módulo.

## Fundamentos teoróricos detrás de los objetos dinámicos compartidos

En las versiones modernas de Unix, existe un mecanismo especialmente útil normalmente llamado enlazado/carga de Objetos Dinámicos Compartidos (DSO). Este mecanismo ofrece una forma de construir trozos de código de programa en un formato especial para cargarlo en tiempo de ejecución en el espacio de direcciones de memoria de un programa ejecutable.

Esta carga puede hacerse de dos maneras: automáticamente con un programa de sistema llamado `ld.so` al inicio de un programa ejecutable o manualmente desde dentro del programa en ejecución con una interfaz programática del sistema al cargador de Unix mediante llamadas al sistema `dlopen()/dlsym()`.

Si se usa el primer método, los objetos dinámicos compartidos se llaman normalmente librerías compartidas ó librerías DSO y se nombran como `libfoo.so` o `libfoo.so.1.2`. Residen en un directorio de sistema (normalmente `/usr/lib`) y el enlace con el programa ejecutable se establece al construir la librería especificando la opción`-lfoo` al comando de enlace. Esto incluye las referencias literales a las librerías en el programa ejecutable de manera que cuando se inicie, el cargador de Unix será capaz de localizar `libfoo.so` en `/usr/lib`, en rutas referenciadas literalmente mediante opciones del linker como `-R` o en rutas configuradas mediante la variable de entorno `LD_LIBRARY_PATH`. Entonces se resuelven los símbolos (todavía no resueltos) en el programa ejecutable que están presentes en el objeto dinámico compartido.

Los símbolos en el programa ejecutable no están referenciados normalmente en el objeto dinámico compartido (porque son librerías reusables de propósito general) y por tanto, no se producen más resoluciones. El programa ejecutable no tiene que hacer nada por sí mismo para usar los símbolos del objeto dinámico compartido porque todo el trabajo de resolución lo hace @@@ Unix loader / el cargador de Unix @@@. (De hecho, el código para invocar `ld.so` es parte del código que se ejecuta al iniciar, y que hay en cualquier programa ejecutable que haya sido construido de forma no estática). La ventaja de cargar dinámicamente el código de las librerías comunes es obvia: el código de las librerías necesita ser almacenado solamente una vez, en una librería de sistema como `libc.so`, ahorrando así espacio en disco.

Por otro lado, los objetos dinámicos compartidos también suelen llamarse objetos compatidos o ficheros DSO y se les puede nombrar con cualquier extensión (aunque su nombre canónico es `foo.so`). Estos archivos normalmente permanecen dentro de un directorio específico del programa y no se establecen enlaces automáticamente con los programas ejecutables con los que se usan. En lugar de esto, el programa ejecutable carga manualmente el objeto dinámico compartido en tiempo de ejecución en su espacio de direcciones de memoria con `dlopen()`. En ese momento no se resuelven los símbolos del objeto dinámico compartido para el programa ejecutable. En lugar de esto, el cargador de Unix resuelve automáticamente los símbolos (aún no resueltos en el objeto dinámico compartido del conjunto de símbolos exportados por el programa ejecutable y de las librerías DSO que tenga ya cargadas (especialmente todos los símbolos de la omnipresente `libc.so`). De esta manera el objeto dinámico compartido puede conocer el conjunto de símbolos del programa ejecutable como si hubiera sido enlazado estáticamente en un primer momento.

Finalmente, para beneficiarse de la API de las DSOs, el programa ejecutable tiene que resolver los símbolos particulares de la DSO con `dlsym()` para ser usado más tarde dentro de tablas de direccionamiento (dispatch tables) etc. En otras palabras: El programa ejecutable tiene que resolver manualmente cada uno de los símbolos que necesita para poder usarlo después. La ventaja de ese mecanismo es que las partes opcionales del programa no necesitan ser cargadas (y por tanto no consumen memoria) hasta que se necesitan por el programa en cuestión. Cuando es necesario, estas partes del programa pueden cargarse dinámicamente para expandir las funcionalidades básicas del programa.

Aunque este mecanismo DSO parece muy claro, hay al menos un paso de cierta dificultad: la resolución de los símbolos que usa el programa ejecutable por la DSO cuando se usa una DSO para extender la funcionalidad de una programa (segundo caso). Por qué? Porque la resolución inversa de símbolos de DSOs del conjunto de símbolos del programa ejecutable se hace en contra del diseño de la librería (donde la librería no tiene conocimiento sobre los programas que la usan) y tampoco está disponible en todas las plataformas no estandarizadas. En la práctica los símbolos globales del programa ejecutable están disponibles para su uso en una DSO. El mayor problema que hay que resolver cuando se usan DSOs para extender un programa en tiempo de ejecución es encontrar un modo de forzar al enlazador a exportar todos los símbolos globales.

El enfoque de las librerías compartidas es bastante típico, porque es para lo que se diseño el mecanismo DSO, por tanto se usa para casi todos los tipos de librerías que incluye el sistema operativo. Por otro lado, no muchos programas usan objetos compartidos para expandir sus funcionalidades.

En 1998, había solamente unos pocos programas disponibles que usaban el mecanismo DSO para extender su funcionalidad en tiempo de ejecucion: Perl 5 (por medio de su mecanismo XS y el módulo DynaLoader), Netscape Server, etc. A partir de la version 1.3, Apache se unió a este grupo, Apache usa desde entonces una concepción modular para extender su funcionalidad e internamente usa un enfoque de tablas de direccionamiento (dispatch-list-based) para enlazar módulos externos con las funcionalidades propias del servidor. De esta manera, Apache puede usar el mecanismo DSO para cargar sus módulos en tiempo de ejecución.

## Ventajas e Inconvenientes

Las características de las librerías dinámicas compartidas arriba explicadas tienen las siguientes ventajas:

- El servidor es mucho más flexible en tiempo de ejecución porque pueden añadirse módulos mediante comandos de configuración `LoadModule` en `httpd.conf` en lugar de tener que hacerlo con las opciones de `configure` al compilar. Por ejemplo, de esta manera uno puede ejecutar diferentes instancias del servidor (estándar & SSL, mínima & super potente [mod_perl, PHP3], etc.) con una única instalación de Apache.
- El servidor puede ser extendido fácilmente con módulos de terceras partes después de la instalación. Esto es un gran beneficio al menos para los mantenedores de paquetes de distribuciones, que pueden crear un paquete básico de Apache y paquetes adicionales que contengan extensiones tales como PHP3, mod_perl, mod_fastcgi, etc.
- Facilita la labor de hacer prototipos de módulos de Apache porque con el dúo DSO/`apxs` se puede trabajar fuera de la estructura de directorios de Apache y únicamente es necesario el comando `apxs -i` seguido del comando `apachectl restart` para probar la nueva versión del módulo que se está desarrollando.

DSO presenta los siguientes inconvenientes:

- El mecanismo DSO no puede ser usado en todas las plataformas porque no todos los sistemas operativos soportan la carga dinámica de código en el espacio de direcciones de memoria de un programa.
- El servidor es aproximadamente un 20% más lento iniciándose por la sobrecarga que implica la resolución de símbolos por parte del cargador de Unix.
- El servidor es aproximadamente un 5% más lento ejecutándose en algunas plataformas porque el código posicionado independientemente (PIC) necesita algunas veces procesos bastante complicados para calcular direcciones relativas que no son en principio tan rápidos como los que se usan para calcular direcciones absolutas.
- Como los módulos DSO no pueden enlazarse a otras librerías basadas en DSO (`ld -lfoo`) en todas las plataformas (por ejemplo en las plataformas basadas en a.out normalmente no puede ser usada esta funcionalidad, mientras que sí puede ser usada en las plataformas basadas en ELF) no se puede usar el mecanismo DSO para todos los tipos de módulos. En otras palabras, los módulos compilados como ficheros DSO solamente pueden usar símbolos del núcleo (kernel) de Apache, los de las librerías de C (`libc`) y de todas las demas librerías dinámicas o estáticas usadas por el núcleo de Apache, o de archivos de librerías estáticas (`libfoo.a`) que contengan código independiente de su posición. Las únicas posibilidades para usar otro código es asegurarse de que el núcleo de Apache contiene una referencia a él o cargar el código por medio de `dlopen()`.

Idiomas disponibles: en | es | fr | ja | ko