시스템 프로그래밍 21장 Dynamic Linking Library

김주현·2021년 10월 25일
0

시스템 프로그래밍

목록 보기
21/21
  1. 라이브러리와 printf!

도대체 실행 가능한 printf 함수의 바이너리는 어디에 있을까. stdio.h 파일안에는 printf 함수의 선언만이 존재할 뿐이다.

  • 해답은 라이브러리

호출 가능한 printf 함수는 라이브러리 안에 있다. 라이브러리란 여러 프로그램에서 자주 사용하는 함수와 데이터들을 실행이 가능한 바이너리 형태로 묶어놓은 파일을 의미한다.

printf 함수의 선언 -> stdio.h, printf 함수의 정의가 컴파일 된 바이너리 코드 -> 라이브러리

C 런타임 라이브러리 : 호출 가능한 ANSI 표준 C함수들로 구성

라이브러리 이름이 d로 끝나는 경우는 디버그 모드로 컴파일할 때 사용되는 라이브러리이다.

  • 정적라이브러리 프로젝트 생성

swap.h에는 swap 함수의 선언이 존재하고, swap.cpp에는 swap 함수의 정의가 존재한다.

#pragma comment(lib,"포함할 라이브러리 이름")으로 라이브러리를 포함시킬수 있다.

  • 정적라이브러리


위 그림은, 라이브러의 생성과 실행파일 생성의 전체 과정을 한눈에 보여준다. swap.cpp와 swap.h를 기반으로 라이브러리 SwapStaticLib.lib를 생성하고 SwapLibTest.cpp를 컴파일해서 오브젝트 파일 SwapLibTest.obj를 생성하고, 이 과정에서 역시 swap.h를 필요로 한다.

이제 SwapStatucLib.lib와 SwapLibTest.obj가 하나로 묶여서 SwapLibTest.exe.라는 이름의 실행파일이 생성되는 과정에 대해서 알아보자. 이 과정을 주도하는 것은 링커이고 링커에 의해서 진행되는 이 작업을 가리켜 링크라 한다. 여기서 중요한 사실은 라이브러리가 실행파일 안에 포함된다는 것이다. 실행파일이 처음 만들어질 때 부터 하나로 묶여진다. 그래서 이러한 형태의 라이브러리를 가리켜 "정적 라이브러리"라 한다.

  1. 또 다른 라이브러리 DLL

-DLL과 정적 라이브러리의 차이점

정적라이브러리와 DLL모두 라이브러리이지만 정적라이브러리는 정적인 특성을 DLL은 동적인 특성을 지닌다. 이는 실행 가능한 프로그램에서 라이브러리를 가져다 쓰는 방법에 따른 차이점이다.

정적 라이브러리의 특성

실행파일 a,b,c .exe는 실행시 필요로 하는 라이브러리 SwapStaticLib.lib의 바이너리 코드를 완전히 포함하고 있다.
이렇듯 라이브러리 코드를 완전히 포함해서 .exe 파일을 생성하는 형태의 링크를 가리켜 "정적 링크"라 한다.

장점 1. 실행의 독립성 : 실행파일만 있으면 언제 어디에서건 실행가능하다(실행파일 안에 라이브러리도 함께 포함되었으므로 .lib 파일은 프로그램의 실행을위해서 더이상 필요가없다.

단점 1. 세 실행파일 .exe는 많은 메모리 공간을 차지한게된다 프로그램 실행시와 하드디스크에 저장되어있는 상태에서도 마찬가지이다.

세개의 실행파일이 모두 동일한 라이브러리를 포함하고 있기 때문에 그만큼 메모리 공간을 더차지하게 됨.

  • DLL의 특성

세게의 실행파일이 동일하게 포함하는 라이브러릴 별도로 저장하고 공유해서 메모리를 절약함.

DLL은 실행파일의 일부로 포함되지 않고 독립적으로 저장되는라이브러리이다. DLL을 사용하고자 하는 프로세스는 자신의 가상 메모리 주소에 DLL을 매핑시켜야 한다. 그 다음에야 비로소 DLL이 제공하는 함수를 호출할 수가 있다. 즉,두 개의 파일이(실행파일과 DLL파일)하나의 가상 메모리를 구성하는 꼴이 된다.

DLL의 장점 둘 이상의 프로세스가 동일한 DLL을 공유할 경우, 메인 메모리 에서 페이지 단위로 공유가 이뤄진다.

  • DLL 제작 1: 암묵적 연결

"__declspec(dllimport)" 는 DLL로부터 제공받을 함수를 선언할 때 사용된다. 위 예제의 헤더파일을 보면,swap함수의 선언앞에 이문장이 들어가 있다. 이는 swap 함수를 DLL로 부터 제공받겠다는 의미이다.

"__declspec(dllexport)"는 외부에 제공할 함수를 선언할 때 사용된다. 이어서 등하는 함수를 외부에서 사용할 수 있는 형태의 DLL로 라이브러리화 하겠다는 것이다.

  • .lib파일과 .dll 파일의 용도

DLL을 생성하는 과정에서 만들어진 .lib 파일은 DLL이 제공하고자 하는 함수 정보(이름 정보(를 지닌다.

DllTest.cpp는 DLL이 제공하는 swap함수를 호출하는 코드로 구성된다.

일단,컴파일러에 의해 DllTest.obj를 생성해야한다. 이 과정에서 다음 선언을 담은 헤더파일이 필요하다. 호출하는 함수의 선언 정보만 있어도 컴파일러는 문제 없이 컴파일을 한다.

__declspec(dllimport) void swap(int v1, int v2);

컴파일 되었으니 링커에 의해서 실행파일을 만들차례. 실행파일의 생성은 선언에 대한 정의가 완전히 존재해야함. 그런데 우리는 DLL을 하나로 묶으려는 것이 아닌 실행시간에 참조하고픈것. 이러한 문제점을 해결하기 위해 DLL이 만들어질때 .lib파일도 함께 만들어짐. DLL을 생성할 때 함께 만들어지는 .lib 파일에는 링커가 실행파일을 만드는 데 필요한 정보가 담겨 있다. SwapDll.lib 파일은 링커에게 다음과 같은 정보를 건넬 것이다.

"DllTest.obj에서 호출하는 swap 함수는 SwapDll.dll 파일에 잘 정의되어 있다"
링커는 lib파일이 건네주는 정보를 참조해서 실행파일을 만들어 낸다. 물론 실행시간에 해당 DLL을 참조하는 실행파일을 만들게된다.

즉 ".lib 파일은 링크할 때 필요하고, .dll 파일은 실행할 때 필요하다."

  • DLL의 활용
  1. 외부 파일에서 호출 하는 함수가 외부에 존재한다는 선언 추가(헤더로해도되고 다음선언을 직접 넣어도됨)

__declspec(dllimport) void swap(int v1, int v2)

2.정적 라이브러리를 만들고 실행할 때, 참조할 lib 파일 이름을 명시해 주었다. 여기서도 lib파일 이름을 명시해줘야함.(프로젝트 속성창에서 설정하거나 아래문장을 코드의 맨 앞에 삽입

#pragma comment(lib,"SwapDll.lib")

마지막 단계로 프로젝트가 찾을 수 있는 위치에 .lib 파일을옮김 표준 검색 경로 기준으로 디렉토리 중 한 곳에 .dll 파일을 옮김

-DLL과 extern 선언

extern "C" 이 문장은 C++문법의 일부분으로, 네임 맹글링을 막는다.

DLL을 통해서 제공하고자 하는 함수에는 다음 문장을 붙여준다.
extern "C" __declspec(dllimport)

반면에 DLL에 존재하는 함수를 호출하고자 하는 경우에는 다음 문장을 붙여준다.
extern "C" __declspec(dllexport)

-DLL 제작 2: 명시적 연결

암묵적,명시적 - DLL을 참조하는 방식에 따라 구분한다. 소스코드 내에 DLL 연결 코드가 명시적으로 존재한다면 명시적 연결방법이다.
암묵적 연결방법은 소스코드 내에 DLL 연결에 대한 명시적인 코드가 존재하지 않는다.
명시적 연결에서는 .lib 파일이 필요 없다. 필요한 DLL 이름을 명시적으로 지정하기 때문이다. 따라서 암묵적 연결에서 보여준 .lib파일에 관련됨 일체의 작업들이 명시적 연결에서는 필요없다.

LoadLibrary 함수는 필요한 DLL을 프로세스 가상 메모리에 매핑하는 기능을 제공한다. 그리고 GetProcAddress 함수를 통해서 필요한 함수의 포인터를 획득한다. 이렇게 획득한 함수 포인터를 이용해 DLL이 제공하는 함수를 호출할 수 있다.

마지막으로 FreeLibrary 함수를 호출해서 DLL을 반환한다. DLL의 반환은 가상 메모리에서의 반환을 의미할 뿐, 물리 메모리에서의 반환을 의미하지는 않는다. DLL참조하는 프로세스가 하나도 존재하지 않는 시점이 물리 메모리에서 DLL이 반환되는 시점이다.

프로세스는 내부적으로 DLL의 레퍼런스 카운트를 계산한다. LoadLibrary 함수 호출 시 지정된 DLL의 레퍼런스 카운트는 1씩 증가하고, FreeLivrary 함수 호출 시 지정된 DLL의 레퍼런스 카운트는 1씩 감소한다. 레퍼런스 카운트가 0이되는 때에 해당 DLL은 프로세스 가상 메모리에서부터 해제된다.

명시적 연결방법 장점

  1. DLL이 필요한 시점에서 로딩하고, 불필요해지면 반환하기 때문에 메모리가 절약된다.

  2. 프로그램 실행 중에 DLL 교체 및 선택이 가능하다.

  3. 암묵적 연결방식은 프로그램 실행 전에, 필요한 모든 DLL을 메모리에 로딩한다. 때문에 실행까지 걸리는 시간이 길 수 있다. 반면에 명시적 연결방식은 필요한 순간에 하나씩 DLL을 로딩할 수있기 때문에 그만큼 실행까지 걸리는 시간이 짧고, DLL로딩에 걸리느 시간을 분산시킬수있다.

-한 번 이상 로드될 수 있는 DLL

일반적으로 다음과 같이 표현되는 DLL의 특성
"DLL은 물리 메모리에 한번 올라가면,이 DLL을 공유하는 프로세스가 모두 종료될 때까지 물리 메모리에 존재한다."

[단계 1]
프로세스 AAA가 실행된다. 그런데 이 프로세스는 Best.dll을 필요로 한다. 따라서 이DLL은 가상 메모리 영역에 매핑되면서 메모리에 올라간다. 메모리에 올라간다는 것은 물리 메모리에 할당되는 것을 의미한다.

[단계 2]
Best.dll을 필요로 하는 또 다른 프로세스 BBB가 실행된다. 그러나 이번에는 Best.dll을 물리 메모리에 올리지 않는다. 이미 단계 1에서 한번 올렸기 떄문이다. 올라간 메모리를 그대로 참조할 수 있도록 BBB프로세스의 가상 메모리 영역에 매핑만 할 뿐이다.

이 그림에서 보여주는 메커니즘을 가리켜 메모리 매핑이라 한다. DLL은 이 메커니즘을 기반으로 완성된다.

위 그림에서 주목 할 부분이 한 가지 더 있다. Best.dll이 할당된 가상 메모리 주소이다. 모든 프로세스가 동일한 주소에 할당했다. 이것이 DLL 공유가 가능한 이유이다. 다시 말해서 두 프로세스가 동일한 DLL을 동일한 가상 주소에 매핑했기 때문에 페이지 단위로 공유가 가능한 것이다.

[단계 3]

이제 프로세스 AAA가 종료되었다. 그러나 AAA실행 시 메모리에 올라갔던 Best.dll은 여전히 남아 있다. 프로세스 BBB의 실행을 위해서다.

[단계 4]

마지막으로 프로세스 BBB도 종료되었다. 이번에는 Best.dll을 참조하는 프로세스가 하나도 존재하지 않으므로 Best.dll도 함께 반환된다. 즉 할당된 물리 메모리를 반환하게 된다.

처음 DLL이 빌드될 때 DLL이 할당되어야 할 가상 메모리 주소가 링커에 의해 결정된다.

위 그림과 같이 추가로 Best.dll이 필요하게 된 경우 두개의 Best.dll이 물리 메모리에 올라가게 된다. 즉 DLL도 물리 메모리에 두 번 이상 올라갈 수 있음을 기억해야 한다.

03 . 헤더파일의 갯수

일단 DLL을 만드는 과정도 일반 프로그램 개발과 별차이가 없는 관계로 헤더파일을 필요로함

뿐만 아니라, 암묵적 연결방식의 DLL 사용자를 위한 헤더파일도 제공해야 함. 그런데 DLL을 빌드 할 때 필요로 하는 함수 선언과 DLL을 참조할 때 필요로 하는 함수 선언이 다르다는 문제가 있음 하나는 Import 선언을 다른 하나는 export선언을 해야마난다. 이를 위해서는 두 개의 헤더파일이필요하다

extern "C"선언은 C++에서만 가능하기 떄문에 C컴파일러용과 C++컴팡일러용을 나눠야함

  • 하나의 헤더파일로 모두 지원하기

__cplusplus는 c++ 컴파일러로 컴파일 할때 기본적으로 정의하는 매크로

매크로 _COMPLING_DLL_CALCULATOR가 정의되면 매크로 LIBSPEC가 export 선언의 역할을 하고, 그렇지 않으면 import 선언의 역할을 하도록 구성되어있음

이것만은 알고 갑시다.

  • 정적 라이브러리의 특성

정적 라이브러리의 특성은 실행파일 안에 라이브러리가 하나로 묶인다는 것이다. 따라서 실행파일의 크기는 커지지만 실행파일만 있으면 언제 어디서든 실행이 가능하다.

  • DLL의 특징

DLL의 가장 큰 특징은 실행파일 안에 포함되지 아니하고,프로그램 실행 시에 참조된다는 점이다. 따라서 실행파일의 크기가 작다. 또한 물리 메모리에 한번 올라간 DLL은 둘이상의 프로세스가 각각 자신의 가상 메모리에 매핑해서 공유하는 구조를 지니기 때문에 메모리 사용에 대한 효율성이 높다.

0개의 댓글