C++ 정적, 동적, Header-only 라이브러리

규규·2024년 2월 10일
0

C++

목록 보기
6/21
post-thumbnail

라이브러리의 정의

  • 라이브러리는 프로그래밍에 사용할 수 있게 미리 만들어져 있는 함수나 변수들의 묶음이다. 미리 컴파일된 오프젝트 파일 형태로 존재하며 컴파일 과정(링킹 과정)에서 링킹되어 실행 가능한 프로그램을 이룬다.

라이브러리 장점

  1. 코드를 재사용하기 쉽다.
  2. 코드의 내용을 숨겨 기술 유출을 방지할 수 있다.
  3. 이미 구현되어 있는 기능들을 가져다 쓸 수 있어 개발 시간을 단축할 수 있다.
  4. 컴파일 시간을 단축할 수 있다. (라이브러리는 미리 컴파일되어 있어 링킹만 하면 바로 사용 가능하다)

라이브러리 사용 시 헤더 파일이 필요한 이유

  • 라이브러리를 사용하기 위해서는 #include 지시문을 통해 라이브러리 헤더 파일을 삽입하는 과정이 필요하다. 헤더 파일이 필요한 이유는 링커가 최종 실행 파일을 생성하기 위해 모든 함수의 심볼을 필요로 하는데 헤더 파일이 심볼을 생성하는 역할을 하기 때문이다. 즉, 라이브러리를 사용해 실행 파일을 만들기 위해서는 헤더 파일이 꼭 필요하다.
    *심볼 테이블이 뭔지 햇갈리면 다음 링크에서 링킹 내용 다시 확인 : https://bradbury.tistory.com/226

라이브러리 종류

  • 정적 라이브러리, 동적 라이브러리 (=공유 라이브 러리)

정적 라이브러리

정의

  • 정적 링킹 (Static Linking) 과정에서 링커가 프로그램에 필요로 하는 부분을 라이브러리에서 찾아 실행 파일에 복사하는 방식
  • 확장자 : 윈도우 .lib, 리눅스 .a (*.lib 라고 무조건 정적 라이브러리는 아님!)

장점

  • 실행 파일이 정적 라이브러리를 복사해서 가지고 있으므로, 실행 할 때 라이브러리가 필요 없음. 즉, 실행 파일만 있으면 이식성이 좋고 안정적.

단점

  1. 실행 파일이 라이브러리 내용을 복사해서 가지고 있으므로 라이브러리에서 수정할 부분이 있으면 파일 전체를 다시 컴파일하여 재배포해야 한다.
  2. 실행 파일이 라이브러리를 가지고 있는 만큼 실행 파일 크기가 커진다.
  3. 같은 라이브러리를 가진 여러 프로그램이 동시에 실행될 경우 코드가 중복되어 메모리 자원을 낭비한다.
  4. 정적 라이브러리 전체를 링킹하면서 사용하지 않는 함수들까지 전부 다 프로그램에 포함한다.

실제 예제

g++ -c func1.c
g++ -c func2.c
ar rcs libmylibrary.a file1.o file2.o
g++ main.c -L. -lmylibrary -o myprogram
  • -L [path]: 해당 path 의 .so, .a 파일을 라이브러리로 사용.
  • -l[라이브러리 이름] : 해당 이름의 파일을 라이브러리로 사용. 실제 라이브러리 파일 이름은 lib이름.so, lib이름.a 형태를 가짐.
  • 배포 할 때는 .a 와 .a 에서 사용 하는 헤더 파일을 같이 배포해서, 헤더 파일을 포함 시킨 다음 라이브러리 내용 사용하면 됨.

동적 라이브러리

정의

  • 동적 링킹 (Dynamic Linking) 과정에서 링커가 라이브러리 내용을 복사하지 않고 해당 내용의 주소만 가지고 있다가 런타임에 해당 주소로 가서 필요한 내용을 가져오는 방식
  • 확장자 : 윈도우 - .dll / 리눅스 - .so

장점

  • 정적 라이브러리를 사용하는 것보다 실행 파일 크기가 작아지고 여러 프로그램이 동적 라이브러리를 메모리에 올려놓고 공유해서 사용하기 때문에 메모리 자원을 효율적으로 사용할 수 있다.
  • 라이브러리에서 수정할 부분이 있으면 실행 파일을 새로 컴파일할 필요 없이 동적 라이브러리만 다시 컴파일하여 재배포할 수 있다.

단점

  • 동적 라이브러리를 사용하는 실행 파일은 실행할 때 동적 라이브러리가 필요하다. 만약 동적 라이브러리가 제대로 링크되어 있지 않거나 버전이 맞지 않는 등의 문제가 있으면 실행이 안 될 수 있다. 즉, 외부 의존도가 생기며 이식성이 낮은 단점이 있다.
  • 또한, 동적 라이브러리를 사용할 경우 매번 프로그램 영역에서 라이브러리가 저장된 주소로 이동하는 과정이 필요하여 약간의 성능 감소가 있을 수 있다.

실제 예제

// mylibrarry.cpp
#include "mylibrary.h"

// 간단한 함수 구현
extern "C" {
    int add(int a, int b) {
        return a + b;
    }
}
$g++ -shared -o libmylibrary.so mylibrary.cpp
// main.cpp
#include <iostream>
#include "mylibrary.h"

int main() {
    int result = add(3, 4);
    std::cout << "3 + 4 = " << result << std::endl;
    return 0;
}
$ g++ -o main main.cpp -L. -lmylibrary

추가로 알 만한 것

리눅스에서 다이나믹 라이브러리

dynamic loader

  • 공유 라이브러리와 연결된 프로그램 실행하면 내부적으로 dynamic loader 가 먼저 동작해서 다음 작업 수행 (Linux 에서는 ld.so, ld-linux.so)
    1. dynamic link 된 공유 라이브러리를 찾아서 메모리에 로딩
    2. entry function (ex: main.c) 찾아서 호출
    3. 프로그램 실행
  • Loader 는 고유 환경 변수 (리눅스의 경우 LD_LIBRARY_PATH 를 찾아서 활용)
  • 리눅스에서 ELF 형식의 바이너리는 rpath 라고 부르는 "실행 시 라이브러리를 찾을 경로 정보" 를 컴파일 시점에 넣어 줄 수 있음. ex: gcc -Wl, -rpath /usr/local/lib hello.c

*.so 파일 찾는 순서

  1. LD_LIBRARY_PATH 환경 변수
  2. System default 경로 (/etc/ld.so.conf 파일에 설정되어 있음)
  3. /lib
  4. /usr/lib
  5. binary code 에 hard-coding 된 경우

공유 라이브러리 의존성 확인

  • ldd 명령 활용 ex: ldd /usr/bin/vim

시스템 동적 라이브러리 위치 경로

  • /lib, /lib64 : cp,mv 와 같은 시스템 기본 명령어
  • /usr/lib, /usr/lib64 : 응용 프로그램 관련 라이브러리

    출처 :
    https://csj000714.tistory.com/491

DLL 의 분류

  • 암시적 링킹 (Implicit Linking), 명시적 링킹 (Explicit Linking)
  • 암시적 링킹
    • 암시적 링킹은 실행 파일 자체에 어떤 *dll의 어떤 함수를 호출하겠다는 정보를 포함시키고 프로그램 실행 시 해당 함수들을 초기화한 후 이용하는 방식이다.
    • 프로그램을 시작하면서 해당 dll을 로드하며 dll에 정의된 함수를 마치 자신의 함수처럼 호출할 수 있다.
    • 실행 파일에 어떤 함수를 사용하겠다는 정보를 포함하기 위해서는 lib 파일이 필요하다. 이때, lib 파일은 정적 라이브러리가 아니라 암시적 링킹을 위해 필요한 심볼이 들어있는 *.lib 파일로 동적 링킹 과정에 꼭 필요하다.
    • 코드가 간결하여 사용하기 쉽다는 장점이 있다. 프로그램이 실행될 때 *dll 파일이 로드되므로 실행 시 연결이라고 표현한다.
  • 명시적 링킹
    • 명시적 링킹은 프로그램이 실행 중일 때 *.dll 파일이 있는지 검사하고 동적으로 원하는 함수만 호출하는 방법이다. 정확하게는 호출할 함수의 포인터를 얻어 함수를 호출하는 방법이라 할 수 있다.
    • 링킹 과정에서 .dll의 함수 정보가 필요하지 않기 때문에 .lib 파일이 필요 없다.
    • 직접 코드를 통해 원하는 함수만 불러와서 사용하는 방법으로 사용하지 않는 함수들까지 전부 다 프로그램에 포함할 필요가 없어 자원을 아낄 수 있다.
    • 프로그램이 실행 중에 *.dll 파일이 메모리에 읽히므로 실행 중 연결이라고 표현한다.

출처 : 
https://bradbury.tistory.com/224
https://www.lesstif.com/software-architect/shared-library-linker-loader-12943542.html

Header-only 라이브러리

  • c++ 배포 할 때는 일반적으로 구현 세부 사항은 숨기고, 헤더 파일과 라이브러리 (dll, lib, so, a, dylib..) 를 배포하는데, 여러 운영체제 및 운영체제 버전에 맞게 모두 지원하도록 각각 컴파일 해야 함.
  • 그냥 cpp 파일을 배포한다면 사용자가 직접 경로 설정 및 빌드를 해줘야 함
  • header-only 라이브러리 장점
    • 라이브러리가 template 코드 일 때, 유일한 배포 방법.
    • User 가 추가적인 빌드 절차가 필요 없음.
    • 멀티 플랫폼 지원이 좋음.
    • 단, 사용자가 라이브러리를 쓰기에는 편하지만, 개발자는 여러가지 불편함을 가지고 코딩해야 함
  • header-only 라이브러리 단점

CMake 에서의 정적,동적 라이브러리 활용

정적 라이브러리 생성 할 때

# 정적 라이브러리 생성
add_library(my_static_lib STATIC ${SOURCE_FILES})

정적 라이브러리 사용 할 때

일반적인 정적 라이브러리 구조

mylib/
├── include/
│ └── mylib/
│ └── mylib.h
├── lib/
│ └── libmylib.a
└── CMakeLists.txt

내 프로젝트의 CMakeLists.txt

# 프로젝트 이름과 최소 CMake 버전을 설정합니다.
cmake_minimum_required(VERSION 3.10)
project(MyApp)

# include 디렉토리를 설정합니다.
include_directories(${CMAKE_SOURCE_DIR}/mylib/include)

# 라이브러리 디렉토리를 설정합니다.
link_directories(${CMAKE_SOURCE_DIR}/mylib/lib)

# 실행 파일을 만듭니다.
add_executable(MyApp main.cpp)

# 라이브러리를 링크합니다.
target_link_libraries(MyApp mylib)

동적 라이브러리 생성 할 때

# 동적 라이브러리 생성
add_library(my_dynamic_lib SHARED ${SOURCE_FILES})

동적 라이브러리 사용 할 때

일반적인 동적 라이브러리 구조

/path/to/library_root/
├── include/
│ ├── mylib.h
│ └── mylib_utils.h
├── lib/
│ ├── libmylib.so # Unix-like systems (e.g., Linux)
│ ├── libmylib.dylib # macOS
│ └── mylib.dll # Windows
├── bin/ # Optional, for executables and DLLs on Windows
│ └── mylib.dll # Sometimes DLLs are placed in the bin directory on Windows
└── CMake/
└── MyLibConfig.cmake # Optional, for CMake package configuration

내 프로젝트의 CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(MyApp)

# include 디렉토리 지정
include_directories(/path/to/library_root/include)

# 라이브러리 디렉토리 지정
link_directories(/path/to/library_root/lib)

# main.c 컴파일하여 실행 파일 생성
add_executable(MyApp main.c)

# 필요한 라이브러리를 링크
target_link_libraries(MyApp mylib)
  • find_package() 사용하는 경우
  • MyLibConfig.cmake : CMake 를 사용하여 패키지를 찾고 설정하기 위한 구성 파일. 라이브러리 위치, 필요 헤더 파일 경로, 링크해야 할 라이브러리 등 을 지정해서 다른 프로젝트가 쉽게 라이브러리를 사용 할 수 있도록 함.
cmake_minimum_required(VERSION 3.10)
project(MyApp)

# 패키지 찾기
find_package(MyLib REQUIRED PATHS /path/to/library_root/CMake)

# include 디렉토리 및 라이브러리 디렉토리 자동 설정
include_directories(${MyLib_INCLUDE_DIRS})
link_directories(${MyLib_LIBRARY_DIRS})

# main.c 컴파일하여 실행 파일 생성
add_executable(MyApp main.c)

# 필요한 라이브러리를 링크
target_link_libraries(MyApp ${MyLib_LIBRARIES})
profile
복습용 저장소

0개의 댓글