Apache 1.3
Originalmente escrito por
Soporte de Objetos Compartidos Dinámicos (Dynamic Shared Object - DSO)
Ralf S. Engelschall <rse@apache.org>, Abril 1998Algo de información
En los sucesores modernos de Unix existe un ingenioso mecanismo llamado carga/enlace dinámico de Objetos Compartidos y Dinámicos (DSO), que proporciona un modo de construir un pedazo de código en un formato especial y que permite cargarlo en tiempo de ejecución en el espacio de direcciones de un programa ejecutable.
Esta carga generalmente puede hacerse de dos modos: automáticamente a través de un programa de sistema llamado
ld.so
y cuando un programa ejecutable ha arrancado, o manualmente desde dentro de un programa en ejecución por medio de interfaz de sistema al cargador de Unix con llamadas al sistema de tipodlopen()/dlsym()
.Del primer modo los DSO son llamados bibliotecas compartidas o bibliotecas DSO y nombrados como
libfoo.so
olibfoo.so.1.2
. Residen en un directorio de sistema (generalmente/usr/lib
) y el enlace al programa ejecutable se establece en tiempo de compilación especificando el parámetro-lfoo
al enlazador (linker). Las bibliotecas así codificadas referencia al ejecutable, de modo que al arrancar el cargador de Unix es capaz de localizarlibfoo.so
en/usr/lib
, en los paths especificados con parámetros pasados al linker como-R
o en paths configurados por medio de variables de entorno enLD_LIBRARY_PATH
. A partir de aquí resuelve cualquier símbolo en el programa ejecutable que esté disponible en el DSO.Los símbolos en el programa ejecutable normalmente no se referencian por el DSO (porque es una biblioteca reutilizable de código general) y así no se debe hacer ningún tipo de resolución. El programa ejecutable no necesita hacer nada por su cuenta para utilizar los símbolo de DSO porque la resolución completa la lleva a cabo el cargador de Unix. (De hecho el código para invocar
ld.so
forma parte del código de arranque que está enlazado a cada programa ejecutable limitado a ser no estático). La ventaja de la carga dinámica de código común de biblioteca es obvia: el código de biblioteca necesita ser almacenado sólo una vez, en una biblioteca de sistema comolibc.so
, ahorrando espacio en disco para cada programa.De la segunda manera los DSO son normalmente llamados objetos compartidos o ficheros DSO y se les puede asignar un nombre con cualquier extensión (aunque el nombre estándar es
foo.so
). Estos ficheros suelen permanecer dentro de un directorio específico de cada programa y no se establece ningún link (enlace) automático al programa ejecutable donde son usados. En lugar de esto, el programa ejecutable carga manualmente el DSO en tiempo de ejecución a un espacio de direcciones por medio dedlopen()
. En ese momento DSO realiza ninguna resolución de símbolos para el programa ejecutable. En su lugar el cargador de Unix automáticamente resuelve cualquier símbolo en el DSO del conjunto de símbolos exportados por el programa ejecutable y que ya ha sido cargado por las librerías DSO (especialmente todos los símbolos delibc.so
). De este modo DSO tiene conocimiento del conjunto de símbolos del programa ejecutable como si hubiera sido enlazado estáticamente con él desde el principio.Finalmente, para aprovechar el API de DSO el programa ejecutable debe resolver símbolos particulares de DSO a través de
dlsym()
para ser utilizados posteriormente dentro de tablas enviadas etc. En otras palabras: el programa ejecutable debe resolver manualmente cada símbolo que necesita a fin de poder utilizarlo. La ventaja de este mecanismo es que las partes opcionales del programa no necesitan ser cargadas (y de este modo no gastan memoria) hasta que el programa en cuestión las necesite. Cuando sean requeridos, estas partes del programa pueden ser cargadas dinámicamente para extender la funcionalidad básica del programa.Aunque este mecanismo del DSO parece sencillo, se da un paso difícil: la resolución de símbolos desde el programa ejecutable para el DSO cuando se usa un DSO para extender un programa (la segunda manera). ¿Porqué? Porque resolver de modo inverso símbolos DSO desde el símbolo del programa ejecutable va en contra del diseño de la biblioteca (la biblioteca no tiene conocimiento del programa que lo está usando) y ni está disponible en todas las plataformas ni estandarizado. En la práctica los símbolos globales de el programa ejecutable no son a menudo reexportados y, de este modo, no están no están en disposición de ser usados en un DSO. Cuando se usa DSO para extender un programa en tiempo de ejecución, el principal problema que debe uno resolver es encontrar un modo de forzar al enlazador para que exporte todos los símbolos globales.
Windows y NetWare proporcionan funcionalidades semejantes, aunque hayan sido implementadas de una forma algo diferente que el método DSO de Unix que se describe en este documento. En particular, los módulos DSO (DLL's y NML's respectivamente) se construyen de forma diferente que en sus primos Unix. Este documento no pretende explorar el tema de la construcción de módulos DSO en esas plataformas. Sin embargo la descripción de mod_so y su configuración son semejantes.
Utilización práctica
La aproximación más usual es la de la biblioteca compartida porque es para lo que se diseñó el DSO, de ahí que sea usado para casi todo tipo de bibliotecas que proporcione el sistema operativo. Por otro lado, el uso de objetos compartidos para extender un programa no es usado por muchos programas.
Hasta 1998 sólo había unos pocos paquetes disponibles que usaran el mecanismo DSO para extender su funcionalidad en tiempo de ejecución: Perl 5 (por medio de su mecanismo XS y el módulo DynaLoader), Netscape Server, etc. Empezando con la versión 1.3, Apache se unión a la panda porque ya usaba el concepto de módulo para extender su funcionalidad y usar internamente una aproximación basada en una "lista de envíos" para enlazar módulos externos a las funcionalidades básicas de Apache. Así que Apache está realmente predestinado a usar DSO para cargar sus módulos en tiempo de ejecución.
Hasta la versión 1.3, el sistema de configuración de Apache tiene soporte opcional para sacar provecho del método DSO: compilación del programa central de Apache produciendo una librería DSO para ser compartida y compilación de módulos Apache en ficheros de una librería DSO que serán cargados explícitamente durante la ejecución.
Implementación
El soporte DSO para cargar módulos individuales de Apache está asentado en un módulo llamado
mod_so.c
, que debe ser estáticamente compilado en el núcleo de Apache. Es el único módulo, además dehttp_core.c
que no puede ser añadido a DSO por sí mismo (bootstrapping, secuencia de instrucciones iniciales). Prácticamente todos los otros módulos distribuidos de Apache pueden ser emplazados en DSO habilitando para ellos el DSO build for víaconfigure
--enable-shared
(vea el ficheroINSTALL
) o cambiando el comandoAddModule
ensrc/Configuration
al comandoSharedModule
(vea el ficherosrc/INSTALL
). Después de que un módulo sea compilado en un DSO, por ejemplo,mod_foo.so
puede usar el comandomod_so
LoadModule
en el ficherohttpd.conf
para cargar este módulo cuando arranque el servidor.Para simplificar la creación de ficheros DSO para módulos Apache (especialmente para módulos de terceros) se dispone de un nuevo programa llamado apxs (APache eXtenSion). Se puede usar para construir módulos DSO fuera del árbol fuente de Apache. La idea es simple: cuando se instala Apache, el proceso
make install
deconfigure
instala los ficheros C de cabecera de Apache y mete enapxs
los flags del compilador y enlazador dependientes de la plataforma para construir ficheros DSO. De este modo el usuario puede usarapxs
para compilar sus módulos fuente de Apache sin el árbol de código fuente de Apache y sin tener que tocar los flags del compilador y del enlazador dependientes de la plataforma para soportar DSO.Para emplazar dentro de una biblioteca DSO el núcleo completo de Apache (sólo requerido en algunas de las plataformas soportadas para forzar al enlazador a exportar los símbolos del núcleo de apache, un requisito para la modularización de DSO) se debe habilitar la regla
SHARED_CORE
a través deconfigure
con la opción--enable-rule=SHARED_CORE
(consulte el ficheroINSTALL
) o cambiando el comandoRule
en su ficheroConfiguration
porRule SHARED_CORE=yes
(vea el ficherosrc/INSTALL
). En ese momento el código del núcleo es emplazado en una biblioteca DSO llamadalibhttpd.so
. Ya que no se puede enlazar DSO con una librería estática, en todas las plataformas, se ha creado un programa ejecutable adicional llamadolibhttpd.ep
que enlaza este código estático y provee de a la funciónmain()
. Finalmente, el programa ejecutablehttpd
es sustituido por una secuencia de instrucciones (bootstrapping) que automáticamente se asegura que el cargador Unix sea capaz de cargar y arrancarlibhttpd.ep
pasándoleLD_LIBRARY_PATH
alibhttpd.so
.Plataformas soportadas
El script
src/Configure
un conocimiento limitado de cómo compilar ficheros DSO porque, como se mencionó anteriormente, esto depende fuertemente de la plataforma. Sin embargo, la mayoría de las plataformas Unix son soportadas. El estado actual (a mayo de 1999) es este:
- Plataformas soportadas:
(las versiones probadas en paréntesis)o FreeBSD (2.1.5, 2.2.x, 3.x, 4.x) o OpenBSD (2.x) o NetBSD (1.3.1) o BSDI (3.x, 4.x) o Linux (Debian/1.3.1, RedHat/4.2) o Solaris (2.4, 2.5, 2.6, 2.7) o SunOS (4.1.3) o Digital UNIX (4.0) o IRIX (5.3, 6.2) o HP/UX (10.20) o UnixWare (2.01, 2.1.2) o SCO (5.0.4) o AIX (3.2, 4.1.5, 4.2, 4.3) o ReliantUNIX/SINIX (5.43) o SVR4 (-) o Mac OS X Server (1.0) o Mac OS (10.0 preview 1) o OpenStep/Mach (4.2) o DGUX (??) o NetWare (5.1) o Windows (95, 98, NT 4.0, 2000)
- Las plataformas explícitamente no soportadas:
o Ultrix (no existe interfaz de tipo dlopen en esta plataforma)Sumario de utilización
Para proporcionarle un vistazo de las características DSO en Apache 1.3, aquí tiene un breve y conciso sumario:
- Emplazar el código del núcleo de Apache (todas las cosas que conforman el binario
httpd
) en un DSOlibhttpd.so
, un programa ejecutablelibhttpd.ep
y una secuencia inicial (bootstrapping) ejecutablehttpd
(Nota. Esto sólo es necesario en algunas de las plataformas soportadas para forzar al enlazador a exportar los símbolos del núcleo de Apache, lo cual es un requisito para la modularización DSO):
- Compilar e instalar a través de
configure
(preferido):
$ ./configure --prefix=/path/to/install --enable-rule=SHARED_CORE ... $ make install- Compilar e instalar manualmente:
- Edit src/Configuration: << Rule SHARED_CORE=default >> Rule SHARED_CORE=yes << EXTRA_CFLAGS= >> EXTRA_CFLAGS= -DSHARED_CORE_DIR=\"/path/to/install/libexec\" $ make $ cp src/libhttpd.so* /path/to/install/libexec/ $ cp src/libhttpd.ep /path/to/install/libexec/ $ cp src/httpd /path/to/install/bin/- Compilar e instalar un módulo Apache distribuido, digamos
mod_foo.c
, en su propio DSOmod_foo.so
:
- Compilar e instalar a través de
configure
(preferido):
$ ./configure --prefix=/path/to/install --enable-shared=foo $ make install- Compilar e instalar manualmente:
- Edite src/Configuration: << AddModule modules/xxxx/mod_foo.o >> SharedModule modules/xxxx/mod_foo.so $ make $ cp src/xxxx/mod_foo.so /path/to/install/libexec - Edite /path/to/install/etc/httpd.conf >> LoadModule foo_module /path/to/install/libexec/mod_foo.so- Compilar e instalar un módulo de terceros, digamos
mod_foo.c
, en su propio DSOmod_foo.so
- Compilar e instar por medio de
configure
(preferido):
$ ./configure --add-module=/path/to/3rdparty/mod_foo.c --enable-shared=foo $ make install- Compilar e instalar manualmente:
$ cp /path/to/3rdparty/mod_foo.c /path/to/apache-1.3/src/modules/extra/ - Edite src/Configuration: >> SharedModule modules/extra/mod_foo.so $ make $ cp src/xxxx/mod_foo.so /path/to/install/libexec - Edite /path/to/install/etc/httpd.conf >> LoadModule foo_module /path/to/install/libexec/mod_foo.so
- Compilar e instalar un módulo de terceros,
mod_foo.c
, en su propio DSOmod_foo.so
fuera del árbol fuente de Apache:
- Compilar e instalar por medio de apxs:
$ cd /path/to/3rdparty $ apxs -c mod_foo.c $ apxs -i -a -n foo mod_foo.soVentajas y desventajas
Las características anteriores de DSO en Apache 1.3 tienen las siguientes ventajas:
- El paquete del servidor es más flexible en tiempo de ejecución porque el proceso actual del servidor puede ser ensamblado en tiempo de ejecución por medio de
LoadModule
enhttpd.conf
en lugar de hacerlo por medio de los comandosConfiguration
AddModule
en tiempo de compilación. De este modo se pueden arrancar diferentes instancias del servidor (estándar & versión SSL, mínima & versión potenciada [mod_perl, PHP3], etc.) con un única instalación de Apache.
- El paquete del servidor puede ser fácilmente ampliado con módulos de terceros incluso después de la instalación. Esto representa un gran beneficio para los que mantienen paquetes, ya que les permite crear paquete del núcleo de Apache y adicionalmente paquetes que contengan extensiones como PHP3, mod_perl, mod_fastcgi, etc.
- Mayor facilidad en los prototipos de módulos Apache porque con DSO y
apxs
usted puede trabajar fuera del árbol fuente de Apache y necesitar un único comandoapxs -i
seguido de unapachectl restart
para cargar una nueva versión del módulo desarrollado en el servidor Apache que esté actualmente corriendo.DSO presenta los siguientes inconvenientes:
- El mecanismo DSO no puede ser usado en cada plataforma porque no todos los sistemas operativos soportan carga dinámica del código en el espacio de direcciones de un programa.
- El servidor es aproximadamente un 20% más lento en su arranque por sobrecarga que la resolución representa para el cargador de Unix.
- El servidor es aproximadamente un 5% más lento en su ejecución bajo algunas plataformas porque el PIC (Position Independent Code, posición de código independiente) necesita maniobras complicadas para direccionamiento dinámico, que no es necesariamente tan rápido como el direccionamiento absoluto.
- Debido a que los módulos DSO no pueden ser enlazados con otras bibliotecas basadas en DSO (
ld -lfoo
) en todas las plataformas (por ejemplo las basadas en a.out normalmente no proporcionan esta funcionalidad, mientras las basadas en ELF sí), no se puede usar DSO para todo tipo de módulos. En otras palabras, los módulos compilados como ficheros DSO están restringidos a utilizar sólo símbolos del núcleo de Apache, de las biblioteca C (libc
) y todas las demás bibliotecas dinámicas o simbólicas usadas por el núcleo de Apache o desde archivos de bibliotecas estáticas (libfoo.a
) que contengan PIC. La única ocasión de usar otro código es, o bien asegurarse de que el núcleo de Apache ya contenga una referencia a él, cargando usted mismo el código por medio dedlopen()
, o bien habilitando la reglaSHARED_CHAIN
cuando compila Apache cuando su plataforma soporte en enlazado de ficheros DSO contra bibliotecas DSO.
- Bajo algunas plataformas (varios sistemas SVR4) no hay forma de forzar al enlazador para que exporte todos los símbolos globales cuando se enlaza el programa ejecutable httpd. Pero sin la visibilidad de los símbolos del núcleo de Apache, ningún módulo estándar de Apache podría ser usado como DSO. La única oportunidad aquí es usar
SHARED_CORE
porque de este modo los símbolos globales son forzados a ser exportados. Como consecuencia, el scriptsrc/Configure
automáticamente fuerza aSHARED_CORE
en estas plataformas cuando las características DSO son usada en el ficheroConfiguration
en la configuración desde la línea de comandos.