동적 라이브러리와 정적 라이브러리를 사용하는 경우


437

C ++에서 클래스 라이브러리를 작성할 때 동적 ( .dll, .so) 및 정적 ( .lib, .a) 라이브러리 중에서 선택할 수 있습니다 . 그들 사이의 차이점은 무엇이며 어느 것을 사용하는 것이 적절한가요?


2
"라이브러리 가져 오기"체크라고하는 것도 있습니다. stackoverflow.com/questions/3573475/…
Wakan Tanka

답변:


299

정적 라이브러리는 바이너리의 코드 크기를 증가시킵니다. 그것들은 항상로드되며 컴파일 한 코드의 버전은 실행될 코드의 버전입니다.

동적 라이브러리는 별도로 저장되고 버전이 지정됩니다. 업데이트가 원래 버전과 이진 호환되는 것으로 간주되는 경우 코드 와 함께 제공된 원래 버전 이 아닌 동적 라이브러리 버전을로드 할 수 있습니다 .

또한 동적 라이브러리는 반드시로드 할 필요는 없으며 일반적으로 처음 호출 될 때로드되며 동일한 라이브러리 (여러 데이터로드, 하나의 코드로드)를 사용하는 구성 요소간에 공유 할 수 있습니다.

동적 라이브러리는 대부분 더 나은 접근 방식으로 여겨졌지만 원래는 최신 Windows OS (특히 Windows XP)에서 제거 된 주요 결함 (Google DLL hell)이있었습니다.


71
Windows / Mac (패키지 관리자 없음)에서는 정적 라이브러리보다 동적 라이브러리를 사용해야 할 이유가 없습니다. Windows DLL은 재배치 할 수 없으므로 코드 공유가 작동하지 않는 경우가 많습니다 (일반적으로 각 앱은 자체 버전의 라이브러리를 제공하여 사용합니다). 유일한 실제 이점은 라이브러리를 업데이트하기가 쉽다는 것입니다.
Zifre

5
Mac에서는 많은 동적 라이브러리를 사용합니다. 예를 들어, mac OS x에는 sqlite3 embed가 있습니다. 성능 저장을위한 sqlite3 데이터베이스 기능이있는 프로그램을 만들었습니다. 그러나 동적 링크가 드물게 사용되므로 컴파일 시간을 절약하고 테스트를 더 쉽고 빠르게 할 수 있습니다. 그러나 릴리스 버전을 빌드하는 경우 호환성 문제가 발생할 경우 항상 정적 라이브러리를 사용한다고 생각합니다.
ReachConnection

6
@Zifre : relocatable = 다른 가상 주소로로드 할 수 있습니다. DLL은 확실히 이것을 지원합니다.
dma_k

20
@dma_k : Windows DLL은 다른 주소에로드 할 수 있지만 링커는 모든 코드를 복사하고 주소 번호를 변경해야합니다. 공유 객체를 사용하면 모든 주소 참조가 상대적이므로 여러 프로세스가 공유 객체에 대해 동일한 메모리를 공유 할 수 있습니다. 즉, Windows에서는 3 개의 프로그램에서 사용하는 1MB DLL = 3MB입니다. Linux에서 3 개의 프로그램이 사용하는 MB SO = 1MB
Zifre

7
Windows와 Linux 모두 공유 라이브러리 eli.thegreenplace.net/2011/08/25/ 의로드 타이밍 재배치라는 개념을 가지고 있습니다. Position Independent Code를 허용 한 가장 큰 것은 Linux에 특별한 것이 아니라 RIP 상대 주소 지정입니다. x64 명령어 세트; Windows와 Linux 모두 RIP 상대 주소 지정을 사용하여 라이브러리를 재배치 할 때 수정 횟수를 줄입니다.
clemahieu

194

다른 사람들은 정적 라이브러리가 무엇인지 적절하게 설명했지만 적어도 Windows에서 정적 라이브러리를 사용할 때의 몇 가지주의 사항을 지적하고 싶습니다.

  • 싱글 톤 (Singleton) : 어떤 것이 전역 / 정적 / 고유적이고 독창적이어야하는 경우, 정적 라이브러리에 넣는 것에 매우주의하십시오. 여러 DLL이 해당 정적 라이브러리에 연결되어 있으면 각각 고유 한 단일 복사본을 얻게됩니다. 그러나 응용 프로그램이 사용자 지정 DLL이없는 단일 EXE 인 경우에는 문제가되지 않을 수 있습니다.

  • 참조되지 않은 코드 제거 : 정적 라이브러리에 연결하면 DLL / EXE에서 참조하는 정적 라이브러리의 일부만 DLL / EXE에 연결됩니다.

    예를 들어, mylib.libcontains a.objb.objDLL / EXE가의 함수 또는 변수 만 참조 하는 경우 링커 a.obj는 전체를 b.obj버립니다. b.obj전역 / 정적 객체가 포함되어 있으면 생성자와 소멸자가 실행되지 않습니다. 이러한 생성자 / 소멸자가 부작용이있는 경우 부재로 인해 실망 할 수 있습니다.

    마찬가지로 정적 라이브러리에 특수 진입 점이 포함되어 있으면 실제로 포함되도록주의해야합니다. 임베디드 프로그래밍 (예 : Windows가 아님)에서이 예제는 특정 주소에있는 것으로 표시된 인터럽트 핸들러입니다. 또한 중단되지 않도록 인터럽트 처리기를 진입 점으로 표시해야합니다.

    이것의 또 다른 결과는 정적 라이브러리가 해석되지 않은 참조로 인해 완전히 사용할 수없는 오브젝트 파일을 포함 할 수 있지만 해당 오브젝트 파일에서 함수 또는 변수를 참조 할 때까지 링커 오류를 발생시키지 않습니다. 이것은 라이브러리가 작성된 후 오래 지속될 수 있습니다.

  • 디버그 기호 : 각 정적 라이브러리마다 별도의 PDB를 원하거나 DLL / EXE의 PDB로 롤백되도록 디버그 기호를 개체 파일에 배치 할 수 있습니다. Visual C ++ 문서는 필요한 옵션을 설명 합니다 .

  • RTTI :type_info 단일 정적 라이브러리를 여러 DLL에 연결하면 동일한 클래스에 대한 여러 개체 가 생길 수 있습니다 . 프로그램 type_info이 "단일"데이터 인 것으로 가정 &typeid()하거나 또는를 사용 type_info::before()하면 바람직하지 않은 놀라운 결과를 얻을 수 있습니다.


23
싱글 톤에 대한 요점은 DLL이 여러 번로드 (동일한 버전 또는 여러 버전) 될 수 있으며 여전히 싱글 톤 보장이 없다는 것을 잊지 마십시오.
오리온 아드리안

참조되지 않은 코드 제거에 대한 추가 사항 : DLL을 호출하려면 참조 된 DLL을 강제로로드하기 위해 실제 호출도 필요합니다. 참조로 추가하고 참조하는 호출을 포함하지 않으면 아무것도 호출하지 않는 정적 라이브러리가있는 것과 동일한 결과를 얻을 수 있습니다. 유일한 차이점은 실제로 배송되는 것입니다. 두 경우 모두 정적 생성자와 소멸자는 실행되지 않습니다.
Orion Adrian

@ bk1e 그런 일은 일어나지 않아야합니다. .a는 항상 자신이 만든 모든 심볼을 포함합니다. 응용 프로그램에 정적으로 연결되면 사용 된 기호 만 연결됩니다.
Miles Rout

62

lib는 응용 프로그램 실행 파일 내에 번들로 제공되는 코드 단위입니다.

dll은 실행 가능한 코드의 독립형 단위입니다. 해당 코드를 호출 할 때만 프로세스에로드됩니다. dll은 여러 응용 프로그램에서 사용할 수 있으며 여러 프로세스에로드되는 동시에 하드 드라이브에는 코드 사본이 하나만 남아 있습니다.

Dll 전문가 : 여러 제품간에 코드를 재사용 / 공유하는 데 사용할 수 있습니다. 요청시 프로세스 메모리에로드하고 필요하지 않은 경우 언로드 할 수 있습니다. 프로그램의 나머지 부분과 독립적으로 업그레이드 할 수 있습니다.

DLL 단점 : DLL 로딩 및 코드 리베이스의 성능 영향; 버전 관리 문제 ( "dll hell")

Lib 전문가 : 코드가 항상 프로세스에로드되고 리베이스되지 않으므로 성능에 영향을 미치지 않습니다. 버전 관리 문제가 없습니다.

Lib cons : executable / process "bloat"-모든 코드가 실행 파일에 있으며 프로세스 시작시로드됩니다. 재사용 / 공유 금지-각 제품마다 고유 한 코드 사본이 있습니다.


rebase.exe를 사용하거나 / BASE 옵션을 link.exe에 전달하여 빌드시 리베이스를 수행 할 수도 있습니다. 이것이 효과적인지 여부는 런타임에 예기치 않은 주소 공간 충돌이 있는지 여부에 따라 다릅니다.
bk1e

24

정적 대 동적 라이브러리의 기술적 의미 (정적 파일은 여러 다른 실행 파일간에 코드 공유를 허용하는 하나의 큰 바이너리 대 동적 라이브러리에 모든 것을 번들로 묶음) 외에도 법적 의미가 있습니다.

예를 들어 LGPL 라이센스 코드를 사용하고 있고 LGPL 라이브러리에 정적으로 링크하여 큰 바이너리를 생성하는 경우 코드는 자동으로 오픈 소스 ( 자유롭게 무료) LGPL 코드가됩니다. 공유 객체에 연결하는 경우 LGPL 라이브러리 자체의 개선 사항 / 버그 수정 만 LGPL하면됩니다.

예를 들어 모바일 응용 프로그램을 컴파일하는 방법을 결정하는 경우 훨씬 더 중요한 문제가됩니다 (Android에서는 정적 대 동적을 선택할 수 있으며 iOS에서는 그렇지 않습니다-항상 정적입니다).


23

C ++ 프로그램은 두 단계로 구축됩니다

  1. 컴파일-객체 코드 (.obj)를 생성합니다
  2. 연결-실행 코드 (.exe 또는 .dll)를 생성합니다

정적 라이브러리 (.lib)는 .obj 파일의 번들 일 뿐이므로 완전한 프로그램이 아닙니다. 프로그램을 구축하는 두 번째 (연결) 단계를 거치지 않았습니다. 반면에 DLL은 exe와 유사하므로 완전한 프로그램입니다.

정적 라이브러리를 빌드하는 경우 아직 링크되지 않았으므로 정적 라이브러리 소비자는 사용했던 것과 동일한 컴파일러를 사용해야합니다 (g ++를 사용하는 경우 g ++를 사용해야 함).

대신 dll을 빌드하고 올바르게 빌드 한 경우 사용중인 컴파일러에 관계없이 모든 소비자가 사용할 수있는 완전한 프로그램을 빌드 한 것입니다. 그러나 크로스 컴파일러 호환성이 필요한 경우 dll에서 내보내는 경우 몇 가지 제한 사항이 있습니다.


1
이것은 나에게 뉴스입니다. DLL을 사용할 때 크로스 컴파일러에는 어떤 제한이 있습니까? 프로그래머가 동일한 툴체인을 필요로하지 않으면 서 DLL을 구현할 수 있습니다.
Dan

1
이 답변은 유익합니다. 약간의 경고를 추가 : consumers of your static library will have to use the same compiler that you used정적 라이브러리와 같은 C ++ 라이브러리를 사용하는 경우 #include <iostream>.
truthadjustr

동일한 컴파일러를 사용하지 않으면 C ++ dll을 사용할 수 없습니다 (표준 c ++ abi가 없기 때문에 기호가 다른 방식으로 엉망이됩니다). dll과 클라이언트 모듈은 모두 동일한 컴파일러와 동일한 빌드 설정을 사용해야합니다.
kcris

19

정적 라이브러리 만들기

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

동적 라이브러리 생성

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

정적 라이브러리는 클라이언트로 컴파일됩니다. .lib는 컴파일 타임에 사용되며 라이브러리의 내용은 소비하는 실행 파일의 일부가됩니다.

동적 라이브러리는 런타임에로드되며 클라이언트 실행 파일로 컴파일되지 않습니다. 여러 클라이언트 실행 파일이 DLL을로드하고 해당 기능을 활용할 수 있으므로 동적 라이브러리가 더 유연합니다. 또한 클라이언트 코드의 전체 크기와 유지 관리 성을 최소한으로 유지합니다.


13

시간이 지남에 따른 변경, 버전 관리, 안정성, 호환성 등을 신중하게 고려해야합니다.

공유 코드를 사용하는 두 개의 앱이있는 경우 서로 호환되어야 할 경우를 대비하여 해당 앱을 강제로 변경 하시겠습니까? 그런 다음 dll을 사용하십시오. 모든 exe는 동일한 코드를 사용합니다.

또는 서로를 분리하여 하나를 변경하고 다른 하나를 끊지 않았다고 확신 할 수 있습니다. 그런 다음 정적 lib를 사용하십시오.

DLL 지옥은 아마도 정적 라이브러리를 사용해야했지만 dll을 대신 사용했지만 모든 exe가 호환되지는 않습니다.


9

정적 라이브러리는 최종 실행 파일에 연결되어야합니다. 실행 파일의 일부가되어 어디를 가든지 따라갑니다. 동적 라이브러리는 실행 파일이 실행될 때마다로드되며 DLL 파일로 실행 파일과 별도로 유지됩니다.

실행 파일을 다시 연결하지 않고도 라이브러리에서 제공하는 기능을 변경할 수있을 때 DLL을 사용합니다 (실행 파일을 바꾸지 않고 DLL 파일 만 바꾸십시오).

동적 라이브러리를 사용할 이유가 없을 때마다 정적 라이브러리를 사용합니다.


다른 여러 응용 프로그램이 동일한 기능을 사용하는 경우 DLL을 사용할 수도 있습니다. 이렇게하면 설치 공간이 줄어들 수 있습니다.
Tim

또한 초기 개념 인 "플러그인"아키텍처를 확장하여 나중에 다시 작성하거나 다시 릴리스 할 필요없이 추가 / 알 수없는 기능을 허용하려는 경우 동적 라이브러리를 통해서만 수행 할 수 있습니다.
Tim

8

" 공유 라이브러리를 작성하는 방법 "에 대한 Ulrich Drepper의 논문 은 또한 공유 라이브러리 를 최대한 활용하는 방법 또는 "Dynamic Shared Objects"(DSO)라고하는 것을 자세히 설명하는 유용한 자료입니다. ELF 바이너리 형식 의 공유 라이브러리에 더 중점을 두지 만 일부 토론은 Windows DLL에도 적합합니다.


5

이 주제에 대한 훌륭한 토론을 위해서는 Sun 의이 기사 를 읽으십시오 .

삽입 라이브러리를 삽입하는 것을 포함하여 모든 이점에 들어갑니다. 삽입에 대한 자세한 내용은 이 기사 에서 확인할 수 있습니다 .


4

실제로 (대규모 프로젝트에서) 만들고있는 트레이드 오프는 초기로드 시간이며 라이브러리는 한 번에 서로 연결됩니다. 결정은 컴파일러가 필요로하는 링크가 충분히 오래 걸리는 것입니다. 총알을 물고 앞면에 넣거나 동적 링커가로드 할 때 할 수 있습니다.


3

라이브러리를 여러 실행 파일간에 공유하려는 경우 실행 파일의 크기를 줄이기 위해 동적으로 만드는 것이 좋습니다. 그렇지 않으면 반드시 정적으로 만드십시오.

dll을 사용하면 몇 가지 단점이 있습니다. 로드 및 언로드에 대한 추가 오버 헤드가 있습니다. 추가 종속성도 있습니다. executalbes와 호환되지 않도록 dll을 변경하면 작동이 중지됩니다. 반면에 정적 라이브러리를 변경하면 이전 버전을 사용하여 컴파일 된 실행 파일에는 영향을 미치지 않습니다.


3

라이브러리가 정적이면 링크 타임에 코드가 실행 파일과 링크됩니다. 이것은 동적 경로를 갔을 때보 다 실행 파일을 더 크게 만듭니다.

라이브러리가 동적이면 링크 타임에 필요한 메소드에 대한 참조가 실행 파일에 내장됩니다. 즉, 실행 파일과 동적 라이브러리를 제공해야합니다. 또한 라이브러리의 코드에 대한 공유 액세스가 안전하고 선호되는로드 주소인지 여부를 고려해야합니다.

정적 라이브러리와 함께 살 수 있다면 정적 라이브러리를 사용하십시오.


3

우리는 프로젝트에서 많은 DLL (> 100)을 사용합니다. 이 DLL은 서로 의존하므로 동적 링크 설정을 선택했습니다. 그러나 다음과 같은 단점이 있습니다.

  • 느린 시작 (> 10 초)
  • Windows는 고유 한 이름으로 모듈을로드하므로 DLL의 버전을 지정해야했습니다. 자체 작성된 구성 요소는 그렇지 않으면 잘못된 버전의 DLL (예 : 자체 분산 세트 대신 이미로드 된 구성 요소)을 가져옵니다.
  • 최적화 프로그램은 DLL 경계 내에서만 최적화 할 수 있습니다. 예를 들어, 옵티마이 저는 자주 사용되는 데이터와 코드를 나란히 배치하려고하지만 DLL 경계에서 작동하지 않습니다.

아마도 더 나은 설정은 모든 것을 정적 라이브러리 로 만드는 것이므로 실행 파일이 하나뿐입니다. 코드 중복이 발생하지 않는 경우에만 작동합니다. 테스트 가이 가정을 지원하는 것 같지만 공식 MSDN 인용문을 찾을 수 없습니다. 예를 들어 다음과 같이 1 exe를 만드십시오.

  • exe는 shared_lib1, shared_lib2를 사용합니다.
  • shared_lib1은 shared_lib2를 사용합니다
  • shared_lib2

shared_lib2의 코드와 변수는 최종 병합 실행 파일에 한 번만 존재해야합니다. 누구든지이 질문을지지 할 수 있습니까?


코드 중복을 피하기 위해 어떤 방식으로 일부 프리 컴파일러 지시문을 사용하려고하지 않습니까?
Paceman

Afaiac 사전 컴파일은 모듈 당 (exe / dll / lib) 기반에서만 작동합니다. 사전 컴파일은 주로 컴파일 속도를 높이기위한 것이지만 컴파일 단위 내에 여러 포함을 방지 할 수도 있습니다. 그러나 경비를 포함하는 것이이 효과를 얻는 더 좋은 방법입니다.
gast128 2016 년

2

정적 라이브러리는 해당 코드가 실행 파일로 컴파일 된 애플리케이션에 링크 될 때 라이브러리의 오브젝트 코드가 포함 된 아카이브입니다. 공유 라이브러리는 실행 파일로 컴파일되지 않는다는 점에서 다릅니다. 대신 동적 링커는 필요한 라이브러리를 찾기 위해 일부 디렉토리를 검색 한 다음이를 메모리에로드합니다. 하나 이상의 실행 파일이 동시에 동일한 공유 라이브러리를 사용할 수 있으므로 메모리 사용 및 실행 파일 크기가 줄어 듭니다. 그러나 실행 파일과 함께 배포 할 파일이 더 있습니다. 링커가 찾을 수있는 어딘가에 사용 시스템에 라이브러리가 설치되어 있는지 확인해야합니다. 정적 링크는이 문제를 제거하지만 더 큰 실행 파일을 생성합니다.


2

임베디드 프로젝트 또는 특수 플랫폼에서 작업하는 경우 정적 라이브러리가 유일한 방법 일뿐만 아니라 응용 프로그램으로 컴파일하기가 번거롭지 않은 경우가 많습니다. 또한 모든 것을 포함하는 프로젝트와 makefile이 있으면 인생이 더 행복해집니다.


2

나는 당신이 큰 코드베이스를 가지고 있다면 모두 더 낮은 수준의 라이브러리 (예 : Utils 또는 Gui 프레임 워크) 위에 구축되어 더 관리하기 쉬운 라이브러리로 분할 한 다음 정적 라이브러리로 만들고 싶다는 일반적인 규칙을 제시합니다. 동적 라이브러리는 실제로 아무것도 사지 않으며 놀라움이 적습니다. 예를 들어 싱글 톤 인스턴스는 하나뿐입니다.

나머지 코드베이스와 완전히 별 개인 라이브러리 (예 : 타사 라이브러리)가 있으면 dll로 만드는 것을 고려하십시오. 라이브러리가 LGPL 인 경우 라이센스 조건으로 인해 dll을 사용해야 할 수도 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.