DLL 이해하기

배경지식


글 읽을 때 헷갈리지 않게 빌드 과정 및 용어 정리
빌드 과정 = 
소스코드(C++,JAVA 등) -> 실행가능한 코드(기계어, 바이트코드, 오브젝트파일) -> 실행파일(exe 등)

라이브러리(library)

라이브러리는 실행가능한 코드 모음이다. 프로그래밍 모듈화를 도와준다.

string의 글자수를 세주는 여러 프로그램에서 유용하게 쓸 수 있는 함수를 만들었다고하자.

글자수를 세는 기능이 필요한 프로그램을 만들때마다 이 함수를 직접 소스코드에 쓰는것은 낭비다.
라이브러리로 만들어 두면, 매번 소스코드에 쓰지 않고도 실행파일을 만들 수 있게 해준다.

1. 기능을 실행가능한 코드(기계어 또는 바이트코드)로 미리 만들어 놓는다.
  = 라이브러리
2. 다른 프로그램에서 기능을 사용할 때 라이브러리랑 링크해서 기능을 사용한다.

링크 시점에 따라서 정적(static) 라이브러리와 동적(dynamic) 라이브러리로 나뉜다.

정적 라이브러리

링크 시점이 실행 파일을 만들 때이다.

실행파일은 기존 프로그램의 실행가능한 코드(기계어/바이트코드)와 라이브러리의 실행가능한 코드를 합쳐서 만든다.

실행파일 = 내 프로그램 + 라이브러리

동적 라이브러리

링크 시점이 런타임이다.

실행파일은 기존 프로그램의 실행가능한 코드만 가지고 만든다.
라이브러리 코드랑 실행 파일은 관련이 없기 때문에, 라이브러리 업데이트를 하더라도 실행파일을 새로 만들 필요가 없다.

실행파일 = 내 프로그램

*cf) 자바는 JVM 가상환경 특징이 있어서 .jar 이 실행가능한 파일이자 실행파일, 동시에 라이브러리이기도 하다. JVM 가상환경에 필요할 때 로드 되므로 굳이 구분하자면 동적이라고 볼 수 있다.

동적링크라이브러리(dll)이란?

기본 배경

윈도우 운영체제용 라이브러리.

라이브러리이기 때문에 여러 프로그램에서 동시에 사용할 수 있는 코드와 데이터(=실행가능한 코드)이다.

dll을 사용함으로써, 프로그램은 여러 컴포넌트로 모듈화할 수 있다. 런타임에서 필요한 모듈만 메모리에 적재함으로써 로딩시간을 줄이고 메모리를 효율적으로 사용할 수 있다.

예를 들어 윈도우 시스템에서 Comdlg32.dll 은 공통 다이얼로그 상자와 관련한 함수들을 실행한다. 

실행중인 모듈 목록

동작 방식

DLL 로드

1. 운영체제가 DLL 모듈이 갖고 있는 실행가능한 코드(기계어+데이터)를 메모리에 로드한다.
2. 운영체제는 DLL을 호출한 프로세스한테 DLL이 저장된 메모리 주소를 넘겨준다.
3. 프로세스는 그 주소를 저장해두고 DLL의 기능들을 사용할 수 있다.

DLL 언로드

1. DLL 사용이 끝나면 운영체제는 리소스 관리를 위해 메모리에서 DLL을 언로드할 수도 있다.

윈도우 운영체제에서 DLL로 구현한 파일들

- .ocx : 액티브X컨트롤 파일

- .cpl : 컨트롤 패널 파일

- .drv : 디바이스 드라이버 파일

운영체제에 제한이 있고(윈도우에서만 사용), 응용인 액티브X 등도 죽어가고 있어서 원리 정도만 알면 되지 않나 싶다.


참고 : https://goddaehee.tistory.com/185

DLL 함수 만들기(Export)

프로젝트 만들기


헤더 만들기

dll 외부 어플리케이션에서 사용할 함수는 __declspec(dllexport) 키워드를 써서 정의한다.


기능 정의하기

빌드하기(DLL 파일 생성)



외부 DLL 함수 사용하기(Import) 


DLL 링크 시점

Load-time dynamic linking (묵시적 링크, implicit link)

- 실행파일에 사용할 DLL과 DLL내 함수 정보를 포함.
- 운영체제는 프로그램 실행시 DLL을 로드 
- 개발할 때 심볼 정보를 가지고 있는 헤더 및 라이브러리 파일이 필요

장점

- 어플리케이션에서 (외부 라이브러리로부터)exported된 DLL 함수를 쓸 때, 마치 로컬 함수를 쓰듯이 명시적으로 호출할 수 있다.
- 컴파일 및 링크 단계에서 가져올 헤더와 라이브러리 파일을 알고있어야 한다.

Run-time dynamic linking (명시적 링크, explicit link)

- 실행파일에는 DLL 관련 정보 없음 (DLL 심볼 정보 등은 없음)
- 프로그램이 실행중(run-time)인 상태에서 윈도우 운영체제에 포함된 API를 사용해서 DLL 있는지 검사하고 로드
- 어플리케이션이 LoadLibrary 함수 또는 LoadLibraryEx 함수를 호출해서 DLL을 런타임중에 로드한다.
- 로딩이 성공하면 GetProcAddress 함수를 통해서 exported된 DLL 함수 주소를 받아올 수 있다.

장점

- 헤더(.h), 라이브러리(.LIB) 파일을 import 할 필요가 없다.
- 어플리케이션에서 로직에 따라 필요한 모듈만 로드할 수 있다. (다국어 버전 프로그램 개발 등)
- 묵시적 링크 대비, 프로그램 실행시 DLL 로드하지 않으므로 메모리 절약 가능

단점

- 개발하는 어플리케이션은 DLL 함수명, 매개변수 정보를 모르기 때문에 개발하는 과정에서 별도 작업을 해줘야하는 귀찮음이 있다. => 일반적 함수 쓰듯이 프로그래밍할 수 없다.

참고 : https://learn.microsoft.com/ko-kr/cpp/build/linking-an-executable-to-a-dll?view=msvc-170

DLL의 진입점 코딩(DllMain)

entry point 함수는 DllMain으로, 프로세스나 스레드가 해당 DLL과 연결/해제시entry point 함수를 호출한다.

entry point 함수가 false를 반환하면 load-time dll인 경우 어플리케이션 실행이 안되거나, run-time dll 인 경우 해당 dll을 불러올 수 없다.

사용 용도

- entry point 함수는 DLL에 필요한 자료를 생성하거나 파괴하는데 쓸 수 있다.
- 멀티스레드 어플리케이션을 만드는 경우 entry point 함수에서 독립적으로 할당할 수 있는 스레드 로컬 스토리지(TLS)를 사용할 수 있다.
- entry point 함수엔 가벼운 초기화 정도만 하고, 다른 dll을 불러오거나 종료하는 함수를 쓰지 않는것을 권장한다.

출처 : https://learn.microsoft.com/en-us/troubleshoot/windows-client/deployment/dynamic-link-library

출처 : https://learn.microsoft.com/en-us/cpp/build/walkthrough-creating-and-using-a-dynamic-link-library-cpp?view=msvc-160


궁금증 : __declspec() 

마이크로소프트 관련 스토리지 클래스 특성을 같이 저장하도록 지정한다.

 - align : 인스턴스 할당시 메모리 정렬 정밀 제어. 8바이트 할당될걸 16바이트 할당되도록 강제한다던지..

 - dllimport : 인스턴스가 dll 인스턴스임을 명시

 등이 있음.




궁금증 : .LIB 파일

lib 확장자 파일은 두 가지 정도로 사용되는데
첫 번째는 위에서 설명한 static library 를 위한 실행가능한 코드(오브젝트 파일,기계어,바이너리코드 + 데이터) 이고,
두 번째는 링커(linker)가 실행파일을 만들 때 DLL과 링크하기 위한 심볼 정보를 가진 파일이다.

위에서 개발할 때 implicit link 해주기 위해 추가한 .LIB 는 두 번째(링커를 위한 심볼 정보)에 해당한다.

DLL 디버깅(VISUAL STUDIO)

1. DLL Export는 자체적으로 디버깅이 불가능

2. Import시 실행버튼을 누르면 메모리 오류가 발생 -> 계속 진행하기 선택하면 된다.

참조 : https://learn.microsoft.com/en-us/answers/questions/844356/vs2019-problem-in-debugging-of-a-dll

댓글