Biblioteca (informática)

Ilustración de una aplicación que utiliza la biblioteca libvorbisfile.so para reproducir un archivo Ogg Vorbis.

En informática, una biblioteca o, llamada por vicio del lenguaje, librería (del inglés library) es un conjunto de implementaciones funcionales, codificadas en un lenguaje de programación, que ofrece una interfaz bien definida para la funcionalidad que se invoca.

A diferencia de un programa ejecutable, el comportamiento que implementa una biblioteca no espera ser utilizada de forma autónoma (un programa sí: tiene un punto de entrada principal), sino que su fin es ser utilizada por otros programas, independientes y de forma simultánea. Por otra parte, el comportamiento de una biblioteca no tiene por qué diferenciarse demasiado del que pudiera especificarse en un programa. Es más, unas bibliotecas pueden requerir de otras para funcionar, pues el comportamiento que definen refina, o altera, el comportamiento de la biblioteca original; o bien la hace disponible para otra tecnología o lenguaje de programación.

Las bibliotecas pueden vincularse a un programa (o a otra biblioteca) en distintos puntos del desarrollo o la ejecución, según el tipo de vínculo que se quiera establecer, tal y como se detalla en el apartado de "Tipos".

La mayoría de los sistemas operativos modernos proporcionan bibliotecas que implementan los servicios del sistema. De esta manera, estos servicios se han convertido en una «materia prima» que cualquier aplicación moderna espera que el sistema operativo ofrezca. Como tal, la mayor parte del código utilizado por las aplicaciones modernas se ofrece en estas bibliotecas.

Nota terminológica

Habitualmente se emplea el término librería para referirse a una biblioteca, por la similitud con el original inglés library. Ambos vocablos, «biblioteca» y «librería», son correctos según las definiciones (biblioteca,[1]librería[2]​) de la RAE. No obstante lo anterior, atendiendo a una traducción literal la acepción correcta sería biblioteca, ya que el término inglés para «librería» es bookstore o book shop (literalmente: «tienda de libros»), o bien bookshelf (estantería o mueble para guardar libros, librería o librero). Una traducción más directa y literal que «librería» sería el término: «librero». También es habitual referirse a ella con el vocablo de origen anglosajón toolkit (conjunto, equipo, maletín, caja, estuche, juego (kit) de herramientas).

Historia

Los primeros conceptos de programación similares a las bibliotecas intentaban separar las definiciones de datos de la implementación del programa. El concepto COMPOOL (Communication Pool) fue popularizado por JOVIAL en 1959, aunque tomó la idea prestada del software de los grandes sistemas SAGE. Siguiendo los principios de las ciencias de la computación de separación de problemas (aislar problemas pequeños fáciles de abordar) y ocultación de información, "el propósito del COMPOOL es permitir el intercambio de datos del sistema entre varios programas, proporcionando una descripción centralizada de los mismos" (Wexelblat 1981:369).

COBOL incluyó un primitivo sistema de bibliotecas en el año 1959 (Wexelblat 1981:274), pero Jean Sammet las describió retrospectivamente como recursos insuficientes de biblioteca (Wexelblat 1981:258).

Otra de las grandes contribuciones al concepto moderno de biblioteca fue la innovación de subprograma de FORTRAN. Estos pueden ser compilados con independencia unos de otros, pero el compilador carece de un enlazador, por lo que el chequeo de tipos entre los subprogramas resulta imposible (Wilson et. Al. 1988:126).

Por último, se debe hablar de la influencia que Simula 67 tuvo en el concepto de 'biblioteca'. Simula es el primer lenguaje de programación orientado a objetos, y sus clases son casi idénticas que el concepto actual que se utiliza en Java, C++ y C#. El concepto de clase de Simula fue también el origen del paquete en Ada y el módulo de Modula-2 (Wilson et. Al. 1988:52). A pesar de haber sido desarrollado en 1965, las clases de Simula podían ser incluidas en ficheros de biblioteca y añadidas en tiempo de compilación (Wexelblat 1981:716).

Tipos

Las bibliotecas estáticas

Históricamente, las bibliotecas solo podían ser estáticas. Una biblioteca estática, también conocida como archivo, es un fichero contenedor con varios archivos de código objeto empaquetados en su interior, que en el proceso de enlazado, durante la compilación, serán copiados y relocalizados (si es necesario) en el fichero ejecutable final, junto con el resto de ficheros de código objeto. Este proceso, y el archivo ejecutable, se conoce como una construcción estática de la aplicación objetivo. En este caso, la biblioteca actúa simplemente como un recipiente para ficheros de código objeto que no se diferencian (más que semánticamente) de los ficheros objeto intermedios producidos durante la etapa previa de compilación del programa. En la construcción estática de ficheros compilados se resuelven las direcciones de las subrutinas ensambladas en tiempo de compilación (más específicamente, en la etapa de enlazado), de modo que las referencias a subrutinas de la biblioteca se resuelven estáticamente, del mismo modo que las referencias a cualquier otra función del programa. Así, la dirección real, las referencias para saltos y otras llamadas a rutinas se almacenan en una dirección relativa o simbólica.

El enlazador resuelve todas las direcciones no resueltas convirtiéndolas en direcciones fijas o relocalizables (desde una base común) cargando todo el código (incluyendo las bibliotecas) en posiciones de memoria en tiempo de ejecución. Este proceso de enlazado puede durar incluso más tiempo que el proceso de compilación, y debe ser realizado cada vez que alguno de los módulos es recompilado.

Un enlazador puede trabajar sobre tipos específicos de ficheros objeto, y por lo tanto requiere tipos específicos (compatibles) de bibliotecas. Los ficheros objeto recompilados en una biblioteca pueden distribuirse y utilizarse fácilmente. Un cliente, ya sea un programa u otra biblioteca, accede a una biblioteca objeto referenciando solo por su nombre. El proceso de enlazado resuelve las referencias buscando en las bibliotecas del orden dado. Por lo general, no se considera un error si un nombre puede encontrarse varias veces en un determinado conjunto de las bibliotecas.

Bibliotecas dinámicas

Las bibliotecas dinámicas, vinculadas dinámicamente, o de vínculos dinámicos son ficheros que contienen código objeto construido de forma independiente de su ubicación de tal modo que están preparadas para poder ser requeridas y cargadas en tiempo de ejecución por cualquier programa, en lugar de tener que ser enlazadas, previamente, en tiempo de compilación. Por tanto, han de estar disponibles como ficheros independientes al programa ejecutable (generalmente en directorios del sistema). En el proceso de enlazado (en tiempo de compilación) se genera un fichero ejecutable con anotaciones de qué bibliotecas dinámicas requiere (pero no de dónde encontrarlas), y funciones de «esbozo» que se encargan de delegar la llamada a la función al cargador dinámico (o dynamic-loader) (en GNU/Linux, ld.so). En el resto del programa, las llamadas a las funciones de la biblioteca se cambian por una llamada a la función de esbozo generada por el enlazador.

Por otra parte, cuando la aplicación que se ejecute requiera acceder a las rutinas almacenadas en una biblioteca dinámica, y ejecute la función de esbozo, el cargador de enlaces dinámicos podrá sustituir esta llamada por la función real de la biblioteca dinámica, cargándola en memoria si no lo estuviera ya, y mapeando las páginas de esta en el espacio de memoria del proceso del programa.

En algunos sistemas operativos puede decidirse si una biblioteca ha de estar disponible inmediatamente o solamente cuando se haga referencia a una función de ella. Si se decide esto último, aparecerá un fenómeno denominado retraso de carga, derivado de tener que cargar de memoria secundaria la biblioteca, si no estuviera ya en memoria, y de ajustarla al espacio de direcciones del programa contra el que se vincula.

Ventajas del enlace dinámico respecto al estático son que se permite la reutilización no solo de código, sino de espacio físico: un mismo fichero de biblioteca compartida puede ser utilizada por varios programas sin que estos copien su contenido dentro de ellos. Esto puede llegar a ser bastante espacio, según el número de bibliotecas que requiera un programa. Además, puede reutilizarse memoria principal (RAM) para programas que utilicen la misma biblioteca (por ejemplo, puede ser necesario cargar las bibliotecas de Qt solo una vez para todos los programas que las utilicen).

Por otra parte, el mayor inconveniente es el aumento del tiempo de carga (debido a tener que buscar el fichero de la biblioteca, cargarlo y relocalizar las llamadas en el programa) y el aumento de una indirección a la hora de llamar a las funciones de la biblioteca.

El enlace dinámico, por su naturaleza, tiene tan solo las limitaciones establecidas por las licencias de software.

La tecnología que permite enlazar bibliotecas de forma dinámica es muy útil para la construcción de plugins, sobre todo cuando unas bibliotecas pueden ser sustituidas por otras con una interfaz similar, pero diferente funcionalidad. Se puede decir que un software tiene una "arquitectura de plugin" si utiliza bibliotecas con una funcionalidad básica con la intención de que puedan ser sustituidas. Sin embargo, el uso de las bibliotecas enlazadas dinámicamente en la arquitectura de una aplicación no significa necesariamente que puedan ser sustituidas.

El enlace dinámico se desarrolló originalmente en los sistemas operativos Multics a partir de 1964. Se trataba de una característica del MTS (Michigan Terminal System), construido a finales de los 60.[3]

En distintos sistemas operativos toman distintos nombres, por ejemplo:

Relocalización

Uno de los problemas que el cargador debe gestionar es que la localización real de los datos de la biblioteca no puede conocerse hasta que el ejecutable y todas las bibliotecas dinámicas que se han enlazado han sido cargadas en memoria. Eso se debe a que las localizaciones en memoria dependen de qué bibliotecas dinámicas se han cargado. No es posible depender de la dirección absoluta de los datos en el ejecutable, ni incluso en la biblioteca, ya que podrían producirse conflictos entre las diferentes bibliotecas: si dos de ellas utilizaran las mismas direcciones o sus direcciones se solaparan, sería imposible utilizar ambas en el mismo programa.

Sin embargo, en la práctica, en muchos de los sistemas las bibliotecas no cambian frecuentemente. Por tanto, es posible calcular una dirección de carga probable para cada biblioteca compartida en el sistema antes de que sea utilizada, y almacenar esa información en bibliotecas y ejecutables. Si cada biblioteca que es cargada es tratada así, entonces cada una de ellas será cargada en direcciones predeterminadas, lo que acelera el proceso de enlace dinámico. Esta optimización se conoce como Prebinding en Mac OS X y Prelinking en GNU/Linux.

Las desventajas de esta técnica son el tiempo requerido de precálculo de las direcciones cada vez que las bibliotecas compartidas cambian, la incapacidad de usar técnicas como la aleatorización de los espacios de direcciones, y el consumo de espacio virtual de direcciones (un problema que queda mitigado por el uso de arquitecturas de 64 bits, al menos en la actualidad).

Un antiguo método era examinar el programa en tiempo de carga. Una vez que todas las bibliotecas fueran cargadas, se reemplazan todas las referencias a datos en las bibliotecas, con punteros a localidades de memoria apropiados. En Windows 3.1 (y algunos sistemas embebidos como las calculadoras Texas Instruments), las referencias eran manejadas como listas ligadas, permitiendo la fácil enumeración y reemplazo. Ahora, la mayoría de las bibliotecas dinámicas ligan una tabla de símbolos con direcciones en blanco dentro del programa en tiempo de compilación. Todas las referencias a código o datos en la biblioteca pasan a través de esta tabla. En tiempo de carga, la tabla es modificada con la dirección de los datos/código por el linker. Este proceso es lento y afecta significativamente la velocidad de los programas que llaman continuamente a otros programas, tal como algunos scripts de shell.

La biblioteca contiene una tabla de saltos de todos los métodos que contiene, denominados puntos de entrada. Las llamadas dentro de la biblioteca «saltan a lo largo» de la tabla, buscando la ubicación del código en memoria, y a continuación solicitándolo. Estas solicitudes suponen un sobreesfuerzo, pero el retardo es habitualmente tan pequeño que es despreciable.

Localización de bibliotecas en tiempo de ejecución

Los enlazadores/cargadores dinámicos tienen una funcionalidad muy amplia. Algunos dependen de rutas explícitas a las bibliotecas almacenadas en los ejecutables. Cualquier cambio en la nomenclatura o el diseño del sistema de ficheros hará que estos sistemas fallen. Habitualmente solo se almacena en el ejecutable el nombre de la biblioteca (no la ruta), siendo el sistema operativo el que proporciona el mecanismo para encontrar la biblioteca en el disco mediante ciertos algoritmos.

Una de las mayores desventajas del enlace dinámico es que el funcionamiento correcto de los ejecutables depende de una serie de bibliotecas almacenadas de forma aislada. Si la biblioteca es borrada, movida o renombrada, o si una versión incompatible de DLL es copiada en una ubicación que aparece antes en la ruta de búsqueda, el ejecutable no se podrá cargar. En Windows esto se conoce como infierno de las DLL.

Sistemas Unix

La mayor parte de los sistemas tipo Unix disponen de una "ruta de búsqueda" que especifica los directorios del sistema de archivos en los que buscar las bibliotecas dinámicas. En algunos sistemas, la ruta por defecto es especificada en un archivo de configuración; en otros, está prefijada (hard coded) en el cargador dinámico. Algunos formatos de fichero ejecutable pueden especificar directorios adicionales en los que buscar las bibliotecas de un determinado programa. Esto puede ser usualmente alterado por una variable de entorno, aunque es deshabilitado para programas que tengan setuid o setgid, de manera que el usuario no puede forzar a ese programa a ejecutar un código arbitrario. Es aconsejable que los desarrolladores de bibliotecas pongan sus bibliotecas dinámicas en directorios que se encuentren en la ruta de búsqueda por defecto. Por el contrario, esto puede hacer problemática la instalación de nuevas bibliotecas, pues hace que esos directorios crezcan mucho haciéndose complicada

Carga dinámica

Bibliotecas remotas

Otra solución al problema de las bibliotecas es usar ejecutables completamente separados (a menudo una versión ligera) y llamarlos mediante procedimiento remoto (RPC) sobre la red a otra computadora u ordenador. Este enfoque maximiza la reutilización del sistema operativo: el código necesario para dar soporte a la biblioteca es el mismo que el usado para proveer a la aplicación soporte y seguridad para cualquier otro programa. Adicionalmente, dichos sistemas no requerirán que la biblioteca este grabada en la misma máquina, pudiendo redirigir la petición por la red.

Sin embargo, tal enfoque implica que cada llamada a la biblioteca requerirá una gran cantidad de gastos generales. Las llamadas RPC son mucho más costosas que llamadas a procedimiento en la propia máquina. Este enfoque se usa comúnmente en las arquitecturas distribuidas que hacen un uso intensivo de las RPC, en los sistemas cliente-servidor y en aplicaciones como Enterprise JavaBeans.

Véase también

Referencias

  1. RAE. «Definición de biblioteca». 
  2. RAE. «Definición de librería». 
  3. «A History of MTS». Information Technology Digest 5 (5).