Pregunta

Al crear una biblioteca de clases en C ++, puede elegir entre dinámico ( .dll , .so ) y estático ( .lib , .a ) bibliotecas. ¿Cuál es la diferencia entre ellos y cuándo es apropiado usar cuál?

¿Fue útil?

Solución

Las bibliotecas estáticas aumentan el tamaño del código en su binario. Siempre se cargan y cualquier versión del código que compiló es la versión del código que se ejecutará.

Las bibliotecas dinámicas se almacenan y versionan por separado. Es posible que se cargue una versión de la biblioteca dinámica que no era la original que se envió con su código si la actualización se considera binariamente compatible con la versión original.

Además, las bibliotecas dinámicas no se cargan necesariamente, generalmente se cargan cuando se llaman por primera vez, y se pueden compartir entre los componentes que usan la misma biblioteca (múltiples cargas de datos, una carga de código).

Las bibliotecas dinámicas se consideraban el mejor enfoque la mayoría de las veces, pero originalmente tenían una falla importante (google DLL hell), que prácticamente ha sido eliminada por los sistemas operativos Windows más recientes (Windows XP en particular).

Otros consejos

Otros han explicado adecuadamente qué es una biblioteca estática, pero me gustaría señalar algunas de las advertencias del uso de bibliotecas estáticas, al menos en Windows:

  • Singletons: Si algo necesita ser global / estático y único, tenga mucho cuidado al colocarlo en una biblioteca estática. Si se vinculan varias DLL contra esa biblioteca estática, cada una obtendrá su propia copia del singleton. Sin embargo, si su aplicación es un único EXE sin archivos DLL personalizados, esto puede no ser un problema.

  • Eliminación de código sin referencia: cuando se vincula a una biblioteca estática, solo las partes de la biblioteca estática a las que hace referencia su DLL / EXE se vincularán a su DLL / EXE.

    Por ejemplo, si mylib.lib contiene a.obj y b.obj y su DLL / EXE solo hace referencia a funciones o variables de a.obj , el enlazador descartará la totalidad de b.obj . Si b.obj contiene objetos globales / estáticos, sus constructores y destructores no se ejecutarán. Si esos constructores / destructores tienen efectos secundarios, puede estar decepcionado por su ausencia.

    Del mismo modo, si la biblioteca estática contiene puntos de entrada especiales, es posible que deba asegurarse de que realmente estén incluidos. Un ejemplo de esto en la programación incrustada (está bien, no en Windows) sería un controlador de interrupciones que está marcado como una dirección específica. También debe marcar el controlador de interrupciones como un punto de entrada para asegurarse de que no se descarte.

    Otra consecuencia de esto es que una biblioteca estática puede contener archivos de objetos que son completamente inutilizables debido a referencias no resueltas, pero no causará un error de vinculador hasta que haga referencia a una función o variable de esos archivos de objetos. Esto puede suceder mucho después de que se escriba la biblioteca.

  • Símbolos de depuración: es posible que desee un PDB separado para cada biblioteca estática, o puede querer que los símbolos de depuración se coloquen en los archivos de objeto para que se enrollen en el PDB para la DLL / EXE. La documentación de Visual C ++ explica las opciones necesarias .

  • RTTI: puede terminar con múltiples objetos type_info para la misma clase si vincula una única biblioteca estática en múltiples DLL. Si su programa asume que type_info es " singleton " datos y utiliza & amp; typeid () o type_info :: before () , puede obtener resultados no deseados y sorprendentes.

Una lib es una unidad de código que se incluye dentro del ejecutable de su aplicación.

Un dll es una unidad independiente de código ejecutable. Se carga en el proceso solo cuando se realiza una llamada a ese código. Un dll puede ser utilizado por múltiples aplicaciones y cargado en múltiples procesos, mientras que solo tiene una copia del código en el disco duro.

Pros Dll : se puede usar para reutilizar / compartir código entre varios productos; cargar en la memoria de proceso a pedido y puede descargarse cuando no sea necesario; puede actualizarse independientemente del resto del programa.

Contras Dll : impacto en el rendimiento de la carga de dll y el rebase del código; problemas de versiones (" dll hell ")

Pros profesionales : no tiene impacto en el rendimiento ya que el código siempre se carga en el proceso y no se modifica. sin problemas de versiones.

Contras de Lib : ejecutable / proceso " bloat " - todo el código está en su ejecutable y se carga al inicio del proceso; sin reutilizar / compartir: cada producto tiene su propia copia del código.

Además de las implicaciones técnicas de las bibliotecas estáticas vs dinámicas (los archivos estáticos agrupan todo en una gran biblioteca binaria vs bibliotecas dinámicas que permiten compartir código entre varios ejecutables diferentes), están las implicaciones legales .

Por ejemplo, si está utilizando un código con licencia LGPL y se vincula estáticamente con una biblioteca LGPL (y, por lo tanto, crea un gran binario), su código se convierte automáticamente en Open Sourced ( gratis como en libertad) código LGPL. Si se vincula con objetos compartidos, entonces solo necesita LGPL las mejoras / correcciones de errores que realice en la propia biblioteca LGPL.

Esto se convierte en un tema mucho más importante si está decidiendo cómo compilar sus aplicaciones móviles, por ejemplo (en Android tiene la opción de estático frente a dinámico, en iOS no, siempre es estático).


Crear una biblioteca estática

$:~/static [32]> cat foo.c
#include<stdio.h>
void foo()
{
printf("\nhello world\n");
}
$:~/static [33]> cat foo.h
#ifndef _H_FOO_H
#define _H_FOO_H

void foo();

#endif
$:~/static [34]> cat foo2.c
#include<stdio.h>
void foo2()
{
printf("\nworld\n");
}
$:~/static [35]> cat foo2.h
#ifndef _H_FOO2_H
#define _H_FOO2_H

void foo2();

#endif
$:~/static [36]> cat hello.c
#include<foo.h>
#include<foo2.h>
void main()
{
foo();
foo2();
}
$:~/static [37]> cat makefile
hello: hello.o libtest.a
        cc -o hello hello.o -L. -ltest
hello.o: hello.c
        cc -c hello.c -I`pwd`
libtest.a:foo.o foo2.o
        ar cr libtest.a foo.o foo2.o
foo.o:foo.c
        cc -c foo.c
foo2.o:foo.c
        cc -c foo2.c
clean:
        rm -f foo.o foo2.o libtest.a hello.o

$:~/static [38]>

creando una biblioteca dinámica

$:~/dynamic [44]> cat foo.c
#include<stdio.h>
void foo()
{
printf("\nhello world\n");
}
$:~/dynamic [45]> cat foo.h
#ifndef _H_FOO_H
#define _H_FOO_H

void foo();

#endif
$:~/dynamic [46]> cat foo2.c
#include<stdio.h>
void foo2()
{
printf("\nworld\n");
}
$:~/dynamic [47]> cat foo2.h
#ifndef _H_FOO2_H
#define _H_FOO2_H

void foo2();

#endif
$:~/dynamic [48]> cat hello.c
#include<foo.h>
#include<foo2.h>
void main()
{
foo();
foo2();
}
$:~/dynamic [49]> cat makefile
hello:hello.o libtest.sl
        cc -o hello hello.o -L`pwd` -ltest
hello.o:
        cc -c -b hello.c -I`pwd`
libtest.sl:foo.o foo2.o
        cc -G -b -o libtest.sl foo.o foo2.o
foo.o:foo.c
        cc -c -b foo.c
foo2.o:foo.c
        cc -c -b foo2.c
clean:
        rm -f libtest.sl foo.o foo

2.o hello.o
$:~/dynamic [50]>

Los programas C ++ se construyen en dos fases

  1. Compilación - produce código objeto (.obj)
  2. Vinculación: produce código ejecutable (.exe o .dll)

La biblioteca estática (.lib) es solo un paquete de archivos .obj y, por lo tanto, no es un programa completo. No ha pasado por la segunda fase (vinculación) de la construcción de un programa. Dlls, por otro lado, son como exe's y, por lo tanto, son programas completos.

Si crea una biblioteca estática, aún no está vinculada y, por lo tanto, los consumidores de su biblioteca estática tendrán que usar el mismo compilador que usó (si usó g ++, tendrán que usar g ++).

Si en su lugar construyó un dll (y lo construyó correctamente ), ha creado un programa completo que todos los consumidores pueden usar, sin importar qué compilador estén usando. Sin embargo, existen varias restricciones para exportar desde un archivo dll, si se desea compatibilidad cruzada con el compilador.

Debe pensar detenidamente sobre los cambios a lo largo del tiempo, el control de versiones, la estabilidad, la compatibilidad, etc.

Si hay dos aplicaciones que usan el código compartido, ¿desea forzar que esas aplicaciones cambien juntas, en caso de que necesiten ser compatibles entre sí? Luego usa el dll. Todos los exe utilizarán el mismo código.

¿O quieres aislarlos unos de otros, para que puedas cambiar uno y estar seguro de que no has roto el otro? Luego use la lib estática.

El infierno de DLL es cuando probablemente DEBERÍAS HABER UTILIZADO una lib estática, pero en su lugar usaste un dll, y no todos los exes son comaptibles con él.

Una biblioteca estática se compila en el cliente. Se utiliza un archivo .lib en tiempo de compilación y el contenido de la biblioteca se convierte en parte del ejecutable consumidor.

Una biblioteca dinámica se carga en tiempo de ejecución y no se compila en el ejecutable del cliente. Las bibliotecas dinámicas son más flexibles ya que múltiples ejecutables de clientes pueden cargar un archivo DLL y utilizar su funcionalidad. Esto también mantiene el tamaño general y la capacidad de mantenimiento de su código de cliente al mínimo.

Una biblioteca estática debe estar vinculada al ejecutable final; se convierte en parte del ejecutable y lo sigue a donde quiera que vaya. Se carga una biblioteca dinámica cada vez que se ejecuta el ejecutable y permanece separada del ejecutable como un archivo DLL.

Usaría una DLL cuando quiera poder cambiar la funcionalidad proporcionada por la biblioteca sin tener que volver a vincular el ejecutable (simplemente reemplace el archivo DLL, sin tener que reemplazar el archivo ejecutable).

Usaría una biblioteca estática siempre que no tenga una razón para usar una biblioteca dinámica.

Artículo de Ulrich Drepper sobre " Cómo escribir bibliotecas compartidas " También es un buen recurso que detalla la mejor manera de aprovechar las bibliotecas compartidas, o lo que él denomina "Objetos dinámicos compartidos". (DSOs). Se centra más en las bibliotecas compartidas en el ELF formato binario, pero algunas discusiones son adecuadas para las DLL de Windows como bien.

Para una excelente discusión de este tema, lea este artículo de Sun.

Incluye todos los beneficios, incluida la posibilidad de insertar bibliotecas de interposición. Puede encontrar más detalles sobre la interposición en este artículo aquí .

Realmente la compensación que está haciendo (en un proyecto grande) está en el tiempo de carga inicial, las bibliotecas se vincularán en un momento u otro, la decisión que se debe tomar es si el enlace tomará el tiempo suficiente para que el compilador necesita morder la bala y hacerlo por adelantado, o el enlazador dinámico puede hacerlo en el momento de la carga.

Si su biblioteca se va a compartir entre varios ejecutables, a menudo tiene sentido hacerlo dinámico para reducir el tamaño de los ejecutables. De lo contrario, definitivamente hazlo estático.

Hay varias desventajas de usar un dll. Hay gastos generales adicionales para cargarlo y descargarlo. También hay una dependencia adicional. Si cambia el dll para que sea incompatible con sus ejecutables, dejarán de funcionar. Por otro lado, si cambia una biblioteca estática, sus ejecutables compilados usando la versión anterior no se verán afectados.

Si la biblioteca es estática, en el momento del enlace el código está vinculado con su ejecutable. Esto hace que su ejecutable sea más grande (que si fuera la ruta dinámica).

Si la biblioteca es dinámica, en el momento del enlace se incorporan referencias a los métodos requeridos en su ejecutable. Esto significa que debe enviar su ejecutable y la biblioteca dinámica. También debe considerar si el acceso compartido al código en la biblioteca es seguro, dirección de carga preferida, entre otras cosas.

Si puede vivir con la biblioteca estática, vaya con la biblioteca estática.

Las bibliotecas estáticas son archivos que contienen el código objeto de la biblioteca, cuando se vincula a una aplicación, ese código se compila en el ejecutable. Las bibliotecas compartidas son diferentes en el sentido de que no se compilan en el ejecutable. En cambio, el vinculador dinámico busca en algunos directorios en busca de la (s) biblioteca (s) que necesita, luego lo carga en la memoria. Más de un ejecutable puede usar la misma biblioteca compartida al mismo tiempo, lo que reduce el uso de memoria y el tamaño del ejecutable. Sin embargo, hay más archivos para distribuir con el ejecutable. Debe asegurarse de que la biblioteca esté instalada en el sistema de usos en algún lugar donde el vinculador pueda encontrarla, la vinculación estática elimina este problema pero da como resultado un archivo ejecutable más grande.

Si su trabajo en proyectos embebidos o plataformas especializadas, las bibliotecas estáticas son el único camino a seguir, también muchas veces son menos difíciles de compilar en su aplicación. También tener proyectos y archivos MAKE que incluyen todo hace la vida más feliz.

Usamos muchas DLL (> 100) en nuestro proyecto. Estas DLL tienen dependencias entre sí y, por lo tanto, elegimos la configuración de la vinculación dinámica. Sin embargo, tiene las siguientes desventajas:

  • inicio lento (> 10 segundos)
  • DLL's tuvo que ser versionado, ya que Windows carga módulos en la unicidad de los nombres. Los propios componentes escritos obtendrían la versión incorrecta de la DLL (es decir, la que ya está cargada en lugar de su propio conjunto distribuido)
  • El optimizador
  • solo puede optimizar dentro de los límites de DLL. Por ejemplo, el optimizador intenta colocar datos y códigos de uso frecuente uno al lado del otro, pero esto no funcionará a través de los límites de DLL

Quizás una mejor configuración fue hacer de todo una biblioteca estática (y, por lo tanto, solo tiene un ejecutable). Esto funciona solo si no se produce una duplicación de código. Una prueba parece respaldar esta suposición, pero no pude encontrar una cotización oficial de MSDN. Entonces, por ejemplo, haga 1 exe con:

  • exe usa shared_lib1, shared_lib2
  • shared_lib1 use shared_lib2
  • shared_lib2

El código y las variables de shared_lib2 deberían estar presentes en el ejecutable combinado final solo una vez. ¿Alguien puede apoyar esta pregunta?

Daría una regla general que dice que si tiene una base de código grande, todo construido sobre bibliotecas de nivel inferior (por ejemplo, un marco Utils o Gui), que desea dividir en bibliotecas más manejables y luego hacerlas estáticas bibliotecas Las bibliotecas dinámicas realmente no le compran nada y hay menos sorpresas: solo habrá una instancia de singletons, por ejemplo.

Si tiene una biblioteca que está completamente separada del resto de la base de código (por ejemplo, una biblioteca de terceros), considere hacerla una dll. Si la biblioteca es LGPL, es posible que deba usar un dll de todos modos debido a las condiciones de licencia.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top