서의 공간

라이브러리에 대한 설명 본문

Misc.

라이브러리에 대한 설명

홍서의 2020. 11. 27. 14:41

[위키피디아]: 정적 라이브러리 - 위키백과, 우리 모두의 백과사전 (wikipedia.org)

[MSDN]: 연습: 정적 라이브러리 만들기 및 사용 (C++) | Microsoft Docs

[참고1]: 명월 일지 :: [C++] 라이브러리를 작성해서 사용하기 (Window 환경의 lib, dll) (tistory.com)

[참고2]: 라이브러리, 프레임워크, API, ABI 뜻 비교 정리 :: 아인스트라세의 SW 블로그 (tistory.com)

[참고3]: https://luyin.tistory.com/201

 

정적 라이브러리(Static Library)는 컴파일 타임에 호출자가 resolve 하는 루틴, 외부 함수, 변수의 집합으로 목적 파일과 단독 실행 파일 생성 중에 컴파일러 링커에 의해 대상 애플리케이션에 복사된다. 실행 파일을 이와 같은 방식으로 컴파일 하는 과정 또는 실행 파일 그 자체를 프로그램의 정적 빌드라고 부른다. 역사적으로 라이브러리는 정적(static)일 수만 있었다. 정적 라이브러리는 빌드/링크 과정에서 하나의 실행 파일을 만들기 위해 다른 정적 라이브러리 또는 목적 파일과 통합되거나, 런타임 시점에 대응하는 실행 파일의 주소 공간에서 컴파일 타임/링크 타임동안 결정된 정적 메모리 오프셋 위치에 로드된다.

장점과 단점

동적으로 링크하는 대신, 정적으로 라이브러리를 실행 파일에 링크하는 것은 여러가지 장점이 있다. 가장 눈에 띄는 점은 애플리케이션이 자신의 모든 라이브러리들이 존재하고 그것이 정확한 버전인 것을 확신할 수 있다는 점이다. 이것은 DLL 지옥 또는 그 이상의 의존성 지옥으로 알려진, 의존성과 관련된 문제들을 피할 수 있다. 정적 링킹은 또한 애플리케이션이 단일 실행 파일에 포함되게 함으로써 배포와 설치를 간단하게 만든다.

정적 링킹을 통해서 직접 또는 간접적으로 대상 실행 파일(또는 라이브러리)에 참조되는 라이브러리의 부분들을 충분히 포함할 수 있다. 동적 라이브러리를 통해서는 전체 라이브러리가 로드되며, 함수가 애플리케이션에 의해 호출되는 이 방식의 장점은 알려져 있지 않다. 이 장점이 중요한지는 라이브러리의 실제 구조에 달려있다.

정적 링킹에서는 라이브러리 코드가 실행 파일 내에 저장되기 때문에, 실행 파일의 크기는 분리된 파일에 저장하는 동적 링킹 때 보다 커진다. 그러나 라이브러리 파일들이 애플리케이션의 부분으로서 세어진다면, 전체 크기는 비슷하거나 컴파일러가 사용되지 않은 심볼들을 제거한 경우에는 더 작아질 수 있다. 마이크로소프트 윈도우에서 애플리케이션이 필요한 라이브러리 파일들을 포함하는 것은 흔한 일이다. 유닉스 계열 시스템들에서는 패키지 관리 시스템들이 사용 가능한 정확한 라이브러리 파일들을 보장하기 때문에 윈도우보다는 덜 흔한 일이다. 이것은 라이브러리 파일들이 많은 애플리케이션들 사이에서 공유될 수 있게 하며, 공간을 절약한다. 또한 라이브러리를 사용하는 애플리케이션의 업데이트 대신, 라이브러리만을 업데이트함으로써 버그와 보안 결함을 고칠수 있게 한다. 실제로 많은 실행 파일들이(특히 윈도우 용) 정적 라이브러리와 동적 라이브러리들을 모두 사용한다.

 

동적 링크 라이브러리(dynamic-link library, DLL)는 마이크로소프트 윈도우에서 구현된 동적 라이브러리이다. 내부에는 다른 프로그램이 불러서 쓸 수 있는 다양한 함수들을 가지고 있는데, 확장DLL인 경우는 클래스를 가지고 있기도 한다. DLL은 COM을 담는 그릇의 역할도 한다.

사용하는 방법에는 두 가지가 있는데,

  • 묵시적 링킹(Implicit linking) : 실행 파일 자체에 어떤 DLL의 어떤 함수를 사용하겠다는 정보를 포함시키고 운영체제가 프로그램 실행 시 해당 함수들을 초기화한 후 그것을 이용하는 방법과,
  • 명시적 링킹(Explicit linking) : 프로그램이 실행 중일 때 API를 이용하여 DLL 파일이 있는지 검사하고 동적으로 원하는 함수만 불러와서 쓰는 방법이 있다.

전자의 경우는 컴파일러가 자동으로 해주는 경우가 많으며, 후자의 경우는 사용하고자 하는 DLL이나 함수가 실행 환경에 있을지 없을지 잘 모르는 경우에 사용된다. (때때로 메모리 절약을 위해 쓰이기도 한다.)


1. Static library

Dynamic (linking) library (흔히 얘기하는 DLL)을 설명하기 위해 간단하게 정리한다.

특정 기능의 라이브러리를 static 하게 제작한다는 것은 link 단계에서
라이브러리(*.lib 파일)를 실행 바이너리에 포함시킨다는 얘기이다.

즉, 라이브러리의 동작 코드가 이를 사용하는 실행 바이너리 속에
포함되기 때문에 별도의 추가 작업없이, 그리고 독립적으로(실행 바이너리만으로...)
라이브러리 함수들을 사용할 수 있다.

하지만, 정적 라이브러리를 사용하는 프로그램이 늘어나면 날수록
불필요하게 실행 파일들의 크기가 커지며,
라이브러리가 동시에 여러 실행 바이너리에 포함되어 실행되는 경우
메인 메모리의 공간 활용 효율이 떨어지는 등 multiple-caller program이 존재하는 경우 그다지 바람직하지 않다.

정적 라이브러리를 사용하기 위해서는 프로젝트 설정의 Link 옵션에
라이브러리를 추가해 주거나 아래의 #pragma 지시자를 사용하면 된다.

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


2. Dynamic (linking) library : DLL

말 그대로 "동적으로 링크하여 사용하는 라이브러리" 이다.

동적 라이브러리는 이를 사용하고자 하는 실행 바이너리에서
필요시 사용할 수 있도록 최소한의 정보만 포함하여 링크하거나,
아예 독립적으로 DLL을 로드/사용/해제할 수 있다.

1) Implicit linking

DLL을 구현하고 컴파일하고 나면 static library와는 다르게 output file이 2개 생성된다.
하나는 *.lib 파일이고 하나는 *.dll 파일이다.
여기서 *.lib 파일은 static library의 *.lib 파일과는 전혀 다르다.

Static library의 *.lib 파일은 라이브러리 전체 코드를 포함하는 바이너리이며,
DLL의 *.lib 파일은 DLL이 제공하고자 하는 함수 정보(함수명)을 가지는 정보 파일이다.

DLL의 *.lib 파일을 이용하여 링킹하는 것을 암시적 링킹(implicit linking)이라고 한다.
실행 바이너리를 링크 단계에서 실행 바이너리의 *.obj 파일들과 DLL의 *.lib 파일을
함께 링크하여 이 정보를 토대로 runtime에 DLL의 함수 코드를 참조하게 되는 것이다.
(한 줄로 요약하면 *.lib 파일은 링크시 필요하고, *.dll 파일은 실행시 필요하다)

정적 라이브러리를 사용할 때와 같이 프로젝트 설정의 Link 옵션에
라이브러리를 추가해 주거나 아래의 #pragma 지시자를 사용하면 된다.

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

2) Explicit linking

명시적 링킹에서는 *.lib 파일이 필요하지 않다.
실행 바이너리 링크 단계에서 DLL의 함수 정보가 필요하지 않기 때문이다.

명시적 링킹에서 사용되는 세 가지 함수와 역할은 다음과 같다.

1. LoadLibrary : 필요한 DLL을 프로세스 가상 메모리에 맵핑한다.
2. GetProcAddress : DLL 함수의 포인터를 획득한다.
3. FreeLibrary : 프로세스 가상 메모리에서 DLL을 반환한다.

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

여기서 주의할 점이 물리 메모리가 아닌 가상 메모리에서의 반환(해제)라는 것이다.
레퍼런스 카운트는 각 프로세스 내부의 호출 회수이지,
전체 프로세스 간의 호출 회수가 아니라는 것이다.

이러한 레퍼런스 카운트를 두는 이유는 프로그램 실행 중에
DLL을 가상 메모리에 할당, 해제할 수 있도록 하기 위함이다.
(Implicit linking 방식에서는 이러한 장점을 얻을 수 없다.)

정리해서 명시적 링킹의 장점을 세 가지 정도 정리해 보면...

1. DLL이 필요한 시점에서 로딩하고, 불필요해지면 반환하기 때문에 메모리가 절약된다.
2. 프로그램 실행 중에 DLL 교체 및 선택이 가능하다.
3. 암시적 링킹은 프로그램 실행 전에 필요한 모든 DLL을 메모리에 로딩한다.
때문에 실행까지 걸리는 시간이 길어질 수 있다. 반면에 명시적 링킹은 필요한 순간에
하나씩 DLL을 로딩할 수 있기 때문에 그만큼 실행까지 걸리는 시간이 짧고,
DLL 로딩에 걸리는 시간을 분산시킬 수 있다.

뭐 위와 같이 명확한 장점들이 있음에도 불구하고 암시적 링킹이 더 선호되는 경우가 있는데,
이는 사용하기 쉽기 때문이다. 코드가 간결해 진다는 것, 사용하기 쉽다는 것은
대부분의 경우에 있어서의 성능보다도 더 큰 장점을 지니는 것이 아닌가 생각해 본다.


 

Comments