Cuándo usar bibliotecas dinámicas frente a bibliotecas estáticas

Respuestas:

299

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 componentes que usan la misma biblioteca (múltiples cargas de datos, una carga de código).

La mayoría de las veces se consideraba que las bibliotecas dinámicas eran el mejor enfoque, 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).

Orion Adrian
fuente
71
En Windows / Mac (sin administrador de paquetes) realmente no hay una buena razón para usar bibliotecas dinámicas sobre estáticas. Dado que las DLL de Windows no son reubicables, el intercambio de código a menudo no funciona (y, de todos modos, cada aplicación se envía y usa sus propias versiones de la biblioteca). El único beneficio real es que es más fácil actualizar la biblioteca.
Zifre
55
En Mac, uso muchas bibliotecas dinámicas. por ejemplo, mac os x tiene sqlite3 embed. Creé un programa que tiene una función de base de datos sqlite3 para almacenar el rendimiento. sin embargo, debido a que rara vez se usa el enlace dinámico ahorra tiempo de compilación, hace que las pruebas sean más fáciles / rápidas, sin embargo, si tuviera que construir una versión de lanzamiento, creo que siempre usaría una biblioteca estática en caso de problemas de compatibilidad
ReachConnection
66
@Zifre: relocatable = se puede cargar en diferentes direcciones virtuales. DLL ciertamente es compatible con esto.
dma_k
20
@dma_k: las DLL de Windows se pueden cargar en diferentes direcciones, pero solo porque el vinculador copia todo el código y cambia los números de dirección. Con los objetos compartidos, todas las referencias de dirección son relativas, por lo que múltiples procesos pueden compartir la misma memoria para el objeto compartido. En otras palabras, en Windows, una DLL de 1 MB utilizada por 3 programas = 3 MB. En Linux, A MB SO utilizado por 3 programas = 1 MB.
Zifre
77
Tanto Windows como Linux tienen el concepto de reubicación de tiempo de carga de bibliotecas compartidas. Eli.thegreenplace.net/2011/08/25/… Lo más importante que permitió el código de posición independiente no fue algo especial para Linux, sino más bien el direccionamiento relativo a RIP agregado con el conjunto de instrucciones x64; tanto Windows como Linux pueden hacer uso del direccionamiento relativo de RIP si reducen el número de reparaciones al reubicar bibliotecas.
clemahieu
194

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 debe 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 con 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.libcontiene a.objy b.objy su DLL / EXE solo hace referencia a funciones o variables a.obj, b.objel enlazador descartará la totalidad de ellas . Si b.objcontiene objetos globales / estáticos, sus constructores y destructores no se ejecutarán. Si esos constructores / destructores tienen efectos secundarios, puede ser 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 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 enlace 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: puede desear un PDB separado para cada biblioteca estática, o puede desear 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 varios type_infoobjetos para la misma clase si vincula una única biblioteca estática en varias DLL. Si el programa asume que type_infoson datos "simples" y los usos &typeid()o type_info::before(), puede obtener resultados no deseados y sorprendentes.

bk1e
fuente
23
En cuanto al punto sobre los singletons, no olvide que una DLL puede cargarse varias veces (la misma versión o múltiples versiones) y todavía no hay garantía de singleton.
Orion Adrian
Punto adicional sobre la eliminación de código sin referencia: las llamadas realizadas a DLL también requieren una llamada real para forzar la carga de la DLL referenciada. Agregándolo como referencia, pero sin incluir ninguna llamada que haga referencia, obtendrá el mismo resultado que tener una biblioteca estática que no llama a nada. La única diferencia es lo que realmente se envía. En ambos casos, los constructores y destructores estáticos no disparan.
Orion Adrian el
@ bk1e Eso no debería suceder. el .a siempre contendrá todos los símbolos con los que fue construido. Cuando está vinculado estáticamente a su aplicación, sí, solo se vincularán los símbolos que se utilizan.
Miles Rout
62

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 utilizar para reutilizar / compartir código entre varios productos; cargar en la memoria de proceso a pedido y puede descargarse cuando no sea necesario; se puede actualizar independientemente del resto del programa.

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

Pros profesionales : sin 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.

Franci Penov
fuente
La reformulación también se puede realizar en tiempo de compilación utilizando rebase.exe o pasando la opción / BASE a link.exe. Si esto es efectivo depende de si hay conflictos inesperados de espacio de direcciones en tiempo de ejecución.
bk1e
24

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 dinámica que permite compartir código entre varios ejecutables diferentes), existen 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 código LGPL de fuente abierta ( libre como en libertad) . Si se vincula con objetos compartidos, solo necesita LGPL las mejoras / correcciones de errores que realice en la 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).

rburhum
fuente
23

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 construye 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 cambio, compiló un dll (y lo compiló correctamente ), ha creado un programa completo que todos los consumidores pueden usar, sin importar qué compilador estén utilizando. Sin embargo, existen varias restricciones para exportar desde un archivo dll, si se desea compatibilidad cruzada con el compilador.

tcb
fuente
1
Esto es nuevo para mí. ¿Qué restricciones existen con los compiladores cruzados cuando se usan DLL? Hacer que el programador compile sin necesitar la misma cadena de herramientas parece una gran ventaja para las DLL
Dan
1
Esta respuesta es informativa. Agregar una advertencia menor: consumers of your static library will have to use the same compiler that you usedsi la biblioteca estática usa la biblioteca C ++, como #include <iostream>.
truthadjustr
no se puede consumir un dll de c ++ a menos que se use el mismo compilador (debido a que no hay abi estándar de c ++, los símbolos se alteran de diferentes maneras). Tanto el dll como el módulo del cliente deben usar el mismo compilador y la misma configuración de compilación
kcris
19

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]>
Vijay
fuente
13

Una biblioteca estática se compila en el cliente. Un .lib se usa 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.

Jordan Parmer
fuente
13

Debe pensar cuidadosamente 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 usa la lib estática.

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

Corey Trager
fuente
9

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.

Debería usar una DLL cuando desee 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.

chinche
fuente
También puede usar una DLL cuando varias otras aplicaciones usan la misma funcionalidad; esto puede reducir la huella.
Tim
Además, extender su concepto inicial, la arquitectura "plug-in" donde desea permitir la funcionalidad agregada / desconocida más adelante sin tener que reconstruir o volver a lanzar, solo se puede hacer con bibliotecas dinámicas.
Tim
8

El 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" (DSO). Se centra más en las bibliotecas compartidas en el formato binario ELF , pero algunas discusiones también son adecuadas para las DLL de Windows.

Vacío
fuente
5

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

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

Rob Wells
fuente
4

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 lo necesite para morder la bala y hacerlo por adelantado, o el enlazador dinámico puede hacerlo en el momento de la carga.

pfranza
fuente
3

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.

Dima
fuente
3

Si la biblioteca es estática, en el momento del enlace, el código está vinculado con su ejecutable. Esto hace que tu ejecutable sea más grande (que si fueras a 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, la dirección de carga preferida, entre otras cosas.

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

Seb Rose
fuente
3

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)
  • Las DLL tuvieron que ser versionadas, 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)
  • optimizer 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

Tal vez 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 deben estar presentes en el ejecutable combinado final solo una vez. ¿Alguien puede apoyar esta pregunta?

gast128
fuente
¿No está destinado a utilizar algunas directivas de precompilador de alguna manera para evitar la duplicación de código?
Paceman
La precompilación de Afaiac solo funciona en una base por módulo (exe / dll / lib). La precompilación está destinada principalmente a acelerar la compilación, aunque también evita múltiples inclusiones dentro de una unidad de compilación. Sin embargo, incluir guardias son la mejor manera de lograr este efecto.
gast128
2

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 buscando 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.

Terence Simpson
fuente
2

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.

Robert Gould
fuente
2

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 convertirlas en bibliotecas estáticas. Las bibliotecas dinámicas realmente no te 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.

the_mandrill
fuente