문제

C++에서 클래스 라이브러리를 생성할 때 동적(.dll, .so) 및 정적(.lib, .a) 도서관.이들 사이의 차이점은 무엇이며 언제 어느 것을 사용하는 것이 적절합니까?

도움이 되었습니까?

해결책

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

동적 라이브러리는 별도로 저장 및 버전으로 저장됩니다. 코드와 함께 배송 된 원본이 아닌 동적 라이브러리 버전을로드 할 수 있습니다. 만약에 업데이트는 원래 버전과 호환되는 바이너리로 간주됩니다.

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

다이나믹 라이브러리는 대부분의 시간에 더 나은 접근 방식으로 간주되었지만 원래는 주요 결함 (Google DLL Hell)이 있었는데, 이는 최근 Windows OS (특히 Windows XP)에 의해 제거되었습니다.

다른 팁

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

  • 싱글톤: 전역/정적이며 고유해야 하는 항목이 있으면 정적 라이브러리에 넣을 때 매우 주의하세요.여러 DLL이 해당 정적 라이브러리에 연결되어 있으면 각각 고유한 싱글톤 복사본을 갖게 됩니다.그러나 응용 프로그램이 사용자 지정 DLL이 없는 단일 EXE인 경우에는 문제가 되지 않을 수 있습니다.

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

    예를 들어, mylib.lib 포함 a.obj 그리고 b.obj DLL/EXE는 다음의 함수나 변수만 참조합니다. a.obj, 전체 b.obj 링커에 의해 삭제됩니다.만약에 b.obj 전역/정적 개체가 포함되어 있으면 해당 생성자와 소멸자가 실행되지 않습니다.해당 생성자/소멸자에 부작용이 있는 경우 해당 생성자/소멸자가 없으면 실망할 수 있습니다.

    마찬가지로, 정적 라이브러리에 특별한 진입점이 포함된 경우 해당 진입점이 실제로 포함되는지 주의해야 할 수도 있습니다.임베디드 프로그래밍(Windows는 아님)에서 이에 대한 예는 특정 주소에 있는 것으로 표시된 인터럽트 핸들러입니다.또한 인터럽트 핸들러가 삭제되지 않도록 진입점으로 표시해야 합니다.

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

  • 디버그 기호: 각 정적 라이브러리에 대해 별도의 PDB를 원할 수도 있고 DLL/EXE용 PDB에 포함되도록 디버그 기호를 개체 파일에 배치할 수도 있습니다.Visual C++ 설명서에서는 다음과 같이 설명합니다. 꼭 필요한 옵션.

  • RTTI: 당신은 여러 가지로 끝날 수 있습니다 type_info 단일 정적 라이브러리를 여러 DLL에 연결하는 경우 동일한 클래스의 개체입니다.프로그램이 다음과 같이 가정한다면 type_info "싱글톤" 데이터이며 다음을 사용합니다. &typeid() 또는 type_info::before(), 바람직하지 않고 놀라운 결과를 얻을 수 있습니다.

lib는 애플리케이션 실행 파일 내에 번들로 제공되는 코드 단위입니다.

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

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

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

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

lib 단점:실행 파일/프로세스 "bloat" - 모든 코드는 실행 파일에 있으며 프로세스 시작 시 로드됩니다.재사용/공유 없음 - 각 제품에는 고유한 코드 사본이 있습니다.

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

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

예를 들어 모바일 애플리케이션을 컴파일하는 방법을 결정하는 경우 훨씬 더 중요한 문제가됩니다 (Android에서는 iOS에서 정적 대 동적을 선택할 수 있습니다. 항상 정적입니다).


정적 라이브러리 생성

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

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

  1. 컴파일 - 개체 코드 생성 (.OBJ)
  2. 링크 - 실행 가능한 코드 생성 (.exe 또는 .dll)

정적 라이브러리 (.lib)는 .obj 파일의 번들이므로 완전한 프로그램이 아닙니다. 프로그램 구축의 두 번째 (링크) 단계를 거치지 않았습니다. 반면에 DLL은 Exe와 같으므로 완전한 프로그램입니다.

정적 라이브러리를 구축하는 경우 아직 연결되어 있지 않으므로 정적 라이브러리의 소비자는 사용한 것과 동일한 컴파일러를 사용해야합니다 (G ++를 사용하는 경우 G ++를 사용해야합니다).

대신 DLL을 만들면 (그리고 그것을 만들었습니다. 바르게), 당신은 어떤 컴파일러를 사용하든 모든 소비자가 사용할 수있는 완전한 프로그램을 구축했습니다. 그러나 크로스 컴파일러 호환성이 원하는 경우 DLL에서 내보내는 데 몇 가지 제한이 있습니다.

시간에 따른 변화, 버전 작성, 안정성, 호환성 등에 대해 신중하게 생각해야합니다.

공유 코드를 사용하는 두 가지 앱이있는 경우 서로 호환 해야하는 경우 해당 앱을 함께 변경하도록 하시겠습니까? 그런 다음 DLL을 사용하십시오. 모든 EXE는 동일한 코드를 사용합니다.

아니면 서로를 격리시키고 싶어서 하나를 바꾸고 다른 사람을 깨지 않았다고 확신 할 수 있습니다. 그런 다음 정적 리브를 사용하십시오.

DLL HELL은 아마도 정적 LIB를 사용해야했을 때 DLL을 대신 사용했지만 모든 ex가 그것과 함께 혼란 스럽지는 않습니다.

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

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

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

실행 파일을 다시 링크하지 않고도 라이브러리가 제공 한 기능을 변경할 수 있으려면 DLL을 사용합니다 (실행 파일을 교체하지 않고도 DLL 파일을 교체 함).

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

Ulrich Drepper의 논문에 "공유 라이브러리를 작성하는 방법"공유 라이브러리를 활용하는 가장 좋은 방법 또는"DSO (Dynamic Shared Objects) (DSO)라고 지칭하는 좋은 자료는 또한 공유 라이브러리에 더 중점을 둡니다. 꼬마 요정 이진 형식이지만 일부 토론은 Windows DLL에도 적합합니다.

이 주제에 대한 훌륭한 토론을 위해 이 기사 태양에서

중재 라이브러리를 삽입 할 수있는 등 모든 이점에 들어갑니다. 중재에 대한 자세한 내용은 찾을 수 있습니다 이 기사는 여기에 있습니다.

실제로 당신이 만드는 트레이드 오프는 (대규모 프로젝트에서) 초기로드 시간에, 라이브러리는 한 번에 연결되게 될 것입니다. 총알을 물고 앞쪽으로 할 수 있거나 다이나믹 링커가로드 시간에 수행 할 수 있습니다.

라이브러리가 여러 실행 파일 사이에서 공유되는 경우 종종 실행 파일의 크기를 줄이기 위해 역동적으로 만드는 것이 합리적입니다. 그렇지 않으면 확실히 정적으로 만듭니다.

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

라이브러리가 정적 인 경우 링크 시간에 코드가 실행 파일과 연결됩니다. 이렇게하면 실행 가능이 더 커집니다 (동적 경로를 사용하는 것보다).

라이브러리가 동적 인 경우 링크 시간에 필요한 방법에 대한 참조는 실행 파일에 내장됩니다. 즉, 실행 파일과 동적 라이브러리를 배송해야합니다. 또한 라이브러리의 코드에 대한 공유 액세스가 안전하고 선호되는 부하 주소가 다른 것들 중에서도 안전하고 선호되는 부하 주소인지 고려해야합니다.

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

정적 라이브러리는 라이브러리의 객체 코드가 포함 된 아카이브이며, 응용 프로그램에 링크 된 경우 코드가 실행 파일로 컴파일됩니다. 공유 라이브러리는 실행 파일에 컴파일되지 않았다는 점에서 다릅니다. 대신 동적 링커는 필요한 라이브러리를 찾는 일부 디렉토리를 검색 한 다음이를 메모리에로드합니다. 하나 이상의 실행 파일은 동시에 동일한 공유 라이브러리를 사용할 수 있으므로 메모리 사용 및 실행 가능 크기를 줄일 수 있습니다. 그러나 실행 파일과 함께 배포 할 파일이 더 많습니다. 라이브러리가 링커가 찾을 수있는 곳 어딘가에 사용 시스템에 라이브러리가 설치되어 있는지 확인해야합니다. 정적 링크는이 문제를 제거하지만 더 큰 실행 파일을 초래합니다.

임베디드 프로젝트 또는 특수 플랫폼에서 작업하는 경우 정적 라이브러리가 유일한 방법 인 경우, 여러 번 응용 프로그램에 컴파일하는 번거 로움이 적습니다. 또한 모든 것을 포함하는 프로젝트와 makefile을 갖는 것은 인생을 더 행복하게 만듭니다.

우리는 프로젝트에서 많은 DLL (> 100)을 사용합니다. 이 DLL은 서로에 대한 종속성을 가지고 있으므로 동적 링크 설정을 선택했습니다. 그러나 다음과 같은 단점이 있습니다.

  • 느린 시작 (> 10 초)
  • Windows는 이름의 독창성에 모듈을로드하기 때문에 DLL은 버전을 사용해야했습니다. 자신의 서면 구성 요소는 그렇지 않으면 DLL의 잘못된 버전을 얻을 수 있습니다 (즉, 자체 분산 세트 대신 이미로드 된 것).
  • Optimizer는 DLL 경계 내에서만 최적화 할 수 있습니다. 예를 들어 Optimizer는 자주 사용되는 데이터와 코드를 서로 옆에 배치하려고하지만 DLL 경계에서는 작동하지 않습니다.

어쩌면 더 나은 설정은 만들었을 것입니다 모든 것 정적 라이브러리 (따라서 실행 파일이 하나뿐입니다). 코드 복제가 발생하지 않는 경우에만 작동합니다. 테스트는이 가정을 뒷받침하는 것처럼 보이지만 공식 MSDN 견적을 찾을 수 없었습니다. 예를 들어 1 exe를 사용합니다.

  • Exe는 shared_lib1, shared_lib2를 사용합니다
  • shared_lib1 shared_lib2를 사용합니다
  • shared_lib2

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

나는 당신이 대형 코드베이스를 가지고 있다면, 모두 하위 레벨 라이브러리 (예 : Utils 또는 GUI 프레임 워크) 위에 구축 된 대형 코드베이스를 가지고 있다면 더 관리하기 쉬운 라이브러리로 분할 한 다음 정적 라이브러리로 만들려고합니다. 다이나믹 라이브러리는 실제로 당신에게 아무것도 사지 않으며 놀라움이 적습니다. 예를 들어 싱글 톤 인스턴스는 하나뿐입니다.

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

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top