반응형

gcc C++ : 유닉스 라이브러리 만들기 입문

알게된 배경

 리눅스 환경에서 처음에는 간단한 프로그램을 제작을 했지만, 점차 커기게 되자 상당히 많은 양의 파일들이 한폴더에 어지럽게 있자 더 이상 모듈화를 하지 않으면 코드관리가 안될만한 상황이 되어 라이브러리 작성법에 대해서 공부하게 되었다.


염두해야 될 것

 윈도우 개발환경과 달리 리눅스환경의 경우 gcc의 옵션을 활용하는 방법을 알아야 했다. 그리고 편하게 하려면, makefile을 활용해야 하고, 더 편하게 makefile을 만들기 위해서는 makefile을 어느정도 알고 있는 상태에서 cmake를 사용할 줄 알아야 한다.


테스트 샘플 코드

// ~/lib_test/include/sample.h
#pragma once
#inlcude <iostream>

class Sample
{
public:
    Sample(void);
    ~Sample(void);

    void print(void);
};
// ~/lib_test/src/sample.cpp
#include "../include/sample.h"

using namespace std;

Sample::Sample(void)
{}
Sample::~Sample(void)
{}
void Sample::print(void)
{
    cout << "hello snupy?!" << endl;
}
// ~/lib_test/main.cpp
#include "include/sample.h"

int main(int argc, char* argv[])
{
    Sample sam;
    sam.print();
    return 0;
}



정적 라이브러리

 파일 확장자는 a(Archives의 앞글자)이며, object(.o) 파일과 큰 차이는 없다. 정적라이브러리를 통해서 빌드된 프로그램에 정적라이브러리가 포함되기 때문에 프로그램의 용량이 커지는 특징이 있다. 라이브러리에 수정할 내용이 있어서 수정하게 되면, 프로그램을 다시 빌드해야 하는 단점이 있다.


만드는 방법

# ~/lib_test/ 에서 실행할 경우
g++ -c ./src/sample.cpp -o ./obj/sample.o
ar rc ./lib/libsample.a ./obj/sample.o

 첫 줄은 sample.cpp를 sample.o 로 빌드가 되며, 두번째 줄은 libsample.a로 정적 라이브러리로 만들어준다. ar의 명령어는 아카이브(Archives)의 앞의 두글자를 딴 것이다. 또한 리눅스에서는 파일이름앞에 lib를 붙여야 나중에 실행 파일을 빌드할 때 lib로 인식을 하니 붙여줘야 한다.

 만약 해당 코드에 C++11이상 같은 표준라이브러리을 사용한다면, 오브젝트(.o)파일로 빌드할 때 -std=c++11 같은 옵션을 추가해주어야 한다.


링크된 실행파일 만들기

# ~/lib_test/ 에서 실행할 경우
g++ -o test main.cpp -Llib -lsample

 위의 명령어는 main.cpp라는 소스코드를 test라는 이름으로 빌드를 하게 된다. 이때 lib라는 폴더에 있는 sample이라는 이름의 라이브러리를 참조하라는 명령이 된다. 앞에서 작성한 libsample.a 파일을 sample이라는 라이브러리로 인식을 하게 된다.

 정적 라이브러리는 빌드 이후에는 실행파일에 포함이 되기 때문에 빌드이후 sample.a가 없어도 실행이 된다.


공유 라이브러리

 윈도우의 dll(동적 라이브러리)와 비슷한 개념이다. 때문에 공유 라이브러리를 동적 라이브러리라 부르는 곳도 있다. 확장자는 .so(Share Object의 각 앞글자 머리) 공유 라이브러리의 가장 큰 특징은 정적 라이브러리와 달리 특정 공유 라이브러리 파일만 교체를 해도 수정된 내용이 적용이 된다. 즉, 실행파일을 재 빌드할 필요가 없다(물론 파일이 삭제되거나 추가되면 어쩔 수 없이 재컴파일해야 한다).

 이러한 특징으로 나온 개념이 플러그인 이다(라고 많은 책이나 참고자료가 언급한다).


만드는 방법

# ~/lib_test/ 에서 실행할 경우
g++ -fPIC -c ./src/sample.cpp -o ./obj/sample.o
g++ -shared -o ./lib/libsample.so ./obj/sample.o

 첫줄은 고정된 파일로 컴파일을 하여 오브젝트 파일로 빌드를 한다. 이후 두번째 명령에서는 공유 오브젝트 파일을 생성한다. 여기서는 간단히 확장자를 so로 작성했지만, 보통 프로그램 버전을 관리 할 경우 so.1.0.0 이런식으로 숫자를 뒤에 붙여서 관리를 한다. 또한 -Wl 옵션을 사용하여 추가 옵션을 붙이는 경우가 많다(입문자에게는 이러한 부분이 오히려 진입장벽을 높이는 결과가 되는 것 같다)


링크된 실행 파일 만들기

# ~/lib_test/ 에서 실행할 경우
g++ -o test main.cpp -Llib -lsample

 앞의 링크된 실행파일을 만드는 것과 큰 차이가 없다. 하지만, 이렇게 만들어진 실행파일은 바로 실행할 수 없는데, 이유는 정적 라이브러리와 달리 공유 라이브러리는 실행파일에 포함이 안되어 있기 때문에 실행에 필요한 파일을 알려줘야 한다. 여기서 -L옵션은 빌드할때만 사용되는 라이브러리 경로이다.


 다만, 윈도우즈의 동적 라이브러리인 dll의 경우 보통 실행파일이 있는 폴더와 하위 폴더, 그리고 환경변수에 등록된 폴더에서 실행에 필요한 dll파일이 있는지 찾아서 실행한다.


 반면 리눅스 환경에서는 그냥 환경변수만 찾아 본다(어떤이는 이를 버그라 생각을 한다). 따라서 환경변수에 있는 폴더 경로에 공유 라이브러리를 복사하는 방법과 환경변수를 만들어서 등록해줘야 된다.

 때문에 실행하기 전에 ldd 명령어를 통해 의존성 검사로 파일을 실행하는데 필요한 공유 라이브러리가 연결되어 있는지 확인 할 수 있다.

# ~/lib_test/ 에서 실행할 경우
ldd ./test

 이렇게 확인한 의존성 검사에서 "not found"가 없어지도록 해야 실행이 가능해진다.

 공유 라이브러리를 이용해 배포했을 경우 해당 프로그램을 유지 및 삭제 관리를 할 때 어떻게 할 것인지도 고민을 해야 한다.


환경변수 추가 등록

export LD_LIBRARY_PATH = [라이브러리절대경로]


 환경변수를 통해서 라이브러리 위치를 찾을 수 있다면, ldd로 검사했을 때 not found가 더 이상 보이지 않을 것이다. 실행했을 프로그램의 결과가 보이면 성공적으로 라이브러리를 생성하고 실행을 해본 것이다.


 하지만, LD_LIBRARY_PATH라는 환경변수는 디버그용(배포 전단계의 실행 테스트)에서 사용할 것을 권하지 배포하는 방법으로는 적합하지 않다.


보충해야 될 내용

 여기까지 혼자서 테스트 해보는데 성공했다면, gcc에 대한 옵션에 대해서 추가로 살펴봐야 한다. 당연한 소리지만, 테스트 결과 -l 옵션을 여러번 사용해서 여러개의 라이브러리를 참조하는 것이 가능하다. 또한 옵션을 사용하는 경우가 많으니 gcc 옵션에 대해서도 어느 정도 숙지를 하고 있어야 한다.


참고자료

우분투 환경에서 C언어로 배우는 리눅스 프로그래밍(서적)

옵션에 대한 정리(영문)

옵션에 대해 한글로 자주 사용하는 것만 정리(간혹 오역도 보인다)



반응형

+ Recent posts