Menú
Está libre
registro
hogar  /  Internet/ Tutorial ilustrado sobre Macromedia Flash MX. Biblioteca compartida flash soname

Tutorial ilustrado sobre Macromedia Flash MX. Biblioteca compartida flash soname

Anteriormente mencionamos las bibliotecas compartidas como una de las ventajas de los administradores de memoria de segmento y paginación sobre los administradores básicos y bancarios. En el direccionamiento básico, la imagen de cada proceso debe ocupar áreas contiguas en el espacio de direcciones físico y lógico. En estas condiciones, es imposible implementar la biblioteca compartida. Pero incluso cuando se usa el direccionamiento de página, las cosas no son tan simples.

El uso de bibliotecas compartidas y / o DLLs (en este caso la diferencia entre ellas no es fundamental) presupone alguna forma de ensamblado en el momento de la carga: el módulo ejecutable tiene referencias de direcciones no resueltas y los nombres de las bibliotecas que necesita. Cuando se cargan, estas bibliotecas se cargan y los enlaces se resuelven. El problema aquí es que cuando se carga una biblioteca, es necesario moverla reconfigurando las referencias de direcciones absolutas en su código y datos (consulte el Capítulo 3). Si en diferentes procesos la biblioteca está configurada para diferentes direcciones, ¡ya no se compartirá (Fig. 5.14)! Si las bibliotecas compartidas pueden tener enlaces sin resolver a otras bibliotecas, el problema solo se agrava al agregar enlaces externos a los enlaces reubicables.

Arroz. 5.14... Direcciones de mapeo de DLL en conflicto

En sistemas Unix más antiguos que usaban módulos cargables absolutos del formato fuera Las bibliotecas compartidas también se enviaron en formato de módulo absoluto, configuradas para direcciones fijas. Cada biblioteca se ha configurado para su dirección. El proveedor de las nuevas bibliotecas tuvo que acordar esta dirección con los desarrolladores del sistema. Esto era muy poco práctico, por lo que había muy pocas bibliotecas compartidas (especialmente aparte de las que venían con el sistema operativo).

Una solución más aceptable a este problema se implementa en OS / 2 2.xy Win32 (ambas arquitecturas son el desarrollo de sistemas con un solo espacio de direcciones). La idea es asignar un área de direcciones para cargar archivos DLL y asignar esta área a los espacios de direcciones de todos los procesos. Por lo tanto, todos los archivos DLL cargados en el sistema son visibles para todos (Fig. 5.15).

Los programas grandes a menudo requieren bibliotecas listas para usar. Una biblioteca es una colección de funciones ya compiladas (por ejemplo, una biblioteca matemática). La biblioteca se solicita al vincular cuando se genera el exe-shnik. Esto se hace con la opción -l: cc -o badmath badmath.o -lm Si necesita especificar la ruta a una biblioteca no estándar: cc -o badmath badmath.o -lm -L / usr / junk / lib -lcrud Si el nombre de la biblioteca termina en .a, entonces es una biblioteca estática. Cuando se vincula una biblioteca estática, el vinculador simplemente copia fragmentos completos de ella en el archivo exe generado, que, como resultado, puede funcionar de forma autónoma sin él. Si exe-shnik usa una biblioteca compartida, entonces el código se carga solo cuando es necesario. Las bibliotecas suelen estar ubicadas en los directorios / lib y / usr / lib. No debería haber bibliotecas estáticas en el directorio / lib. El nombre de la biblioteca compartida incluye el prefijo .so. Para determinar qué bibliotecas está usando el programa, debe ejecutar el comando: ldd prog El cargador ld.so carga las bibliotecas compartidas. El camino a la biblioteca puede estar dentro del exe-shnik. Si no está allí, el cargador busca en el archivo /etc/ld.so.cache, que enumera los directorios. También hay un archivo /etc/ld.so.conf. Si agregó manualmente la ruta al archivo /etc/ld.so.cache, entonces necesita ejecutar el comando: ldconfig -v También existe la variable de sistema LD_LIBRARY_PATH. Agregar rutas al archivo /etc/ld.so.conf no es una buena idea, las bibliotecas grandes van al caché del sistema y puedes meterte en problemas. La mejor manera es unir la ruta a la biblioteca en exe-shnik: cc -o myprog myprog.o -Wl, -rpath = / opt / obscure / lib -L / opt / obscure / lib -lweird Con el crecimiento de las bibliotecas compartidas, es posible que surjan problemas debido a la superposición, etc. El uso de LD_LIBRARY_PATH también es problemático. En su lugar, se puede utilizar un script: LD_LIBRARY_PATH = / opt / crummy / lib exportar LD_LIBRARY_PATH exec / opt / crummy / bin / my_prog [correo electrónico protegido] La principal herramienta de compilación en Unix es make. La descripción más detallada se da en El entorno de programación de UNIX o en Gestión de proyectos con make... Make siempre tiene un objetivo, objetivo, que puede ser un archivo o una etiqueta. El objetivo puede ser primario y secundario. Para construir un objetivo, busque reglas. Por ejemplo: OBJS = aux.o main.o # archivos de objeto todos: myprog myprog: $ (OBJS) $ (CC) -o myprog $ (OBJS) El primer objetivo, todos, es el principal por defecto. La regla para ello es myprog, que puede ser otra regla, otro objetivo o un archivo. OBJS es una macro para construir un objetivo. Este archivo MAKE producirá el siguiente resultado: cc -c -o aux.o aux.c cc -c -o main.o main.c cc -o myprog aux.o main.o make se puede ejecutar junto con opciones de comando, por ejemplo: make aux. o Opciones de Make: -n -f Macros de uso frecuente: CFLAGS - opciones del compilador LDFLAGS - opciones del enlazador make tiene varios objetivos estándar: clean distclean install test depend Makefile generalmente se inicia con cualquiera e incluye, por ejemplo: X_INCLUDES = -I / usr / X11R6 / include X_LIB = -L / usr / X11R6 / lib -lX11 -Xt NG_INCLUDES = -I / usr / local / include PNG_LIB = -L / usr / local / lib -lpng A continuación, están las opciones para el compilador y el enlazador. CFLAGS = $ (CFLAGS) $ (X_INCLUDES) $ (PNG_INCLUDES) LDFLAGS = $ (LDFLAGS) $ (X_LIB) $ (PNG_LIB) Entonces se puede enumerar el código fuente: UTIL_OBJS = util.o BORING_OBJS = $ (UTIL_OBJS) aburrido. O TRITE_OBJS = $ (UTIL_OBJS) trillado. O PROGS = aburrido trillado Y solo ahora hay metas y reglas: todo: $ (PROGS) aburrido: $ (BORING_OBJS) $ (CC) -o [correo electrónico protegido]$ (BORING_OBJS) $ (LDFLAGS) trillado: $ (TRITE_OBJS) $ (CC) -o [correo electrónico protegido]$ (TRITE_OBJS) $ (LDFLAGS) Las bibliotecas compartidas son un componente fundamental del sistema Unix. La biblioteca C estándar, por ejemplo, en Suse 9.1 tiene un tamaño de 1,3 MB. Una copia de esta biblioteca para todos los programas que la usan en / usr / bin ocupará más de un gigabyte. Dichas bibliotecas requieren no solo espacio en disco, sino también memoria. El kernel está diseñado para mantener una copia de la biblioteca compartida en la memoria. Debe recordarse que cuando se vincula estáticamente, el código de la biblioteca se agrega estáticamente al código del archivo ejecutable, y cuando lo ejecuta, no necesitamos la biblioteca; su código ya está vinculado durante la compilación al cuerpo del archivo ejecutable. Con la vinculación dinámica, la biblioteca compartida se vincula en tiempo de ejecución.
La vinculación dinámica es el tipo predominante de bibliotecas de vinculación. La biblioteca estándar por sí sola consta de más de mil llamadas al sistema. Para programas que realmente funcionan, el llamado. La tabla de vinculación de procedimientos (PLT) es una tabla que consta de llamadas a funciones llamadas desde bibliotecas compartidas. El primer problema que puede surgir con esto es un problema de compatibilidad. Con la compilación estática, este problema se resuelve en tiempo de compilación. Con la vinculación dinámica, no existe tal garantía, porque podemos actualizar la biblioteca compartida después de la compilación. En este caso, la verificación es el número de versión: cuando el vinculador dinámico intenta vincular la biblioteca, verifica el número de versión y, si el número no coincide, no está vinculado. El número de versión consta de 2 partes: el número principal (principal) y el número secundario (menor). Si el número principal es el mismo, entonces, como regla, no debería haber problemas al cargar la biblioteca. Si tenemos la biblioteca libexample.so, entonces tendrá un enlace a la versión libexample.so.N donde N es el número de versión principal principal, desde el cual, a su vez, habrá un enlace a libexample.so.NM donde M es el número de versión secundaria principal. En este caso, el programa buscará exactamente la versión N que necesita.
En las bibliotecas estáticas, el código está en archivos con la extensión .a. Para las bibliotecas dinámicas, los archivos tienen la extensión .so. El formato de la biblioteca estática se genera mediante la utilidad ar, y el formato es similar al que genera la utilidad tar. Para las bibliotecas dinámicas en las últimas versiones de Linux, este formato suele ser binario ELF. Consta de un encabezado y segmentos, que se dividen en secciones. Al vincular, el código de las bibliotecas estáticas se agrega directamente al archivo ejecutable del programa, y ​​los vínculos de las bibliotecas dinámicas se toman y se agregan al PLT. Cuando se carga, el programa carga un enlazador dinámico que carga las bibliotecas necesarias. Al vincular dinámicamente un programa, puede agregar rutas para buscar bibliotecas en tiempo de compilación. Para gcc, esta sintaxis se parece a -Wl, -R / ruta Si el programa ya está vinculado, puede usar la variable LD_LIBRARY_PATH. También puede ejecutar un script contenedor especial antes de iniciar el programa que configura específicamente los directorios de la biblioteca, como se hace , por ejemplo, al iniciar Mozilla. El enlazador dinámico accede a la configuración /etc/ld.so.conf, que incluye una lista de directorios. Pero primero, el vinculador accede a LD_LIBRARY_PATH. ldd (enumerar dependencias dinámicas)- utilidad para depurar bibliotecas compartidas. Enumera las bibliotecas compartidas para un módulo determinado, por ejemplo: ldd / bin / sh linux-gate.so.1 => (0xffffe000) libreadline.so.4 => /lib/libreadline.so.4 (0x40036000) libhistory.so.4 => /lib/libhistory.so.4 (0x40062000) libncurses.so.5 => /lib/libncurses.so.5 (0x40069000) libdl.so.2 => /lib/libdl.so.2 (0x400af000) libc.so.6 => / lib / tls /libc.so.6 (0x400b2000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
  • Para principiantes: artículo de Linux Journal "Vinculadores y cargadores" de Sandeep Grover
  • El manuscrito del libro "Linkers and Loaders" en línea.
  • Recomendado también en 2002 por Ulrich Drepper titulado "Cómo escribir bibliotecas compartidas" (pdf). Diapositivas (pdf) y un guión mencionado en las diapositivas.

Construyendo una biblioteca compartida

Escribamos una pequeña biblioteca que no haga nada, casi nada, además de "hola mundo". Primer objeto:

/ * print1.c * / #include vacío print1 (vacío) (printf ("1 \ n");)

Del mismo modo, el segundo objeto:

/ * print2.c * / #include vacío print2 (vacío) (printf ("2 \ n");)

Compilemos:

gcc -Wall -g -c -fpic print1.c gcc -Wall -g -c -fpic print2.c

Crea una biblioteca compartida:

gcc -shared -Wlsoname = libprint.so.1 -g \ -o libprint.so.1.0.1 print1.o print2.o

Hacemos un enlace a la biblioteca:

ln -sf libprint.so.1.0.1 libprint.so

Para que otros puedan usar esta biblioteca, debe escribir un encabezado:

/ * print.h * / #ifndef PRINT_H #define PRINT_H void print1 (void); void print2 (void); #terminara si

Escribamos un programa de prueba:

/ * print.c * / #include "print.h" int main () (print1 (); print2 (); return 0;)

Vamos a compilarlo:

gcc -Wall -g -L. -lprint -o print print.c

Para ejecutar el programa, el vinculador debe indicar dónde se encuentra la biblioteca:

Funciones de sobrecarga

Antes de cargar una biblioteca, siempre podemos decirle al cargador dónde comenzar a buscarla y cargarla.

Agreguemos una nueva función a la biblioteca que acabamos de escribir. Esta función se llama fopen.

/ * fopen.c * / #include FILE * fopen (const char * fn, const char * mode) (printf ("fopen llamado \ n"); return NULL;)

Compilemos esta función y la pongamos en la biblioteca:

gcc -Wall -g -c -fpic fopen.c gcc -shared -Wlsoname = libprint.so.1 -g \ -o libprint.so.1.0.2 print1.o print2.o fopen.o ln -sf libprint.so .1.0.2 libprint.so

Escribamos un programa para probar:

/ * print-2.c * / #include int main () (ARCHIVO * f; f = fopen ("abc", "r"); if (f == NULL) (printf ("1");) else (printf ("2"); fclose (f);) return 0;)

Compilemos este programa:

gcc -Wall -g -o print print.c

Debemos lanzarlo con el siguiente prefijo:

LD_PRELOAD =. / Libprint.so ./print

Si algo se hace mal, se mostrará la línea: fopen llamado

El proceso de creación de un programa.

Traducción al ruso: Vladimir Popov

Hoy, en un entorno en constante evolución, el proceso de desarrollo de software es producto de la evolución de las habilidades y la experiencia desarrolladas por programadores y desarrolladores.

Este proceso consta de los siguientes pasos:

    Generación de código fuente de lenguaje de alto nivel en un editor de texto. Si intentamos encajar un programa muy grande en un archivo, se vuelve difícil de administrar. Por esta razón, el código fuente se divide en módulos funcionales que se crean a partir de uno o más archivos de código fuente. Es posible que el código fuente de estos módulos no esté escrito necesariamente en el mismo idioma, ya que algunos idiomas son más adecuados para resolver un problema específico que otros.

    Una vez creados los archivos con el código fuente del programa, deben traducirse a bloques de código que puedan ser ejecutados por la máquina. Este código generalmente se conoce como código de objeto... Este código realiza las mismas acciones que el código original, excepto que está escrito en un lenguaje especial que la máquina puede ejecutar directamente. El proceso de traducir el código fuente en código objeto se conoce como Compilacion... La compilación se realiza en partes y compila (dependiendo del compilador) parte del programa y generalmente uno o más archivos de una vez. El código objeto compilado contiene un programa, subrutina, variables, etc. - las partes de los programas que se han traducido y están listas para el siguiente paso.

    Una vez creados todos los archivos de programa con código de máquina, deben conectarse o vincularse mediante una operación realizada por una utilidad especial llamada enlazador... En esta etapa, todas las referencias que el código del módulo hace al código que pertenece a otro módulo se "resuelven" (como llamar a una subrutina o referencias a variables propias o definidas en otro módulo). El producto resultante es un programa que se puede cargar y ejecutar directamente.

    La ejecución de un programa se realiza mediante un tipo especial de software, que es parte esencial del sistema operativo, en el caso de Linux es la llamada al sistema exec (). Esta función encuentra el archivo, asigna memoria al proceso, carga ciertas partes del contenido del archivo (que contiene el código y los valores de las variables iniciales) y transfiere el control al procesador en el punto de "texto" en el programa, que generalmente apunta al ejecutable. sí mismo.

2.- Breve historia del proceso de creación del programa.

El proceso de creación de un programa está en constante evolución, es necesario lograr la ejecución más eficiente de los programas o el mejor uso de los recursos del sistema.

Al principio, los programas se escribían directamente en código máquina. Posteriormente quedó claro que era posible escribir programas en un lenguaje de nivel superior, ya que la posterior traducción a código de máquina podía automatizarse debido a la naturaleza sistemática de la traducción. Esto aumentó el rendimiento de los programas.

Luego de aprender a compilar programas (simplifiqué la evolución de la compilación, de hecho fue un paso muy difícil, ya que es un proceso muy complejo), el proceso de creación de programas comenzó a consistir en crear un archivo con el código fuente del programa, compilándolo y finalmente ejecutándolo.

Pronto se notó que el proceso de compilación es muy laborioso y consume muchos recursos, incluido el tiempo de la máquina, y que muchas de las funciones incluidas en estos programas se utilizan una y otra vez en varios programas. Además, cuando alguien cambiaba una parte del programa, compilar el código insertado significaba volver a compilar todo el código fuente, incluida una nueva traducción de todo el código sin cambios.

Por este motivo, se empezó a utilizar la compilación modular. Su significado era dividir el programa en, por un lado, el programa principal y, por otro lado, aquellas funciones que se solían utilizar una y otra vez y que ya habían sido compiladas y almacenadas en un lugar especial (llamaremos este el predecesor de la biblioteca).

Entonces fue posible desarrollar programas que admitan estas funciones sin gastar un esfuerzo adicional en codificarlas una y otra vez. Aun así, el proceso se complicó debido a que al vincular programas, era necesario combinar todas las partes y tenían que ser conocidas por el programador (esto introdujo costos adicionales para verificar la posibilidad de usar una función conocida que usa / requiere otras funciones desconocidas).

3.- ¿Qué es una biblioteca?

El problema descrito anteriormente llevó a la creación de bibliotecas. Esto no es más que un tipo especial de archivo (para ser precisos, un archivo, tar (1) o cpio (1)) con parámetros especiales, cuyo formato entiende el enlazador, y cuando lo apuntamos a un archivo de biblioteca, el LINKER ELIGE SÓLO LOS MÓDULOS QUE NECESITAN PROGRAMAR y excluye todo lo demás. Ha surgido una nueva ventaja. Ahora era posible desarrollar programas que usaban grandes bibliotecas de funciones, y el programador no necesitaba conocer todas las dependencias de las funciones en esa biblioteca.

Las bibliotecas que hemos comentado hasta ahora no han avanzado más en su desarrollo. Simplemente agregaron un archivo, que a menudo se encuentra al principio del archivo, que contiene descripciones de módulos e identificadores que el vinculador debe resolver sin leer toda la biblioteca (y así eliminar la necesidad de leer la biblioteca varias veces). Este proceso (agregar la tabla de símbolos al archivo de la biblioteca) en Linux se realiza mediante el comando ranlib (1). Las bibliotecas descritas hasta ahora se conocen como BIBLIOTECAS ESTÁTICAS.

El progreso ocurrió después de que apareció el primer sistema multitarea: código de división... Si dos copias del mismo código se estuvieran ejecutando en el mismo sistema, sería deseable que ambos procesos pudieran compartir el mismo código, ya que un programa no suele modificar su propio código. Esta idea elimina la necesidad de asignar múltiples copias en la memoria, lo que libera mucha memoria en grandes sistemas multiusuario.

Llevando esta última innovación un paso más allá, alguien (no sé quién era, pero la idea era genial ;-) pensó que muy a menudo muchos programas usan la misma biblioteca, pero, al ser programas diferentes, partes de las mismas usan las bibliotecas no eran necesariamente las mismas partes utilizadas por otro programa. Además, el código principal era diferente (son programas diferentes), por lo que sus textos no fueron compartidos. Bueno, nuestro héroe pensó que si diferentes programas que usaban la misma biblioteca podían compartir esta biblioteca entre ellos, entonces podríamos ganar algo de memoria. Ahora, diferentes programas, con texto diferente, funcionan con el mismo código de biblioteca.

Sin embargo, el proceso ahora se ha vuelto más complicado. El programa ejecutable no está completamente vinculado, pero la resolución de las referencias del identificador de la biblioteca se pospone hasta que se carga el programa. El enlazador (ld (1) en el caso de Linux) reconoce las llamadas a bibliotecas compartidas y no incluye su código en el programa. El propio sistema, el kernel, al ejecutar exec () reconoce el lanzamiento de un programa usando bibliotecas compartidas y ejecuta código especial que carga bibliotecas compartidas (asignando memoria compartida para su texto, asignando memoria privada para valores de biblioteca, etc.). Ahora este proceso se realiza al cargar el archivo ejecutable y todo el procedimiento se ha vuelto mucho más complicado.

Por supuesto, cuando el enlazador encuentra una biblioteca normal, continúa comportándose como antes.

Una biblioteca compartida no es un archivo que contiene el código del objeto, sino un archivo que contiene el código del objeto en sí. Al vincular un programa con una biblioteca compartida, el vinculador no examina la biblioteca, qué módulos deben agregarse al programa y cuáles no. Solo se asegura de que los enlaces no resueltos se resuelvan y determina qué se debe agregar a la lista cuando se incluye la biblioteca. Es posible hacer una biblioteca de archivo ar (1) de todas las bibliotecas compartidas, pero esto a menudo no se hace, ya que las bibliotecas compartidas son a menudo el resultado de vincular diferentes módulos, por lo que la biblioteca se necesitará más adelante, en tiempo de ejecución. Quizás el nombre biblioteca compartida no sea el más apropiado y sería más exacto llamarlo objeto compartido (sin embargo, dado que es posible que no se nos entienda, no usamos este término).

4.- Tipos de bibliotecas.

Como dijimos, hay dos tipos de bibliotecas en Linux: estáticas y compartidas. Las bibliotecas estáticas son una colección de módulos que se combinan en un archivo usando la utilidad ar (1) y se indexan con la utilidad ranlib (1). Estos módulos a menudo se almacenan en un archivo que termina en .a (no uso el término extensión porque Linux no usa el concepto de extensión de archivo). El enlazador reconoce el final del archivo .a y comienza a buscar módulos como si fuera una biblioteca estática, y selecciona y agrega al programa aquellos módulos que resuelven referencias no resueltas.

A diferencia de las bibliotecas estáticas, las bibliotecas compartidas no son archivos, sino objetos reubicables, designados por un código especial (que las designa como bibliotecas compartidas). El enlazador ld (1), como ya se mencionó, no agrega módulos al código del programa, sino que selecciona los identificadores proporcionados por la biblioteca según lo permitido, agrega aquellos que son requeridos por la propia biblioteca y continúa trabajando sin agregar ningún código, asumiendo que el código requerido ya se ha agregado al código principal. El enlazador ld (1) reconoce las bibliotecas compartidas terminando con .so (no .so.xxx.yyy, lo discutiremos más adelante).

5.- El proceso de vinculación en Linux.

Cada programa consta de módulos de objetos vinculados a un archivo ejecutable. Esto lo hace el enlazador ld (1) utilizado en Linux.

ld (1) soporta varias opciones que cambian su comportamiento, pero nos limitaremos aquí solo a aquellas relacionadas con el uso de bibliotecas en general. ld (1) no es llamado directamente por el usuario, sino por el propio compilador gcc (1) en su etapa final. Poco conocimiento sobre el modus operandis ayúdanos a comprender la forma de utilizar las bibliotecas en Linux.

Para que ld (1) funcione correctamente, necesita una lista de objetos para vincular con el programa. Estos objetos se pueden especificar en cualquier orden (*) siempre que sigamos la convención anterior, como ya se mencionó, la biblioteca compartida es reconocida por el final .so (no .so.xx.yy) y la biblioteca estática por .a (y, por supuesto, los archivos de objetos simples son aquellos cuyo nombre termina en .o).

(*) Esto no es enteramente verdad. ld (1) incluye solo aquellos módulos que resuelven enlaces al momento de la inclusión de la biblioteca, por lo tanto, en un módulo incluido más adelante, aún puede haber un enlace que más adelante, ya que no aparece en el momento de la inclusión de esta biblioteca. , puede hacer que un comando incluya las bibliotecas necesarias ...

Por otro lado, ld (1) permite incluir bibliotecas estándar gracias a las opciones -l y -L.

Pero ... ¿Qué entendemos por biblioteca estándar, cuál es la diferencia? No. Solo que ld (1) busca bibliotecas estándar en ciertos lugares, mientras que las descritas en la lista de parámetros como objetos se buscan por sus nombres de archivo.

De forma predeterminada, las bibliotecas se buscan en los directorios / lib y / usr / lib (aunque he oído que dependiendo de la versión / implementación de ld (1), puede haber directorios adicionales). -L nos permite agregar directorios a los buscados por una búsqueda de biblioteca normal. Se usa especificando -L directorio para cada directorio que queremos agregar. Las bibliotecas estándar se especifican con la opción -l Name (donde Name especifica la biblioteca a cargar) y ld (1) buscará, en el orden apropiado, en los directorios apropiados un archivo llamado libName.so. Si no se encuentra, se intentará encontrar libName.a, su versión estática.

Si ld (1) encuentra libName.so, lo vinculará como una biblioteca compartida, mientras que si encuentra libName.a, vinculará los módulos derivados de él si resuelven cualquier referencia no resuelta.

La vinculación dinámica se realiza cuando el ejecutable es cargado por un módulo especial (de hecho, este módulo especial es la propia biblioteca compartida) llamado /lib/ld-linux.so.

En realidad, hay dos módulos para vincular bibliotecas dinámicas: /lib/ld.so (para bibliotecas que usan el antiguo formato a.out) y /lib/ld-linux.so (para bibliotecas que usan el nuevo formato ELF).

La peculiaridad de estos módulos es que deben cargarse cada vez que un programa se enlaza dinámicamente. Sus nombres son estándar (la razón es que no se pueden mover del directorio / lib y tampoco se pueden cambiar sus nombres). Si cambiamos el nombre /etc/ld-linux.so, automáticamente dejamos de usar cualquier programa que use bibliotecas compartidas, ya que este módulo se encarga de resolver todos los enlaces que no se resuelven en tiempo de ejecución.

El último módulo es ayudado por la existencia del archivo /etc/ld.so.cache, que especifica para cada biblioteca el ejecutable más apropiado que contiene esa biblioteca. Volveremos a este tema más adelante.

7.- soname. Versiones de biblioteca ejecutable. Compatibilidad.

Esto nos lleva al tema más confuso de las bibliotecas compartidas: sus versiones.

A menudo vemos el mensaje "biblioteca libX11.so.3 no encontrada", lo que nos deja confusos: al tener la biblioteca libX11.so.6, no podemos hacer nada. ¿Cómo es posible que ld.so (8) reconozca libpepe.so.45.0.1 y libpepe.so.45.22.3 indistintamente y no reconozca libpepe.so.46.22.3?

En Linux (y en todos los sistemas operativos que utilizan el formato ELF), las bibliotecas se identifican por su secuencia de caracteres distintiva: soname.

soname está incluido en la propia biblioteca, y esta secuencia se determina al vincular los objetos que componen la biblioteca. Al crear una biblioteca compartida, para dar un valor a esta cadena de caracteres, debe pasar la opción ld (1) (-soname<имя_библиотеки>).

El cargador dinámico utiliza esta secuencia de caracteres para identificar la biblioteca compartida que se cargará e identificar el ejecutable. Se parece a esto:
Ld-linux.so detecta que el programa requiere una biblioteca y define su nombre. Luego, se busca el nombre en /etc/ld.so.cache y se determina el nombre del archivo que contiene esta biblioteca. A continuación, el soname solicitado se compara con el nombre de la biblioteca existente, y si son idénticos, ¡lo necesitamos! De lo contrario, la búsqueda continuará hasta que se encuentre, o si no se encuentra, se mostrará un mensaje de error.

El soname se puede utilizar para determinar si la biblioteca es adecuada para cargar, porque ld-linux.so comprueba si el soname requerido es el mismo que el archivo requerido. En caso de diferencia, podemos obtener el famoso "libXXX.so.Y not found". Es el soname que se busca y el error devuelto lo determina el soname.

Si cambiamos el nombre de la biblioteca, puede haber mucha confusión y el problema persiste. Pero cambiar el soname tampoco es una buena idea, porque existe una convención en la comunidad de Linux para asignar soname:

El nombre de una biblioteca, por defecto, debe identificar la biblioteca correspondiente y la INTERFAZ de esa biblioteca. Si hacemos cambios en la biblioteca que afectan solo a la funcionalidad interna, pero la interfaz permanece sin cambios (número de funciones, variables, parámetros de función), entonces las dos bibliotecas serán intercambiables y en general diremos que los cambios fueron menores (ambos las bibliotecas son compatibles y podemos reemplazar una encima de la otra). Si esto sucede, entonces el número menor (que no es parte del soname) cambia con frecuencia y la biblioteca se puede reemplazar sin problemas importantes.

Sin embargo, cuando agregamos funciones, eliminamos funciones y, en general, CAMBIAMOS la INTERFAZ de la biblioteca, ya no es posible decir que esta biblioteca es intercambiable con la anterior (por ejemplo, reemplazando libX11.so.3 por libX11. so.6 es parte de la transición de X11R5 a X11R6, cuando esto introduce nuevas funciones y por lo tanto cambia la interfaz). Pasar de X11R6-v3.1.2 a X11R6-v3.1.3 probablemente no cambiará la interfaz y la biblioteca tendrá el mismo soname, aunque para mantener la versión anterior debemos darle un nombre diferente (por esta razón, el número de versión finaliza el nombre de la biblioteca, mientras que en el soname solo se especifican los números principales).

8.- ldconfig (8)

Como dijimos anteriormente, /etc/ld.so.cache permite a ld-linux.so convertir el nombre del archivo contenido en la biblioteca. Este es un archivo binario (por eficiencia) generado por la utilidad ldconfig (8).
ldconfig (8) crea un enlace simbólico con el nombre de la biblioteca soname para cada DLL que se encuentra en los directorios especificados en /etc/ld.so.conf. En este caso, cuando ld.so quiere obtener el nombre de un archivo, lo que hace es seleccionar el archivo con el soname requerido de la lista de directorios. Por lo tanto, no es necesario ejecutar ldconfig (8) cada vez que agrega una biblioteca. Solo ejecutamos ldconfig cuando agregamos un directorio a la lista.

9.- Quiero hacer una biblioteca dinámica.

Antes de empezar a crear una biblioteca dinámica, debemos pensar si es realmente necesaria. Las bibliotecas dinámicas provocan una sobrecarga del sistema por varias razones:
    La carga del programa se realiza en varias etapas; uno para cargar el programa principal, el resto para cada librería de enlaces dinámicos que utilice este programa (lo tendremos en cuenta para la librería de enlaces dinámicos correspondiente, ya que este último punto ya no es una rareza y se convierte en una ventaja).

    Los archivos DLL deben contener código reubicable porque la dirección asignada a un proceso en el espacio de direcciones virtuales se desconoce hasta que se cargan. En este caso, el compilador se ve obligado a reservar un registro para almacenar la posición de carga de la biblioteca y, como resultado, tenemos un registro menos para optimizar el código. Este no es un problema, ya que la sobrecarga resultante en la mayoría de los casos no es más del 5% de sobrecarga.

Para una biblioteca dinámica, el caso más aceptable es cuando algún programa la usa constantemente (esto ayuda a evitar descargar el texto de la biblioteca después de la finalización del proceso que lo cargó. Mientras que otros procesos usan los módulos de la biblioteca, permanece en la memoria) .

La biblioteca compartida está completamente cargada en la memoria (no solo los módulos que necesita), por lo que debe usarse en su totalidad para que sea útil. El peor ejemplo de uso de una biblioteca de enlaces dinámicos es usar solo una función, y el 90% de la biblioteca casi nunca se usa.

La biblioteca estándar de C es un buen ejemplo de una biblioteca de enlaces dinámicos (es utilizada por todos los programas escritos en C). Todas las funciones se utilizan en promedio.

Una biblioteca estática no necesita incluir funciones de uso poco frecuente; Siempre que estén contenidos en su propio módulo, no se vincularán con ningún programa que no los requiera.

9.1.- Compilación de códigos fuente
La compilación del código fuente es exactamente igual que para el código fuente normal, excepto que usaremos el "-f PIC" (código independiente de la posición).

Este paso es fundamental porque en una biblioteca estática la posición de los objetos de la biblioteca se resuelve durante la vinculación, por lo que lleva una cantidad fija de tiempo. Este paso no era posible en los antiguos binarios a.out, lo que resultó en que cada biblioteca compartida se colocara en una posición fija en el espacio de direcciones virtuales. Como resultado, podrían surgir conflictos en cualquier momento si el programa quisiera usar dos bibliotecas y las cargara en regiones superpuestas de memoria virtual. Esto significó que se vio obligado a mantener una lista en la que todos los que quisieran hacer que la biblioteca fuera dinámica tenían que declarar un rango de direcciones para que nadie más pudiera usarlo.

Como ya hemos señalado, no es necesario registrar una biblioteca dinámica en la lista oficial, ya que cuando se carga la biblioteca, se coloca en la posición definida en ese momento, a pesar de que el código debe ser movido.

9.2.- Vinculación de objetos a la biblioteca
Después de compilar todos los objetos, es necesario vincularlos especificando una opción que cree un objeto cargable dinámicamente. gcc -shared -o libName.so.xxx.yyy.zzz -Wl, -soname, libName.so.xxx biblioteca compartida. Tratemos cada uno por separado:
    -compartido.
    Esto le dice al enlazador que al final debe crear la biblioteca compartida y por lo tanto debe contener código ejecutable en el archivo de salida correspondiente a la biblioteca.

    O lib Name.so.xxx.yyy.zzz.
    Este es el nombre del archivo de salida. No es necesario seguir la convención de nomenclatura, pero si queremos que esta biblioteca se convierta en un estándar para el desarrollo futuro, es mejor seguirla.

    Wl, -soname, lib Name.so.xxx.
    La opción -Wl le dice a gcc (1) que siga las opciones (separadas por comas) que son para el enlazador. Este mecanismo es utilizado por gcc (1) para pasar opciones a ld (1). En el ejemplo, pasamos las siguientes opciones al enlazador:

-soname libName.so.xxx Esta opción cambia el soname de la biblioteca, por lo que esta biblioteca se cargará cuando lo soliciten los programas que requieran el soname especificado.
9.3.- Instalación de la biblioteca
Pues ya tenemos el archivo ejecutable correspondiente. Ahora, para poder usarlo, debe estar instalado en la ubicación adecuada.

Para compilar un programa que requiere nuestra nueva biblioteca, necesitamos usar el siguiente comando:

Gcc -o programa lib Name.so.xxx.yyy.zzz o, si la biblioteca se instaló en el directorio (/ usr / lib), será suficiente: gcc -o programa -l Nombre (si la biblioteca está en / usr / local / lib, luego agregue la opción "-L / usr / local / lib"). Para instalar la biblioteca, haga lo siguiente:

    Copie la biblioteca en el directorio / lib o / usr / lib. Si elige copiarlo en una ubicación diferente (por ejemplo, / usr / local / lib), no puede estar seguro de que el vinculador ld (1) lo encontrará automáticamente cuando vincule sus programas.

    Ejecute ldconfig (1) para crear un enlace simbólico libName.so.xxx.yyy.zzz a libName.so.xxx. En este paso, sabremos si hemos seguido correctamente todos los pasos anteriores y si la biblioteca se reconoce como dinámica. Este paso solo afecta la carga de la biblioteca en tiempo de ejecución, no la vinculación de programas.

    Para que el enlazador encuentre la biblioteca con la opción -l, cree un enlace simbólico desde libName.so.xxx.yyy.zzz (o libName.so.xxx, soname) a libName.so. Para que este mecanismo funcione, el nombre de la biblioteca debe coincidir con el patrón lib Name.so

10.- Creando una librería estática

Si, por otro lado, necesita crear una biblioteca estática (o necesita dos versiones para poder crear copias vinculadas estáticamente), debe hacer lo siguiente:

Nota: Al buscar bibliotecas, el vinculador primero busca un archivo llamado libName.so, y solo luego libName.a. Si llamamos a ambas bibliotecas (versiones estáticas y dinámicas) con el mismo nombre, en general será imposible determinar cuál de las dos se vinculará en cada caso (la dinámica siempre se vincula primero, ya que el vinculador la detecta primero ).

Por esta razón, si es necesario tener dos versiones de la misma biblioteca, siempre se recomienda nombrar la estática en la forma libName_s.a, y la dinámica libName.so. Luego, al vincular, deberá especificar:

Gcc -o programa -l Name_s para vincular con la versión estática, mientras que para la versión dinámica: gcc -o programa -l

10.1.- Compilando el Código Fuente
No es necesario realizar ningún paso especial para compilar el código fuente. Asimismo, la posición de los objetos se decide en la etapa de enlace, no es necesario compilar con la opción -f PIC (aunque es posible seguir usándola).
10.2.- Vinculación de objetos a la biblioteca
No se realiza ningún enlace para las bibliotecas estáticas. Todos los objetos se archivan en un archivo de biblioteca con el comando ar (1). Además, para resolver rápidamente los símbolos, es recomendable ejecutar el comando ranlib (1) en la biblioteca. Aunque no es necesario, la falla al ejecutar este comando puede desvincular módulos en el ejecutable porque cuando el enlazador procesa un módulo durante la creación de la biblioteca, no todas las dependencias indirectas entre módulos se resuelven inmediatamente: digamos, un módulo al final de un archivo necesita otro módulo en al principio del archivo, esto significa que se necesitan varias pasadas a través de la misma biblioteca para resolver todos los enlaces.
10.3.- Instalación de la biblioteca
Es recomendable nombrar las bibliotecas estáticas en el formato libName.a solo si desea tener solo bibliotecas estáticas. En el caso de dos tipos de bibliotecas, recomendaría llamarlas libName_s.a para que sea más fácil distinguir entre cuándo cargar una biblioteca estática y cuándo cargar una biblioteca dinámica.

El proceso de construcción le permite ingresar la opción -static. Esta opción controla la carga del módulo /lib/ld-linux.so y no afecta el orden de búsqueda de la biblioteca, por lo que si alguien especifica -static y ld (1) encuentra una biblioteca dinámica, funcionará con ella (y no sigue buscando biblioteca estática). Esto dará lugar a errores de tiempo de ejecución debido a llamadas a procedimientos en una biblioteca que no es parte del ejecutable; el módulo para la carga dinámica automática no está vinculado y, por lo tanto, el proceso no se puede ejecutar.

11.- Comparación de diseño estático y dinámico

Supongamos que queremos crear una distribución de un programa que usa una biblioteca que podemos distribuir solo estáticamente incluida en el programa, y ​​de ninguna otra forma (un ejemplo de este caso son las aplicaciones creadas con Motif).

Hay dos formas de crear un programa de este tipo. La primera es crear un ejecutable vinculado estáticamente (usando solo las bibliotecas .a y no usando un cargador dinámico). Este tipo de programas se carga una vez y no requiere ninguna biblioteca del sistema (ni siquiera /lib/ld-linux.so). Sin embargo, tienen la desventaja de que todo lo necesario debe guardarse en un archivo binario y, por lo tanto, suelen ser archivos muy grandes. La segunda opción es crear un programa enlazado dinámicamente, es decir, el entorno en el que se ejecutará nuestra aplicación debe tener todas las librerías dinámicas correspondientes. El ejecutable puede ser muy pequeño, aunque a veces es imposible tener absolutamente todas las bibliotecas (por ejemplo, hay personas que no tienen Motif).

Existe una tercera opción, mixta, en la que algunas bibliotecas están vinculadas dinámicamente y otras estáticamente. En este caso, sería lógico seleccionar la biblioteca en conflicto en su forma estática y todas las demás en su forma dinámica. Esta variante es una forma muy común de distribución de software.

Por ejemplo, puede compilar tres versiones diferentes de un programa de la siguiente manera:

Gcc -static -o programa.programa estático.o -lm_s -lXm_s -lXt_s -lX11_s \ -lXmu_s -lXpm_s gcc -o programa.programa dinámico.o -lm -lXm -lXt -lX11 -lXmu -lXpm gcc -o programa. programa mixto.o -lm -lXm_s -lXt -lX11 -lXmu -lXpm En el tercer caso, solo la biblioteca Motif (-lXm_s) está vinculada estáticamente y todas las demás están vinculadas dinámicamente. El entorno en el que se ejecutará el programa debe tener las versiones adecuadas de las bibliotecas libm.so.xx libXt.so.xx libX11.so.xx libXmu.so.xx y libXpm.so.xx

Biblioteca compartida o biblioteca compartida es un archivo destinado a ser compartido entre programas. Los módulos utilizados por el programa se cargan desde objetos compartidos separados en la memoria, en lugar de ser copiados por el enlazador cuando copia un solo archivo ejecutable para el programa.

Las bibliotecas compartidas se pueden vincular estáticamente, lo que significa que las referencias a los módulos de la biblioteca se resuelven y a los módulos se les asigna memoria cuando se crea el ejecutable. Pero a menudo la vinculación de bibliotecas compartidas se retrasa hasta que se cargan.

Algunos sistemas antiguos como MCP de Burroughs Multics también tiene un solo formato para archivos ejecutables, sean o no genéricos. Tienen archivos de biblioteca compartida en el mismo formato que los ejecutables. Esto tiene dos ventajas principales: primero, cada uno solo requiere un cargador de arranque, no dos (tener un cargador de arranque separado aporta complejidad adicional). En segundo lugar, también permite que los ejecutables se utilicen como bibliotecas compartidas si tienen una tabla de símbolos. Los formatos típicos para las bibliotecas ejecutables y compartidas combinadas son ELF y Mach-O (ambos en Unix) y (Windows).

En algunos entornos más antiguos como Windows de 16 bits o MPE por HP 3000, en el código con una biblioteca compartida, solo se permitieron datos basados ​​en pilas (locales), o se impusieron otras restricciones significativas en el código de la biblioteca compartida.

Memoria compartida

El código de la biblioteca se puede compartir en la memoria con los procesos, así como en el disco. Si se utiliza memoria virtual, los procesos se ejecutarán en una página física de RAM, que se asigna a diferentes espacios de direcciones de procesos. Esto tiene sus ventajas. Por ejemplo, en el sistema OpenStep, las aplicaciones suelen tener un tamaño de unos pocos cientos de kilobytes y se cargan rápidamente; la mayor parte de su código estaba en bibliotecas que el sistema operativo ya había cargado para otros fines.

Los programas pueden compartir RAM usando código independiente, como en Unix, lo que resulta en una arquitectura compleja pero flexible. Esto asegura que haya una mayor probabilidad de compartir a través de diversas técnicas, como la asignación previa del espacio de direcciones y la reserva de páginas para cada biblioteca compartida. La tercera opción es almacenamiento de un solo nivel usado por IBM System / 38 y sus sucesores.

En algunos casos, diferentes versiones de bibliotecas compartidas pueden causar problemas, especialmente cuando las bibliotecas de diferentes versiones tienen los mismos nombres de archivo y se utilizan para diferentes aplicaciones instaladas en el sistema, cada una requiere una versión específica. Tal escenario se conoce como

Una biblioteca compartida le permite usar los símbolos que contiene en varias películas sin copiar esos símbolos a las bibliotecas de películas. Debido a esto, los objetos de la biblioteca compartida se denominan recursos(Activos). En este caso, la biblioteca compartida se utiliza como un archivo externo y no se agrega a la película creada (o editada).

Se aconseja el uso de bibliotecas compartidas, por ejemplo, en los siguientes casos:

  • cuando se usa la misma banda sonora en varias páginas del sitio;
  • al usar caracteres de texto de la fuente en varias páginas del sitio;
  • cuando desee proporcionar una fuente única para los elementos de animación utilizados en varias escenas de una película o en varias películas;
  • cuando necesite tener una biblioteca central de recursos para facilitar el control de los cambios que realiza.

Flash MX admite dos tipos de bibliotecas compartidas:

  • Tiempo de ejecución- biblioteca de tiempo de ejecución compartida; los símbolos incluidos en dicha biblioteca están disponibles para ser compartidos por varias películas, sin embargo, dichos símbolos solo se pueden editar directamente en la película fuente;
  • Hora del autor- biblioteca compartida en el momento del desarrollo; los símbolos incluidos en dicha biblioteca están disponibles para ser compartidos por varias películas, y se permite editar el contenido de la biblioteca en cualquier película de copropietario.

Para que los recursos de la biblioteca compartida estén disponibles en películas alojadas en un sitio remoto, el archivo Flash con la biblioteca debe exportarse a formato SWF y cargarse en el sitio web.

Comentario
La versión anterior de Flash solo es compatible con la biblioteca de tiempo de ejecución compartida
.

Para crear una biblioteca compartida como Tiempo de ejecución, necesario:

  1. Determine sus recursos (símbolos incluidos en él) en una película separada.
  2. Permitir la exportación de caracteres compartidos.
  3. Especifique la URL del sitio donde se alojará la biblioteca.
  4. Exporte el archivo Flash con esta biblioteca a formato SWF y cárguelo en el sitio web.

Para poder utilizar símbolos de biblioteca compartida Tiempo de ejecución en otras películas ("copropietarios"), es necesario en cada una de ellas crear un enlace a los personajes compartidos.

Ahora veamos los pasos anteriores con más detalle.

Después de crear una biblioteca compartida, debe especificar qué símbolos incluidos se pueden exportar a otras películas. Esto requiere los siguientes pasos:

  1. Seleccione el símbolo de la lista que desea hacer "compartido".
  2. En el menú contextual del símbolo, seleccione el comando Enlace(Vinculante).
  3. En el cuadro de diálogo abierto Propiedades de enlace de símbolo(Parámetros de enlace de símbolos), Fig. 10.12, marque la casilla Exportar para compartir en tiempo de ejecución(Permitir exportar en tiempo de ejecución).
  4. En un cuadro de texto Identificador ingrese el nombre (identificador) del símbolo bajo el cual se exportará a la película del copropietario; aunque el nombre predeterminado es el símbolo de la biblioteca, si contiene espacios, elimínelos.
  5. En un cuadro de texto URL introduzca la dirección de Internet de la película de origen (es decir, el archivo SWF de la biblioteca compartida).
  6. Si el símbolo exportado se va a utilizar directamente desde el primer fotograma de la película del copropietario, seleccione el Exportar en el primer fotograma.
  7. Si desea que el símbolo exportado esté disponible en ActionScript, seleccione la Exportar para ActionScript.

Arroz. 10.12... Cuadro de diálogo Configuración de símbolo de biblioteca compartida

Para usar los recursos de la biblioteca compartida Tiempo de ejecución en una película de copropietario, se requieren los siguientes pasos:

  1. Abra la biblioteca de esta película eligiendo en el menú Ventana mando Biblioteca.
  2. En el menú desplegable de la biblioteca, elija el comando Nuevo símbolo; como resultado, aparecerá un cuadro de diálogo en la pantalla Crear nuevo símbolo(Crear un nuevo símbolo), cuya parte central es similar en formato al cuadro de diálogo Propiedades de enlace de símbolo(figura 10.13).
  3. En un cuadro de texto Identificador ingrese el nombre del símbolo que se va a importar a la película del copropietario.
  4. En un cuadro de texto URL introduzca la dirección de Internet de la película de origen.


Arroz. 10.13... Cuadro de diálogo para configurar los parámetros del carácter dividido

Bibliotecas estáticas y dinámicas.

Las bibliotecas se denominan "colecciones" de subrutinas u objetos, por regla general, centradas en resolver un conjunto de problemas relacionados. Desde el punto de vista de su organización y uso, las bibliotecas son estáticas y dinámicas.

Las bibliotecas estáticas (bibliotecas estáticas) pueden ser un conjunto de códigos fuente, incluidos por el programador en su programa, o en forma de archivos objeto precompilados, vinculados entre sí en la etapa de compilación. En Windows, estos archivos suelen tener la extensión<.lib>... Como resultado de la vinculación con una biblioteca estática, el programa incluye todas las funciones que utiliza, lo que aumenta su tamaño, pero lo hace más autónomo.

Las bibliotecas dinámicas (biblioteca compartida, biblioteca de vínculos dinámicos) son cargadas por el sistema operativo a "demanda" de un programa en ejecución ya durante su ejecución. Si la biblioteca requerida ya se ha cargado en la memoria, no se realiza la recarga. En este caso, el mismo conjunto de funciones u objetos de biblioteca puede ser utilizado simultáneamente por varios programas en ejecución, lo que hace posible utilizar eficientemente los recursos de la RAM. Las bibliotecas compartidas en Windows suelen tener la extensión<.dll>.

Para usar la biblioteca, necesita decirle al compilador que necesita conectarlo y llamar a la función desde la biblioteca (usando el archivo de encabezado apropiado), mientras que el texto fuente de la función no es necesario.

Creando una biblioteca dinámica.

En Microsoft Visual Studio, crear un proyecto para construir una biblioteca de vínculos dinámicos es similar al procedimiento habitual para crear un proyecto para una aplicación tonta. El tipo de proyecto es Aplicación de consola Win32, solo que ahora debe seleccionar el elemento "DLL" en el asistente de nuevo proyecto:

Si no existe tal elemento en el asistente, puede crear un proyecto de aplicación de consola normal y luego establecer su tipo en las propiedades del proyecto:

Propiedades de configuración => General => Tipo de configuración: Biblioteca dinámica (.dll)

Declspec (dllexport) visualización vacía (const char * str);

#include "dlltest.h"

visualización vacía (const char * str)

El modificador __declspec (dllexport) permite que la biblioteca exporte la función especificada para que la utilicen otras aplicaciones.

Como resultado de la construcción del proyecto, un archivo de biblioteca dinámica con la extensión<.dll>y también un archivo de biblioteca de importación con la extensión<.lib>... La biblioteca de importación está destinada a facilitar el uso posterior de la biblioteca dinámica. Aunque la extensión del archivo de la biblioteca de importación es la misma que la extensión estándar para las bibliotecas estáticas, no deben confundirse.

Usando una biblioteca dinámica.

Hay dos formas de utilizar una biblioteca dinámica en un programa. La vinculación implícita implica el uso de una biblioteca de importación para determinar las direcciones de las funciones proporcionadas por la biblioteca. El sistema operativo carga la DLL después de cargar el archivo ejecutable del programa. El archivo ejecutable llama a las funciones DLL exportadas como si las funciones estuvieran contenidas en el propio ejecutable.

Cuando se vincula explícitamente, el ejecutable que usa la DLL debe realizar llamadas a funciones para cargar y descargar explícitamente la DLL y acceder a las funciones de la DLL exportada. El ejecutable del cliente llama a las funciones exportadas utilizando un puntero de función.

Independientemente del método elegido, el archivo ejecutable puede utilizar la misma DLL. Además, estos mecanismos no son mutuamente excluyentes porque mientras un ejecutable está vinculado implícitamente a una DLL, otro puede vincularse explícitamente.

Como ejemplo, usaremos el mecanismo de enlace implícito (como el más simple) para conectar la biblioteca simple construida.

Creemos un proyecto separado de una aplicación de consola normal (es posible dentro del marco de la misma solución que el proyecto de la biblioteca en sí). Para utilizar la función de visualización implementada en la biblioteca, debe:

    Conecte el archivo de encabezado correspondiente "dlltest.h". Para hacer esto, use la ruta al archivo directamente en la directiva #include (si los proyectos están ubicados en la misma carpeta de la solución, es mejor usar una ruta relativa), o en las propiedades del proyecto agregue la ruta a este encabezado archivo en la sección

Parámetros de configuración => C / C ++ => General => Directorios de inclusión adicionales

    Utilice el archivo de biblioteca de importación adecuado. Para hacer esto, puede usar una directiva del formulario (aquí se usa la ruta relativa al archivo lib construido).

Alternativamente, puede agregar la ruta al archivo de la biblioteca de importación en las propiedades del proyecto en

Parámetros de configuración => Vinculador => Entrada => Dependencias adicionales

Ahora creemos el archivo de programa principal con el siguiente contenido:

#pragma comment (lib, "../Debug/dlltest.lib")

#include "dlltest.h"

display ("Hola");

sistema ("pausa");

Para iniciar con éxito el programa, todo lo que queda es asegurarse de que la DLL correspondiente esté disponible. Cuando se inicia el programa, el sistema operativo buscará todos los archivos de biblioteca necesarios en la carpeta desde la que se inició el programa, así como en las rutas del sistema definidas por la variable de entorno PATH. En consecuencia, el archivo DLL de la biblioteca construida debe estar en la misma carpeta que el archivo ejecutable del programa que realiza la llamada, o en una de las carpetas definidas en la RUTA.