Quando usar bibliotecas dinâmicas vs. estáticas

Respostas:

299

Bibliotecas estáticas aumentam o tamanho do código no seu binário. Eles sempre são carregados e qualquer versão do código que você compilou é a versão do código que será executada.

Bibliotecas dinâmicas são armazenadas e versionadas separadamente. É possível que uma versão da biblioteca dinâmica seja carregada que não fosse a original que acompanha o seu código se a atualização for considerada binária compatível com a versão original.

Além disso, as bibliotecas dinâmicas não são necessariamente carregadas - geralmente são carregadas quando chamadas pela primeira vez - e podem ser compartilhadas entre componentes que usam a mesma biblioteca (vários carregamentos de dados, um carregamento de código).

As bibliotecas dinâmicas foram consideradas a melhor abordagem na maioria das vezes, mas originalmente elas tinham uma falha importante (inferno da DLL do Google), que foi praticamente eliminada pelos sistemas operacionais Windows mais recentes (Windows XP em particular).

Orion Adrian
fonte
71
No Windows / Mac (sem gerenciador de pacotes), não há realmente nenhuma boa razão para usar bibliotecas dinâmicas sobre estática. Como as DLLs do Windows não são realocáveis, o compartilhamento de código geralmente não funciona (e geralmente cada aplicativo é enviado e usa suas próprias versões da biblioteca). O único benefício real é que é mais fácil atualizar a biblioteca.
Zifre
5
no mac, eu uso muitas bibliotecas dinâmicas. por exemplo, o mac os x possui o sqlite3 embed. eu criei um programa que possui um recurso de banco de dados sqlite3 para armazenamento de desempenho. no entanto, a ligação dinâmica porque é raramente usado economiza em tempo de compilação, facilita o teste / mais rápido no entanto, se eu fosse para construir uma versão de lançamento, eu acho que eu sempre usar biblioteca estática apenas em caso de problemas de compatibilidade
ReachConnection
6
@ Zifre: relocatable = pode ser carregado em um endereço virtual diferente. DLL certamente suporta isso.
dma_k
20
@dma_k: as DLLs do Windows podem ser carregadas em endereços diferentes, mas apenas porque o vinculador copia todo o código e altera os números de endereço. Com objetos compartilhados, todas as referências de endereço são relativas, portanto, vários processos podem compartilhar a mesma memória para o objeto compartilhado. Em outras palavras, no Windows, uma DLL de 1 MB usada por 3 programas = 3 MB. No Linux, um SO SO de MB usado por 3 programas = 1 MB.
Zifre 17/10/10
7
Windows e Linux tem o conceito de deslocalização timte de carga de bibliotecas compartilhadas eli.thegreenplace.net/2011/08/25/... A maior coisa que permitiu Posição Código Independente não foi algo especial para Linux, em vez RIP-endereçamento relativo acrescentou com o conjunto de instruções x64; o Windows e o Linux podem fazer uso do endereçamento relativo ao RIP, reduzindo o número de correções ao realocar as bibliotecas.
Clemahieu
194

Outros explicaram adequadamente o que é uma biblioteca estática, mas eu gostaria de destacar algumas das advertências sobre o uso de bibliotecas estáticas, pelo menos no Windows:

  • Singletons: se algo precisar ser global / estático e exclusivo, tenha muito cuidado em colocá-lo em uma biblioteca estática. Se várias DLLs estiverem vinculadas a essa biblioteca estática, cada uma receberá sua própria cópia do singleton. No entanto, se o seu aplicativo for um único EXE sem DLLs personalizadas, isso pode não ser um problema.

  • Remoção de código não referenciada: Quando você vincula uma biblioteca estática, apenas as partes da biblioteca estática referenciadas pela sua DLL / EXE são vinculadas à sua DLL / EXE.

    Por exemplo, se mylib.libcontiver a.obje b.obje seu DLL / EXE fizer referência apenas a funções ou variáveis ​​de a.obj, a totalidade b.objserá descartada pelo vinculador. Se b.objcontiver objetos globais / estáticos, seus construtores e destruidores não serão executados. Se esses construtores / destruidores tiverem efeitos colaterais, você poderá se decepcionar com a ausência deles.

    Da mesma forma, se a biblioteca estática contiver pontos de entrada especiais, talvez seja necessário cuidar para que eles sejam realmente incluídos. Um exemplo disso na programação incorporada (ok, não no Windows) seria um manipulador de interrupções marcado como estando em um endereço específico. Você também precisa marcar o manipulador de interrupção como um ponto de entrada para garantir que ele não seja descartado.

    Outra conseqüência disso é que uma biblioteca estática pode conter arquivos de objetos completamente inutilizáveis ​​devido a referências não resolvidas, mas não causará um erro de vinculador até que você faça referência a uma função ou variável desses arquivos de objetos. Isso pode acontecer muito tempo após a biblioteca ser gravada.

  • Símbolos de depuração: você pode desejar um PDB separado para cada biblioteca estática ou os símbolos de depuração sejam colocados nos arquivos de objetos para que sejam inseridos no PDB para DLL / EXE. A documentação do Visual C ++ explica as opções necessárias .

  • RTTI: você pode acabar com vários type_infoobjetos da mesma classe se vincular uma única biblioteca estática em várias DLLs. Se o seu programa assume que type_infosão dados "singleton" e usa &typeid()or type_info::before(), você pode obter resultados indesejáveis ​​e surpreendentes.

bk1e
fonte
23
Quanto ao ponto sobre singletons, não esqueça que uma DLL pode ser carregada várias vezes (mesma versão ou várias versões) e ainda não há garantia de singleton.
Orion Adrian
Ponto adicional sobre remoção de código não referenciado: As chamadas feitas para DLLs também exigem uma chamada real para forçar o carregamento da DLL referenciada. Adicioná-lo como referência, mas não incluir nenhuma chamada que faça referência a ele, ainda terá o mesmo resultado de ter uma biblioteca estática que não chama nada. A única diferença é o que realmente é enviado. Nos dois casos, os construtores e destruidores estáticos não são acionados.
Orion Adrian
@ bk1e Isso não deveria acontecer. o .a sempre conterá todos os símbolos com os quais foi construído. Quando é estaticamente ligado em sua aplicação, sim apenas os símbolos que são usados será ligado no.
Miles Rout
62

Uma lib é uma unidade de código incluída no executável do aplicativo.

Uma dll é uma unidade autônoma de código executável. Ele é carregado no processo somente quando uma chamada é feita nesse código. Uma dll pode ser usada por vários aplicativos e carregada em vários processos, mantendo apenas uma cópia do código no disco rígido.

Dll profissionais : pode ser usado para reutilizar / compartilhar código entre vários produtos; carregar na memória do processo sob demanda e pode ser descarregado quando não for necessário; pode ser atualizado independentemente do restante do programa.

Contras da DLL : impacto no desempenho do carregamento da DLL e rebase do código; problemas de versão ("dll hell")

Prós da Lib : nenhum impacto no desempenho, pois o código é sempre carregado no processo e não é refeito; sem problemas de versão.

Contras da lib : executável / processo "inchar" - todo o código está no seu executável e é carregado no início do processo; sem reutilização / compartilhamento - cada produto possui sua própria cópia do código.

Franci Penov
fonte
O rebaseamento também pode ser feito no momento da criação usando rebase.exe ou passando a opção / BASE para link.exe. Se isso é eficaz depende se há algum conflito inesperado no espaço de endereço em tempo de execução.
bk1e 26/09/08
24

Além das implicações técnicas das bibliotecas estáticas e dinâmicas (os arquivos estáticos agrupam tudo em uma grande biblioteca binária versus dinâmica que permite o compartilhamento de código entre vários executáveis ​​diferentes), existem as implicações legais .

Por exemplo, se você estiver usando o código licenciado pela LGPL e vincular estaticamente a uma biblioteca LGPL (e assim criar um binário grande), seu código se tornará automaticamente o código LGPL de código aberto ( livre como em liberdade) . Se você vincular a objetos compartilhados, precisará LGPL apenas das melhorias / correções feitas na própria biblioteca LGPL.

Isso se torna uma questão muito mais importante se você estiver decidindo como compilar seus aplicativos móveis, por exemplo (no Android, você pode escolher estático versus dinâmico, no iOS, não - é sempre estático).

rburhum
fonte
23

Programas C ++ são criados em duas fases

  1. Compilação - produz código de objeto (.obj)
  2. Vinculação - produz código executável (.exe ou .dll)

A biblioteca estática (.lib) é apenas um pacote de arquivos .obj e, portanto, não é um programa completo. Não passou pela segunda fase (vinculação) da criação de um programa. DLLs, por outro lado, são como exe e, portanto, são programas completos.

Se você criar uma biblioteca estática, ela ainda não está vinculada e, portanto, os consumidores da sua biblioteca estática terão que usar o mesmo compilador que você usou (se você usou o g ++, eles terão que usar o g ++).

Se, em vez disso, você criou uma dll (e a criou corretamente ), criou um programa completo que todos os consumidores podem usar, independentemente do compilador que estiverem usando. Porém, existem várias restrições na exportação de uma dll, se a compatibilidade entre compiladores for desejada.

tcb
fonte
1
Isso é novidade para mim. Quais restrições existem entre compiladores ao usar DLLs? Ter a construção programador sem a necessidade do mesmo conjunto de ferramentas parece ser uma enorme vantagem para DLLs
Dan
1
Esta resposta é informativa. Adicionando uma pequena ressalva: consumers of your static library will have to use the same compiler that you usedse a biblioteca estática usa a biblioteca C ++, como #include <iostream>.
truthadjustr 29/03
não se pode consumir uma dll c ++, a menos que o mesmo compilador seja usado (porque não há c ++ abi padrão, os símbolos são desconfigurados de maneiras diferentes). A dll e o módulo cliente devem usar o mesmo compilador e as mesmas configurações de compilação
kcris
19

Criando uma 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]>

criando uma 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
fonte
13

Uma biblioteca estática é compilada no cliente. Um .lib é usado em tempo de compilação e o conteúdo da biblioteca se torna parte do executável consumidor.

Uma biblioteca dinâmica é carregada em tempo de execução e não compilada no executável do cliente. As bibliotecas dinâmicas são mais flexíveis, pois vários executáveis ​​de clientes podem carregar uma DLL e utilizar sua funcionalidade. Isso também mantém o tamanho geral e a capacidade de manutenção do código do cliente em um nível mínimo.

Jordan Parmer
fonte
13

Você deve pensar cuidadosamente sobre alterações ao longo do tempo, controle de versão, estabilidade, compatibilidade etc.

Se houver dois aplicativos que usam o código compartilhado, você deseja forçá-los a mudar juntos, caso precisem ser compatíveis entre si? Em seguida, use a dll. Todos os exe estarão usando o mesmo código.

Ou você deseja isolá-los um do outro, para poder mudar um e ter certeza de que não quebrou o outro. Em seguida, use a lib estática.

O inferno da DLL é quando você provavelmente DEVERIA ter usado uma lib estática, mas em vez disso uma dll, e nem todos os exes são em coma com ela.

Corey Trager
fonte
9

Uma biblioteca estática deve ser vinculada ao executável final; torna-se parte do executável e segue-o onde quer que vá. Uma biblioteca dinâmica é carregada toda vez que o executável é executado e permanece separado do executável como um arquivo DLL.

Você usaria uma DLL quando quiser alterar a funcionalidade fornecida pela biblioteca sem precisar vincular novamente o executável (basta substituir o arquivo DLL, sem precisar substituir o arquivo executável).

Você usaria uma biblioteca estática sempre que não tiver um motivo para usar uma biblioteca dinâmica.

spotcatbug
fonte
Você também pode usar uma DLL quando vários outros aplicativos usarem a mesma funcionalidade - isso pode reduzir a área ocupada.
Tim
Além disso, estender seu conceito inicial, a arquitetura "plug-in", na qual você deseja permitir funcionalidades adicionadas / desconhecidas posteriormente, sem precisar reconstruir ou relançar, só pode ser feita com bibliotecas dinâmicas.
Tim
8

O artigo de Ulrich Drepper sobre " Como escrever bibliotecas compartilhadas " também é um bom recurso que detalha a melhor forma de aproveitar as bibliotecas compartilhadas, ou o que ele chama de "Objetos Compartilhados Dinâmicos" (DSOs). Ele se concentra mais em bibliotecas compartilhadas no formato binário ELF , mas algumas discussões também são adequadas para DLLs do Windows.

Vazio
fonte
5

Para uma excelente discussão sobre este tópico, leia este artigo da Sun.

Ele inclui todos os benefícios, incluindo a possibilidade de inserir bibliotecas interpostas. Mais detalhes sobre a interposição podem ser encontrados neste artigo aqui .

Rob Wells
fonte
4

Realmente a troca que você está fazendo (em um projeto grande) está no tempo de carregamento inicial, as bibliotecas serão vinculadas uma vez ou outra, a decisão que precisa ser tomada é que o link levará tempo suficiente para que o compilador precise morder a bala e fazê-lo com antecedência, ou o vinculador dinâmico pode fazê-lo no tempo de carregamento.

pfranza
fonte
3

Se sua biblioteca for compartilhada entre vários executáveis, geralmente faz sentido dinamizar a redução do tamanho dos executáveis. Caso contrário, definitivamente torne-o estático.

Existem várias desvantagens de usar uma dll. Existe uma sobrecarga adicional para carregá-lo e descarregá-lo. Há também uma dependência adicional. Se você alterar a dll para torná-la incompatível com a sua execução, eles deixarão de funcionar. Por outro lado, se você alterar uma biblioteca estática, seus executáveis ​​compilados usando a versão antiga não serão afetados.

Dima
fonte
3

Se a biblioteca for estática, no momento do link, o código será vinculado ao seu executável. Isso torna seu executável maior (do que se você seguisse a rota dinâmica).

Se a biblioteca for dinâmica, no momento do link, as referências aos métodos necessários serão incorporadas ao seu executável. Isso significa que você precisa enviar seu executável e a biblioteca dinâmica. Você também deve considerar se o acesso compartilhado ao código na biblioteca é seguro, endereço de carregamento preferido entre outras coisas.

Se você pode viver com a biblioteca estática, vá com a biblioteca estática.

Seb Rose
fonte
3

Usamos muitas DLLs (> 100) em nosso projeto. Essas DLLs têm dependências entre si e, portanto, escolhemos a configuração do vínculo dinâmico. No entanto, possui as seguintes desvantagens:

  • inicialização lenta (> 10 segundos)
  • As DLLs tiveram que ser versionadas, pois o Windows carrega módulos com exclusividade de nomes. Caso contrário, os próprios componentes gravados obteriam a versão errada da DLL (ou seja, a que já foi carregada em vez de seu próprio conjunto distribuído)
  • O otimizador só pode otimizar dentro dos limites da DLL. Por exemplo, o otimizador tenta colocar dados e códigos usados ​​com freqüência próximos um do outro, mas isso não funciona nos limites da DLL

Talvez uma configuração melhor fosse tornar tudo uma biblioteca estática (e, portanto, você só tem um executável). Isso funciona apenas se nenhuma duplicação de código ocorrer. Um teste parece apoiar essa suposição, mas não consegui encontrar uma cotação oficial do MSDN. Então, por exemplo, faça 1 exe com:

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

O código e as variáveis ​​de shared_lib2 devem estar presentes no executável mesclado final apenas uma vez. Alguém pode apoiar esta pergunta?

gast128
fonte
Você não deveria usar algumas diretivas de pré-compilador de alguma forma para evitar a duplicação de código?
Paceman
A pré-compilação afaiac funciona apenas em uma base por módulo (exe / dll / lib). A pré-compilação destina-se principalmente a acelerar a compilação, mas também evita várias inclusões em uma unidade de compilação. No entanto, incluir guardas são a melhor maneira de alcançar esse efeito.
gast128
2

Bibliotecas estáticas são arquivos que contêm o código de objeto da biblioteca, quando vinculados a um aplicativo em que o código é compilado no executável. As bibliotecas compartilhadas são diferentes, pois não são compiladas no executável. Em vez disso, o vinculador dinâmico procura em alguns diretórios procurando a (s) biblioteca (s) necessária (s) e carrega-o na memória. Mais do que um executável pode usar a mesma biblioteca compartilhada ao mesmo tempo, reduzindo assim o uso de memória e o tamanho do executável. No entanto, existem mais arquivos para distribuir com o executável. Você precisa garantir que a biblioteca esteja instalada no sistema de usos em algum lugar onde o vinculador possa encontrá-la. A vinculação estática elimina esse problema, mas resulta em um arquivo executável maior.

Terence Simpson
fonte
2

Se você trabalha em projetos incorporados ou em bibliotecas estáticas de plataformas especializadas, é o único caminho a percorrer, também muitas vezes são menos complicadas de serem compiladas em seu aplicativo. Também ter projetos e arquivos que incluam tudo torna a vida mais feliz.

Robert Gould
fonte
2

Eu daria uma regra geral de que, se você tiver uma grande base de código, toda construída em bibliotecas de nível inferior (por exemplo, uma estrutura Utils ou Gui), que você deseja particionar em bibliotecas mais gerenciáveis, então as tornará bibliotecas estáticas. As bibliotecas dinâmicas não compram nada para você e há menos surpresas - haverá apenas uma instância de singletons, por exemplo.

Se você possui uma biblioteca totalmente separada do restante da base de código (por exemplo, uma biblioteca de terceiros), considere torná-la uma dll. Se a biblioteca for LGPL, você poderá precisar usar uma DLL de qualquer maneira, devido às condições de licenciamento.

the_mandrill
fonte