일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 깃
- jupyter lab
- c# 윈폼
- HTML
- c# 추상 클래스
- git
- 유니티
- Python
- 포인터
- docker
- 플러터
- Flutter
- jupyter
- Houdini
- C언어 포인터
- Unity
- c#
- 구조체
- C# delegate
- c언어
- Data Structure
- gitlab
- 도커
- 다트 언어
- vim
- Algorithm
- C++
- dart 언어
- c# winform
- github
- Today
- Total
nomad-programmer
[Programming/C] 동적 라이브러리 (Dynamic Library) 본문
동적 라이브러리를 사용하면 실행 시에 코드를 바꾸기 쉽다. 프로그램을 다시 컴파일할 필요 없이 애플리케이션을 갱신할 수 있다.만약 여러 프로그램이 똑같은 코드를 공유하고 있다면 모든 프로그램을 한꺼번에 모두 갱신할 수 있다.
정적 라이브러리와 동적 라이브러리 중 어떤 것이 더 좋은가?
상황에 따라 다르다. 정적 라이브러리는 컴퓨터 간에 이동하기 쉽게 더 작고 빠른 실행 파일을 만든다.
동적 라이브러리를 사용하면 실행 시에 프로그램의 환경을 더 많이 바꿀 수 있다.
오브젝트 파일 혹은 정적 라이브러리 파일을 링크하고 빌드하면 정적 프로그램이 된다.
별개로 있던 여러 오브젝트 코드로 하나의 실행 파일을 만들고나면, 프로그램을 새로 빌드하지 않고서는 들어간 코드를 바꿀 방법이 없다는 이야기다.
프로그램은 그저 오브젝트 코드를 뭉쳐놓은 커다란 덩어리다. 코드와 코드를 분리할 방법이 없다. 일단 섞이면 어디에 있는지 알 수 없다.
실행 파일에 들어 있는 오브젝트 코드를 다른 코드로 바꿀 수 없는 이유는 모든 코드가 파일 하나에 들어 있기 때문이다. 프로그램을 컴파일할 때 오브젝트 코드가 정적으로 링크되어 있다.
그런데 프로그램이 그저 한 파일로 되어 있지 않고 실행될 때에 비로소 연결되는 별도의 파일들로 구성할 수 있다. 그리고 그 연결되는 별도의 파일들이 바로 "동적 라이브러리(Dynamic Library)" 이다.
오브젝트 코드를 별도의 파일에 저장하고 프로그램이 실행될 때 이 코드들을 동적으로 연결하는 방법에 대해 알아보자.
먼저 오브젝트 파일을 만든다. calories.c 코드를 동적 라이브러리로 만들려면 다음과 같이 먼저 이 코드를 컴파일해 *.o 오브젝트 파일로 만들어야 한다.
gcc -c -fPIC -I<헤더 디렉토리> calories.c -o calories.o
/*
-c 옵션은 링크하지 말라는 의미이다.
-fPIC 옵션은 gcc에 위치 독립 코드를 만들라는 명령의 의미이다.
*/
-fPIC 플래그 옵션:
위치 독립 코드(Position-Independent Code)를 만들라는 명령이다. 운영체제와 프로세서에 따라 실행 시 로드할 메모리 위치를 결정하려면, 라이브러리를 위치 독립적인 코드로 만들어야 한다.
그러나 사실 대부분의 시스템에서는 이 옵션을 지정할 필요가 없다.
위치 독립 코드는 메모리 어디에든 로드될 수 있다.
위치 독립 코드(Position Independent Code) 란?
위치 독립 코드는 메모리의 어느 위치에 로드되더라도 제대로 실행될 수 있는 코드를 말한다. 예를 들어 라이브러리가 로드된 위치에서 500바이트 떨어진 전역 데이터에서 어떤 값을 찾는 동적 라이브러리가 있다고 가정하겠다. 이러면 운영체제가 메모리의 다른 곳에 라이브러리를 로드하면 문제가 발생한다. 위치 독립 코드를 만들라고 컴파일러에 명령하면 이런 문제가 생기지 않는다.
윈도우와 같은 운영체제는 동적 라이브러리를 로드할 때 메모리 매핑(Memory Mapping)이라는 기법을 사용한다. 이 기법을 사용하면 사실 모든 코드가 위치 독립적인 성격을 갖게 된다. 윈도우에서 코드를 컴파일하면 gcc는 -fPIC 옵션이 필요치 않다고 경고 메시지를 출력한다.
동적 라이브러리 이름은 플랫폼에 따라 달라진다.
대부분의 운영체제에서 동적 라이브러리를 사용할 수 있으며 작동하는 방식도 거의 똑같다. 그런데 동적 라이브러리는 플랫폼에 따라 상당히 다른 이름으로 불린다.
윈도우에서는 동적 라이브러리를 "동적 링크 라이브러리(Dynamic Link Library)" 라고 하며 *.dll 확장자를 가진다.
리눅스와 유닉스에서는 "공유 오브젝트 파일(Shared Obejct File)" 이라고 하며 *.so 확장자를 가진다.
맥에서는 "동적 라이브러리(Dynamic Library)" 라고 하며 *.dylib 확장자를 가진다.
그러나 파일의 확장자가 다르더라도 동적 라이브러리를 만드는 방법은 매우 비슷하다.
// 리눅스
gcc -shared calories.o -o libcalories.so
// 윈도우
gcc -shared calories.o -o calories.dll
// 맥
gcc -shared calories.o -o libcalories.dylib
/*
-shared 옵션은 *.o 오브젝트 파일을 동적 라이브러리로 변환하라는 옵션이다.
*/
-shared 옵션:
shared 옵션은 *.o 오브젝트 파일을 동적 라이브러리로 변환하라고 gcc에 명령을 한다.
컴파일러가 동적 라이브러리를 만들 때 라이브러리 이름을 파일 안에 저장한다. 따라서 리눅스에서 libcalories.so 라는 라이브러리를 만들면 libcalories.so 파일은 자신의 라이브러리 이름이 calories 라는 걸 기억한다.
이것이 중요한 이유는 만약 어떤 이름으로 라이브러리를 컴파일하면 나중에 파일 이름만 바꿔도 라이브러리 이름이 바뀌지 않음을 의마하기 때문이다.
라이브러리 이름을 바꾸려면 새로운 이름으로 다시 컴파일해야 한다.
프로그램 컴파일
gcc main.c -I<header 디렉토리 경로> -L<라이브러리 디렉토리 경로> -l<파일 이름> -o main.out
정적 라이브러리를 만들때와 똑같은 명령을 사용한다. 하지만 컴파일러가 작동하는 방법이 다르다.
동적 라이브러리이므로 컴파일러는 실행 파일 안에 라이브러리 코드를 포함시키지 않는다. 대신 컴파일러는 실행 파일에 라이브러리를 연결하기 위한 코드만 포함시키고, 실행 시에 라이브러리에 연결한다.
프로그램 실행
Linux & Unix 계열
리눅스와 유닉스 계열에서는 컴파일러가 libcalories.so 라이브러리 파일 이름만 기록하고, 경로명을 기록하지 않는다. 따라서 라이브러리가 표준 라이브러리 디렉토리에 있지 않으면, 프로그램이 calories 라이브러리를 찾을 수 없다.
이 문제를 해결하려면 LD_LIBRARY_PATH 변수에 추가로 검색할 디렉토리를 지정하면 된다. 라이브러리 경로를 export 명령으로 LD_LIBRARY_PATH 환경 변수에 추가하면 main.out 프로그램은 libcalories.so 파일을 찾을 수 있다.
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/scii/libs
// 라이브러리가 /usr/lib 와 같은 표준 디렉토리에 존재하면 설정할 필요 없다.
Windows
윈도우는 라이브러리를 찾기 위해 LD_LIBRARY_PATH 환경 변수를 사용하지 않는다.
윈도우는 현재 디렉토리를 검색하고, 현재 디렉토리에서 찾을 수 없다면 PATH 환경 변수에 저장된 디렉토리들을 검색한다.
// 윈도우 시스템에서 MinGW를 사용하는 경우
PATH="%PATH%:c:\libs"
Mac
맥 컴퓨터에서는 그저 프로그램을 실행하면 된다. 맥에서 프로그램을 컴파일할 때 라이브러리에 대한 경로명 /libs/libcalories.dylib가 실행 파일 안에 저장되기 때문이다.
그래서 프로그램이 실행될 때 어디에서 라이브러리를 찾아야 할지 알고 있다.
동적 라이브러리를 사용하는 대부분의 프로그램은 라이브러리를 표준 디렉토리에 저장한다.
리눅스와 맥에서는 동적 라이브러리를 /usr/lib나 /usr/local/lib와 같은 표준 디렉토리에,
윈도우에서는 DLL 파일을 실행 파일과 같은 디렉토리에 놓는다.
정리
* 동적 라이브러리는 실행 시에 프로그램에 링크된다.
* 동적 라이브러리는 하나 이상의 오브젝트 파일로부터 만들어진다.
* 어떤 컴퓨터에서는 -fPIC 옵션으로 컴파일해야 한다.
* -fPIC 옵션은 오브젝트 코드를 위치 독립적으로 만든다.
* 많은 시스템에서는 -fPIC 옵션을 주지 않아도 된다.
* -shared 옵션은 동적 라이브러리를 만든다.
* 시스템에 따라 동적 라이브러리 이름이 달라진다.
* 동적 라이브러리를 표준 디렉토리에 저장하면 여러 일을 더 간단히 할 수 있다.
* 동적 라이브러리가 표준 디렉토리에 없을 때에는 LD_LIBRARY_PATH 환경 변수를 설정해야 한다.
사용한 소스 코드
// main.c
#include <stdio.h>
#include "calories.h"
int main(void) {
display_calories(115.2, 11.3, 0.79);
return 0;
}
// calories.h
#ifndef __CALORIES_H__
#define __CALORIES_H__
void display_calories(float weight, float distance, float coeff);
#endif
// calories.c (미국 기준)
#include <stdio.h>
#include "calories.h"
void display_calories(float weight, float distance, float coeff){
printf("몸무게: %3.2f파운드\n", weight);
printf("거리: %3.2f마일\n", distance);
printf("칼로리 소비: %4.2fCal\n", coeff * weight * distance);
}
// calories.c (우리나라 기준)
#include <stdio.h>
#include "calories.h"
void display_calories(float weight, float distance, float coeff){
printf("몸무게: %3.2f킬로그램\n", weight / 2.2046);
printf("거리: %3.2f킬로미터\n", distance * 1.609344);
printf("칼로리 소비: %4.2fCal\n", coeff * weight * distance);
}
'Programming > C' 카테고리의 다른 글
[Programming/C] errno.h 헤더 파일 (0) | 2020.06.19 |
---|---|
[Programming/C] exec 함수 사용 (0) | 2020.06.19 |
[Programming/C] 정적 라이브러리 (Static Library) (0) | 2020.06.18 |
[Programming/C] 가변 인자 함수 (0) | 2020.06.18 |
[Programming/C] 구조체와 열거형 그리고 함수 포인터 (0) | 2020.06.18 |