development

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

big-blog 2020. 2. 19. 22:03
반응형

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


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


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

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

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

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


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

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

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

    예를 들어, mylib.lib포함 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()하면 바람직하지 않은 놀라운 결과를 얻을 수 있습니다.


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

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

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

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

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

Lib cons : executable / process "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는 동일한 코드를 사용합니다.

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

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


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

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


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

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

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


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


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

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


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


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

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


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

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

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


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


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


우리는 프로젝트에서 많은 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의 코드와 변수는 최종 병합 실행 파일에 한 번만 존재해야합니다. 누구든지이 질문을지지 할 수 있습니까?


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

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

참고 URL : https://stackoverflow.com/questions/140061/when-to-use-dynamic-vs-static-libraries



반응형