c++ - programacion - Cuándo usar bibliotecas dinámicas vs. estáticas
diferencia entre biblioteca y libreria en programacion (18)
Creando 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]>
Al crear una biblioteca de clases en C ++, puede elegir entre bibliotecas dinámicas (.dll) y estáticas (.lib). ¿Cuál es la diferencia entre ellos y cuándo es apropiado usar cuál?
Además de las implicaciones técnicas de las bibliotecas estáticas y dinámicas (los archivos estáticos agrupan todo en una gran biblioteca binaria frente a las bibliotecas dinámicas que permiten 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 de forma estática con una biblioteca LGPL (y, por lo tanto, crea un gran binario), su código se convierte automáticamente en código LGPL de código abierto ( libre como en libertad) . Si se vincula con un objeto compartido, 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 contra dinámico, en iOS no lo hace, siempre es estático).
Debe pensar detenidamente sobre los cambios a lo largo del tiempo, las versiones, la estabilidad, la compatibilidad, etc.
Si hay dos aplicaciones que usan el código compartido, ¿desea forzarlas a que cambien juntas, en caso de que deban ser compatibles entre sí? A continuación, utilice la dll. Todos los exes estarán usando el mismo código.
¿O quiere aislarlos unos de otros para poder cambiar uno y tener la seguridad de que no ha roto el otro? Luego usa la biblioteca estática.
El infierno de DLL es cuando probablemente DEBERÍAS haber usado una biblioteca estática, pero en su lugar usaste una DLL, y no todos los exes son compatibles con ella.
El documento de Ulrich Drepper sobre " Cómo escribir bibliotecas compartidas " también es un buen recurso que detalla cómo aprovechar mejor las bibliotecas compartidas, o lo que él denomina "objetos compartidos dinámicos" (DSO). Se enfoca más en las bibliotecas compartidas en el formato binario ELF , pero algunas discusiones también son adecuadas para las DLL de Windows.
Las bibliotecas estáticas aumentan el tamaño del código en su binario. Siempre se cargan y cualquiera que sea la 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 fue la original que se envió con su código si la actualización se considera compatible con la versión original.
Además, las bibliotecas dinámicas no necesariamente se cargan (generalmente se cargan cuando se llaman por primera vez) y se pueden compartir entre componentes que usan la misma biblioteca (cargas de datos múltiples, carga de un código).
Las bibliotecas dinámicas se consideraron el mejor enfoque la mayor parte del tiempo, pero originalmente tenían una falla importante (google DLL hell), que ha sido eliminada por los sistemas operativos más recientes de Windows (Windows XP en particular).
Las bibliotecas estáticas son archivos que contienen el código objeto de la biblioteca, cuando se vinculan a una aplicación que el código se compila en el ejecutable. Las bibliotecas compartidas son diferentes en que no están compiladas en el ejecutable. En su lugar, el enlazador dinámico busca algunos directorios en busca de las bibliotecas que necesita, y luego las 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, el enlace estático elimina este problema, pero da como resultado un archivo ejecutable más grande.
Le daría una regla general de que si tiene una base de código grande, todas ellas construidas sobre bibliotecas de nivel inferior (por ejemplo, un marco Utils o Gui), que desea particionar en bibliotecas más manejables, entonces conviértalas en bibliotecas estáticas. Las bibliotecas dinámicas realmente no te compran nada y hay menos sorpresas, por ejemplo, solo habrá una instancia de singletons.
Si tiene una biblioteca que está completamente separada del resto de la base de código (por ejemplo, una biblioteca de terceros), considere convertirla en una dll. Si la biblioteca es LGPL, es posible que deba usar una DLL de todas formas debido a las condiciones de la licencia.
Los programas de C ++ se construyen en dos fases
- Compilación - produce código objeto (.obj)
- Enlace: 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 se ha sometido a la segunda fase (de enlace) de construir un programa. Dlls, por otro lado, son como exe y por lo tanto son programas completos.
Si creas una biblioteca estática, aún no está vinculada y, por lo tanto, los consumidores de tu biblioteca estática tendrán que usar el mismo compilador que usaste (si usaste g ++, tendrán que usar g ++).
Si en cambio construiste una DLL (y la construiste correctly ), construiste un programa completo que todos los consumidores pueden usar, sin importar qué compilador estén usando. Sin embargo, hay varias restricciones al exportar desde una dll, si se desea la compatibilidad entre compiladores.
Otros han explicado adecuadamente qué es una biblioteca estática, pero me gustaría señalar algunas de las advertencias de usar bibliotecas estáticas, al menos en Windows:
Singletons: si algo debe ser global / estático y único, tenga mucho cuidado de ponerlo en una biblioteca estática. Si se vinculan múltiples DLL contra esa biblioteca estática, cada uno obtendrá su propia copia del singleton. Sin embargo, si su aplicación es un solo EXE sin DLL personalizados, esto puede no ser un problema.
Eliminación de código no referenciado: 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.lib
contienea.obj
yb.obj
y su DLL / EXE solo hace referencia a funciones o variables dea.obj
, elb.obj
descartará la totalidad deb.obj
. Sib.obj
contiene objetos globales / estáticos, sus constructores y destructores no se ejecutarán. Si esos constructores / destructores tienen efectos secundarios, puede que te sientas decepcionado por su ausencia.Del mismo modo, si la biblioteca estática contiene puntos de entrada especiales, es posible que deba tener cuidado de que realmente estén incluidos. Un ejemplo de esto en la programación incrustada (bueno, no Windows) sería un controlador de interrupción que está marcado como una dirección específica. También debe marcar el controlador de interrupción 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 independiente para cada biblioteca estática o que los símbolos de depuración se coloquen en los archivos de objetos para que se incorporen al PDB para el DLL / EXE. La documentación de Visual C ++ explica las opciones necesarias .
RTTI: puede terminar con varios objetos
type_info
para la misma clase si vincula una única biblioteca estática en múltiples DLL. Si su programa asume quetype_info
es información de "singleton" y usa&typeid()
otype_info::before()
, puede obtener resultados indeseables y sorprendentes.
Para una excelente discusión de este tema, lea este artículo de Sun.
Incluye todos los beneficios, incluido el poder insertar bibliotecas de interposición. Más detalles sobre la interposición se pueden encontrar en este artículo aquí .
Realmente la transacción que está realizando (en un proyecto grande) está en el tiempo de carga inicial, las bibliotecas se enlazarán en un momento u otro, la decisión que se debe tomar es si el enlace tardará el tiempo suficiente para que el compilador necesite para morder la bala y hacerlo por adelantado, o puede hacerlo el enlazador dinámico en tiempo de carga.
Si la biblioteca es estática, en el momento del enlace el código se vincula con su ejecutable. Esto hace que tu ejecutable sea más grande (que si fueras 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 puedes vivir con la biblioteca estática, ve con la biblioteca estática.
Si su biblioteca va a ser compartida entre varios ejecutables, a menudo tiene sentido hacerla dinámica para reducir el tamaño de los ejecutables. De lo contrario, definitivamente hacerlo estático.
Hay varias desventajas de usar un dll. Hay gastos generales adicionales para cargarlo y descargarlo. También hay una dependencia adicional. Si cambias la dll para que sea incompatible con tus ejecutales, dejarán de funcionar. Por otro lado, si cambia una biblioteca estática, sus ejecutables compilados que usan la versión anterior no se verán afectados.
Si su trabajo en proyectos integrados o plataformas estáticas, las bibliotecas son la única manera de hacerlo, también muchas veces son menos problemáticas para compilarlas en su aplicación. También tener proyectos y makefile que incluyen todo hace que la vida sea más feliz.
Una biblioteca estática debe estar vinculada al ejecutable final; se convierte en parte del ejecutable y lo sigue donde quiera que vaya. Una biblioteca dinámica se carga cada vez que se ejecuta el ejecutable y permanece separada del ejecutable como un archivo DLL.
Utilizaría una DLL cuando desee poder cambiar la funcionalidad proporcionada por la biblioteca sin tener que volver a vincular el archivo 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.
Una biblioteca estática se compila en el cliente. Una .lib se usa en tiempo de compilación y los contenidos de la biblioteca se convierten 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 varios ejecutables de clientes pueden cargar una DLL y utilizar su funcionalidad. Esto también mantiene el tamaño total y la capacidad de mantenimiento de su código de cliente al mínimo.
Una lib es una unidad de código que se incluye en el ejecutable de la aplicación.
Una dll es una unidad independiente de código ejecutable. Se carga en el proceso solo cuando se realiza una llamada a ese código. Las aplicaciones múltiples pueden usar una dll y cargarla en varios procesos, mientras que aún tiene una sola copia del código en el disco duro.
Dll pros : se puede utilizar para reutilizar / compartir códigos entre varios productos; cargue en la memoria de proceso a pedido y se puede descargar 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 rebasado de código; Problemas de versionamiento ("dll hell")
Pros : no hay impacto en el rendimiento ya que el código siempre se carga en el proceso y no se vuelve a basar; No hay problemas de versionamiento.
Contras de lib : ejecutable / proceso "bloat": todo el código está en su ejecutable y se carga al inicio del proceso; no reutilizar / compartir - cada producto tiene su propia copia del código.
Usamos muchos archivos 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:
- arranque lento (> 10 segundos)
- Los archivos DLL debían ser versionados, ya que Windows carga módulos sobre la singularidad 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 los datos y el código que se usan con frecuencia uno al lado del otro, pero esto no funcionará más allá 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 tienes un ejecutable). Esto funciona solo si no hay duplicación de código. Una prueba parece apoyar esta suposición, pero no pude encontrar una cotización oficial de MSDN. Así, por ejemplo, hacer 1 exe con:
- exe utiliza 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 final fusionado solo una vez. ¿Alguien puede apoyar esta pregunta?