Kiedy używać bibliotek dynamicznych a statycznych


437

Podczas tworzenia biblioteki klas w C ++ możesz wybierać między bibliotekami dynamicznymi ( .dll, .so) i statycznymi ( .lib, .a). Jaka jest różnica między nimi i kiedy należy użyć której?


2
Należy zauważyć, że istnieje również coś, co nazywa się „biblioteką importu” sprawdź stackoverflow.com/questions/3573475/…
Wakan Tanka,

Odpowiedzi:


299

Biblioteki statyczne zwiększają rozmiar kodu w pliku binarnym. Są zawsze ładowane, a dowolna wersja skompilowanego kodu jest wersją kodu, który zostanie uruchomiony.

Biblioteki dynamiczne są przechowywane i wersjonowane osobno. Możliwe jest załadowanie wersji biblioteki dynamicznej, która nie była oryginalną wersją dostarczoną z Twoim kodem, jeśli aktualizacja zostanie uznana za binarnie zgodną z wersją oryginalną.

Ponadto biblioteki dynamiczne niekoniecznie są ładowane - zwykle są ładowane przy pierwszym wywołaniu - i mogą być współużytkowane przez komponenty korzystające z tej samej biblioteki (wiele ładowań danych, jedno ładowanie kodu).

Biblioteki dynamiczne były przez większość czasu uważane za lepsze podejście, ale pierwotnie miały poważną wadę (piekło DLL Google), która została prawie całkowicie wyeliminowana przez nowsze systemy operacyjne Windows (w szczególności Windows XP).


71
W systemie Windows / Mac (bez menedżera pakietów) naprawdę nie ma dobrego powodu, aby używać bibliotek dynamicznych zamiast statycznych. Ponieważ bibliotek DLL systemu Windows nie można przenosić, udostępnianie kodu często nie działa (i zazwyczaj każda aplikacja jest dostarczana i używa własnych wersji biblioteki). Jedyną prawdziwą korzyścią jest to, że łatwiej jest zaktualizować bibliotekę.
Zifre

5
na Macu używam wielu bibliotek dynamicznych. na przykład Mac OS X ma wbudowany sqlite3. stworzyłem program, który ma funkcję bazy danych sqlite3 do przechowywania wydajności. jednak ponieważ rzadko jest używane dynamiczne łączenie, oszczędza czas kompilacji, ułatwia testowanie / przyspieszenie, jednak gdybym miał zbudować wersję, myślę, że zawsze używałbym biblioteki statycznej na wypadek problemów ze zgodnością
ReachConnection

6
@Zifre: relocatable = można załadować pod innym adresem wirtualnym. DLL z pewnością obsługuje to.
dma_k,

20
@dma_k: Biblioteki DLL systemu Windows można ładować pod różnymi adresami, ale tylko dlatego, że linker kopiuje cały kod i zmienia numery adresów. W przypadku obiektów współdzielonych wszystkie odwołania do adresów są względne, więc wiele procesów może współużytkować tę samą pamięć dla obiektu współdzielonego. Innymi słowy, w systemie Windows 1 MB DLL używana przez 3 programy = 3 MB. W systemie Linux A SO SO używane przez 3 programy = 1 MB.
Zifre

7
Zarówno Windows, jak i Linux mają koncepcję relokacji load-timte bibliotek współużytkowanych eli.thegreenplace.net/2011/08/25/… Największą rzeczą, która pozwoliła na kod niezależny od pozycji, nie było coś specjalnego w Linuksie, a raczej adresowanie względne do protokołu RIP z zestawem instrukcji x64; zarówno Windows, jak i Linux mogą korzystać z względnego adresowania RIP, co zmniejsza liczbę poprawek podczas przenoszenia bibliotek.
clemahieu,

194

Inni odpowiednio wyjaśnili, czym jest biblioteka statyczna, ale chciałbym zwrócić uwagę na niektóre zastrzeżenia dotyczące korzystania z bibliotek statycznych, przynajmniej w systemie Windows:

  • Singletony: Jeśli coś musi być globalne / statyczne i niepowtarzalne, należy bardzo uważać na umieszczenie go w bibliotece statycznej. Jeśli wiele bibliotek DLL jest połączonych z tą statyczną biblioteką, każda z nich otrzyma własną kopię singletonu. Jeśli jednak twoja aplikacja to pojedynczy plik EXE bez niestandardowych bibliotek DLL, może to nie stanowić problemu.

  • Usuwanie kodu bez odniesień: Podczas łączenia z biblioteką statyczną tylko części biblioteki statycznej, do których odwołuje się biblioteka DLL / EXE, zostaną połączone z biblioteką DLL / EXE.

    Na przykład, jeśli mylib.libzawiera, a.obja b.objtwoja biblioteka DLL / EXE odwołuje się tylko do funkcji lub zmiennych a.obj, całość b.objzostanie odrzucona przez linker. Jeśli b.objzawiera obiekty globalne / statyczne, ich konstruktory i destruktory nie zostaną wykonane. Jeśli ci konstruktorzy / destruktory mają skutki uboczne, możesz być rozczarowany ich nieobecnością.

    Podobnie, jeśli biblioteka statyczna zawiera specjalne punkty wejścia, może być konieczne zadbanie o to, aby były one uwzględnione. Przykładem tego w programowaniu wbudowanym (w porządku, nie w systemie Windows) może być moduł obsługi przerwań oznaczony jako znajdujący się pod określonym adresem. Musisz także zaznaczyć moduł obsługi przerwań jako punkt wejścia, aby upewnić się, że nie zostanie odrzucony.

    Inną konsekwencją tego jest to, że biblioteka statyczna może zawierać pliki obiektowe, które są całkowicie bezużyteczne z powodu nierozwiązanych odwołań, ale nie spowoduje to błędu linkera, dopóki nie odwołasz się do funkcji lub zmiennej z tych plików obiektowych. Może się to zdarzyć długo po napisaniu biblioteki.

  • Symbole debugowania: Możesz chcieć osobnego PDB dla każdej biblioteki statycznej lub możesz chcieć, aby symbole debugowania były umieszczane w plikach obiektowych, aby były one wtaczane do PDB dla DLL / EXE. Dokumentacja Visual C ++ wyjaśnia niezbędne opcje .

  • RTTI: Możesz skończyć z wieloma type_infoobiektami dla tej samej klasy, jeśli połączysz jedną bibliotekę statyczną z wieloma bibliotekami DLL. Jeśli twój program zakłada, że type_infosą to dane „singleton” i wykorzystuje &typeid()lub type_info::before(), możesz uzyskać niepożądane i zaskakujące wyniki.


23
Jeśli chodzi o kwestię singletonów, nie zapominaj, że DLL może być ładowany wiele razy (ta sama wersja lub wiele wersji) i nadal nie ma gwarancji singleton.
Orion Adrian

Dodatkowa uwaga na temat usuwania kodu bez odniesień: Wywołania do bibliotek DLL również wymagają rzeczywistego wywołania w celu wymuszenia załadowania biblioteki DLL, do której istnieje odwołanie. Dodanie go jako referencji, ale bez uwzględnienia żadnego wywołania, które się do niego odwołuje, nadal da ci taki sam rezultat, jak posiadanie statycznej biblioteki, która niczego nie wywołuje. Jedyną różnicą jest to, co faktycznie wysyła. W obu przypadkach konstruktory statyczne i destruktory nie strzelają.
Orion Adrian

@ bk1e To nie powinno się zdarzyć. .a zawsze będzie zawierać wszystkie symbole, z którymi został zbudowany. Gdy jest on statycznie powiązany z twoją aplikacją, tak, tylko te, które są używane, zostaną połączone.
Miles Rout

62

Lib to jednostka kodu zawarta w pliku wykonywalnym aplikacji.

Dll jest samodzielną jednostką kodu wykonywalnego. Jest on ładowany w procesie tylko wtedy, gdy do tego kodu zostanie wywołane wywołanie. Biblioteka DLL może być używana przez wiele aplikacji i ładowana w wielu procesach, a jednocześnie ma tylko jedną kopię kodu na dysku twardym.

Dll pros : może być użyty do ponownego użycia / współdzielenia kodu między kilkoma produktami; ładowanie do pamięci procesu na żądanie i może być rozładowane, gdy nie jest potrzebne; można zaktualizować niezależnie od reszty programu.

Wady dll : wpływ na ładowanie dll i zmiany kodu; problemy z wersjonowaniem („dll hell”)

Lib plus : brak wpływu na wydajność, ponieważ kod jest zawsze ładowany w procesie i nie jest ponownie bazowany; brak problemów z wersjonowaniem.

Lib Cons : plik wykonywalny / proces „wzdęcia” - cały kod znajduje się w pliku wykonywalnym i jest ładowany przy uruchomieniu procesu; bez ponownego użycia / udostępniania - każdy produkt ma własną kopię kodu.


Rebasing można również wykonać w czasie kompilacji za pomocą rebase.exe lub poprzez przekazanie opcji / BASE do link.exe. To, czy jest to skuteczne, zależy od tego, czy wystąpią nieoczekiwane konflikty przestrzeni adresowej w czasie wykonywania.
bk1e

24

Oprócz technicznych implikacji bibliotek statycznych vs. dynamicznych (pliki statyczne zawierają wszystko w jednej dużej bibliotece binarnej vs. dynamicznej, które umożliwiają współdzielenie kodu między kilkoma różnymi plikami wykonywalnymi), istnieją implikacje prawne .

Na przykład, jeśli korzystasz z kodu licencjonowanego LGPL i łączysz się statycznie z biblioteką LGPL (i w ten sposób tworzysz jeden duży plik binarny), Twój kod automatycznie staje się Open Sourced ( darmowy jak na wolności) kodem LGPL typu . Jeśli łączysz się z obiektami współużytkowanymi, musisz tylko LGPL ulepszeń / poprawek błędów, które wprowadziłeś do samej biblioteki LGPL.

Staje się to o wiele ważniejszym problemem, jeśli decydujesz na przykład, jak skompilować swoje aplikacje mobilne (w Androidzie masz wybór statyczny vs dynamiczny, w iOS nie - zawsze jest statyczny).


23

Programy C ++ są zbudowane w dwóch fazach

  1. Kompilacja - tworzy kod obiektowy (.obj)
  2. Łączenie - tworzy kod wykonywalny (.exe lub .dll)

Biblioteka statyczna (.lib) jest tylko pakietem plików .obj i dlatego nie jest kompletnym programem. Nie przeszedł on drugiej (łączącej) fazy budowania programu. Z drugiej strony biblioteki DLL są podobne do plików exe i dlatego są kompletnymi programami.

Jeśli zbudujesz bibliotekę statyczną, nie jest ona jeszcze połączona i dlatego konsumenci twojej biblioteki statycznej będą musieli użyć tego samego kompilatora, którego użyłeś (jeśli użyłeś g ++, będą musieli użyć g ++).

Jeśli zamiast tego zbudowałeś bibliotekę DLL (i poprawnie ją zbudowałeś ), zbudowałeś kompletny program, z którego mogą korzystać wszyscy konsumenci, bez względu na to, jakiego kompilatora używają. Istnieje jednak kilka ograniczeń dotyczących eksportu z biblioteki dll, jeśli wymagana jest kompatybilność z wieloma kompilatorami.


1
To dla mnie nowość. Jakie ograniczenia istnieją w przypadku kompilatorów krzyżowych podczas korzystania z bibliotek DLL? Posiadanie przez programistę kompilacji bez potrzeby korzystania z tego samego zestawu narzędzi wydaje się ogromnym plusem dla bibliotek DLL
Dan.

1
Ta odpowiedź ma charakter informacyjny. Dodanie drobnego zastrzeżenia: consumers of your static library will have to use the same compiler that you usedjeśli biblioteka statyczna korzysta z biblioteki C ++, takiej jak #include <iostream>.
truthadjustr

nie można korzystać z biblioteki dll c ++, chyba że użyje się tego samego kompilatora (ponieważ nie ma standardowego abi c ++, symbole są zniekształcane na różne sposoby). Zarówno dll, jak i moduł klienta muszą używać tego samego kompilatora i tych samych ustawień kompilacji
kcris

19

Tworzenie biblioteki statycznej

$$:~/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]>

tworzenie biblioteki dynamicznej

$$:~/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]>

13

Biblioteka statyczna jest kompilowana do klienta. Plik .lib jest używany podczas kompilacji, a zawartość biblioteki staje się częścią konsumującego pliku wykonywalnego.

Biblioteka dynamiczna jest ładowana w czasie wykonywania i nie jest kompilowana w pliku wykonywalnym klienta. Biblioteki dynamiczne są bardziej elastyczne, ponieważ wiele plików wykonywalnych klienta może załadować bibliotekę DLL i wykorzystać jej funkcjonalność. Dzięki temu ogólny rozmiar i łatwość konserwacji kodu klienta są minimalne.


13

Powinieneś dokładnie przemyśleć zmiany w czasie, wersjonowanie, stabilność, kompatybilność itp.

Jeśli istnieją dwie aplikacje korzystające ze wspólnego kodu, czy chcesz zmusić te aplikacje do zmiany razem, na wypadek, gdyby musiały być ze sobą kompatybilne? Następnie użyj biblioteki dll. Wszystkie pliki exe będą używać tego samego kodu.

A może chcesz je oddzielić od siebie, abyś mógł je zmienić i mieć pewność, że nie złamiesz drugiego. Następnie użyj statycznej biblioteki lib.

Piekło DLL ma miejsce wtedy, gdy prawdopodobnie POWINIENEŚ użyć statycznej biblioteki lib, ale zamiast tego użyłeś biblioteki dll i nie wszystkie exy są z nią kompatybilne.


9

Biblioteka statyczna musi być połączona z końcowym plikiem wykonywalnym; staje się częścią pliku wykonywalnego i podąża za nim, gdziekolwiek się znajduje. Biblioteka dynamiczna jest ładowana za każdym razem, gdy plik wykonywalny jest wykonywany i pozostaje oddzielna od pliku wykonywalnego jako plik DLL.

Korzystasz z biblioteki DLL, jeśli chcesz mieć możliwość zmiany funkcjonalności dostarczanej przez bibliotekę bez konieczności ponownego łączenia pliku wykonywalnego (wystarczy zastąpić plik DLL, bez konieczności zastępowania pliku wykonywalnego).

Korzystasz z biblioteki statycznej, gdy nie masz powodu, aby używać biblioteki dynamicznej.


Możesz także użyć biblioteki DLL, gdy wiele innych aplikacji korzysta z tej samej funkcji - może to zmniejszyć powierzchnię.
Tim

Ponadto, rozszerzenie pierwotnej koncepcji, architektury „wtyczki”, w której chcesz zezwolić na dodanie / nieznaną funkcjonalność później, bez konieczności przebudowy lub ponownego wydania, można wykonać tylko za pomocą bibliotek dynamicznych.
Tim

8

Artykuł Ulricha Dreppera na temat „ Jak pisać biblioteki współdzielone ” jest również dobrym zasobem, który szczegółowo opisuje, jak najlepiej korzystać z bibliotek współdzielonych lub co określa jako „dynamiczne obiekty współdzielone” (DSO). Koncentruje się bardziej na bibliotekach współdzielonych w formacie binarnym ELF , ale niektóre dyskusje są odpowiednie również dla bibliotek DLL systemu Windows.


5

Aby uzyskać doskonałą dyskusję na ten temat, przeczytaj ten artykuł od Sun.

Ma wszystkie zalety, w tym możliwość wstawiania bibliotek wstawiających. Więcej szczegółów na temat interpozycji można znaleźć w tym artykule tutaj .


4

Naprawdę kompromis, który podejmujesz (w dużym projekcie), polega na początkowym czasie ładowania, biblioteki będą się łączyły w tym czy innym czasie, decyzja, którą należy podjąć, to czy połączenie zajmie wystarczająco dużo czasu, którego potrzebuje kompilator ugryźć pocisk i zrobić to z góry, lub dynamiczny linker może to zrobić w czasie ładowania.


3

Jeśli twoja biblioteka ma być współużytkowana przez kilka plików wykonywalnych, często warto nadać jej dynamiczny charakter, aby zmniejszyć rozmiar plików wykonywalnych. W przeciwnym razie zdecydowanie uczyń go statycznym.

Istnieje kilka wad korzystania z biblioteki dll. Dodatkowe obciążenie związane jest z załadunkiem i rozładunkiem. Istnieje również dodatkowa zależność. Jeśli zmienisz bibliotekę DLL, aby była niezgodna z plikami wykonywalnymi, przestaną działać. Z drugiej strony, jeśli zmienisz bibliotekę statyczną, nie wpłynie to na skompilowane pliki wykonywalne przy użyciu starej wersji.


3

Jeśli biblioteka jest statyczna, to w czasie łączenia kod jest łączony z plikiem wykonywalnym. Dzięki temu plik wykonywalny jest większy (niż gdybyś wybrał ścieżkę dynamiczną).

Jeśli biblioteka jest dynamiczna, wówczas w czasie łączenia w pliku wykonywalnym są wbudowane odwołania do wymaganych metod. Oznacza to, że musisz wysłać plik wykonywalny i bibliotekę dynamiczną. Należy również rozważyć, czy współużytkowany dostęp do kodu w bibliotece jest bezpieczny, preferowany adres ładowania między innymi.

Jeśli możesz żyć z biblioteką statyczną, przejdź do biblioteki statycznej.


3

W naszym projekcie używamy wielu bibliotek DLL (> 100). Te biblioteki DLL są od siebie zależne, dlatego wybraliśmy konfigurację dynamicznego łączenia. Ma jednak następujące wady:

  • wolne uruchamianie (> 10 sekund)
  • Biblioteki DLL musiały być wersjonowane, ponieważ system Windows ładuje moduły na unikalność nazw. W przeciwnym razie własne pisemne komponenty otrzymałyby niewłaściwą wersję biblioteki DLL (tj. Już załadowaną zamiast własnego zestawu rozproszonego)
  • Optymalizator może optymalizować tylko w granicach bibliotek DLL. Na przykład optymalizator próbuje umieścić często używane dane i kod obok siebie, ale to nie zadziała poza granicami bibliotek DLL

Być może lepszą konfiguracją było uczynienie wszystkiego statyczną biblioteką (a zatem masz tylko jeden plik wykonywalny). Działa to tylko wtedy, gdy nie ma miejsca powielanie kodu. Test wydaje się potwierdzać to założenie, ale nie mogłem znaleźć oficjalnej wyceny MSDN. Na przykład zrób 1 exe z:

  • exe używa shared_lib1, shared_lib2
  • shared_lib1 użyj shared_lib2
  • shared_lib2

Kod i zmienne shared_lib2 powinny być obecne w końcowym scalonym pliku wykonywalnym tylko raz. Czy ktoś może poprzeć to pytanie?


Czy nie chcesz w jakiś sposób używać dyrektyw prekompilatora, aby uniknąć duplikacji kodu?
Paceman

Kompilacja wstępna Afaiac działa tylko na bazie na moduł (exe / dll / lib). Kompilacja wstępna ma przede wszystkim na celu przyspieszenie kompilacji, chociaż zapobiega także wielu inkluzjom w jednostce kompilacyjnej. Jednak osłony obejmują lepszy sposób na osiągnięcie tego efektu.
gast128

2

Biblioteki statyczne to archiwa zawierające kod obiektowy biblioteki, gdy są połączone z aplikacją, kod ten jest wkompilowany w plik wykonywalny. Biblioteki współdzielone różnią się tym, że nie są wkompilowane w plik wykonywalny. Zamiast tego dynamiczny linker przeszukuje niektóre katalogi w poszukiwaniu potrzebnych bibliotek, a następnie ładuje je do pamięci. Więcej niż jeden plik wykonywalny może jednocześnie korzystać z tej samej biblioteki współużytkowanej, co zmniejsza zużycie pamięci i rozmiar pliku wykonywalnego. Istnieje jednak więcej plików do rozpowszechnienia w pliku wykonywalnym. Musisz upewnić się, że biblioteka jest zainstalowana w systemie użytkownika w miejscu, w którym linker może ją znaleźć, statyczne łączenie eliminuje ten problem, ale skutkuje większym plikiem wykonywalnym.


2

Jeśli pracujesz nad projektami osadzonymi lub na wyspecjalizowanych platformach, biblioteki statyczne są jedyną drogą, którą możesz wykonać, również wiele razy mniej kłopotów z kompilacją w twojej aplikacji. Posiadanie projektów i plików mak, które zawierają wszystko, sprawia, że ​​życie jest szczęśliwsze.


2

Podałbym ogólną zasadę, że jeśli masz dużą bazę kodu, wszystkie zbudowane na bibliotekach niższego poziomu (np. Frameworku Utils lub Gui), które chcesz podzielić na łatwiejsze do zarządzania biblioteki, a następnie uczyń je bibliotekami statycznymi. Biblioteki dynamiczne tak naprawdę nic nie kupują i jest mniej niespodzianek - na przykład będzie tylko jedna instancja singletonów.

Jeśli masz bibliotekę, która jest całkowicie oddzielna od reszty bazy kodu (np. Biblioteka strony trzeciej), rozważ utworzenie dll. Jeśli biblioteką jest LGPL, może być konieczne użycie biblioteki DLL ze względu na warunki licencyjne.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.