반응형

dlopen(3)

#include <dlfcn.h>

void *dlopen(const char *filename, int flag);

C언어에서 프로그램을 개발할 때에 특정 기능을 사용하기 위하여 함수를 호출합니다. 이들 함수들을 사용하기 위해서 원본 소스가 있는 경우에는 .c 파일을 직접 컴파일을 하여 사용하거나 컴파일이 된 .o 파일 또는 여러 개의 .o 파일을 묶놓은 .a 파일 또는 .so 파일의 형태로 제공받아서 컴파일 후에 링크 시에 프로그램과 연동을 합니다.

 

.o나 .a 파일은 링크시에 프로그램에 복사되어 실행 파일을 크게 만들게 되며, 또한 여러 프로그램에서 사용하다가 라이브러리에 변경사항이 발생하였을 때에는 이 라이브러리를 사용한 모든 프로그램들을 재컴파일 해야 하는 불편함이 있습니다.

 

그러나 .so (shared object) 파일은 링크 시에 참조만하고 runtime시에 같이 loading되어 실행될 뿐만 아니라 같은 .so 파일을 사용하는 모든 프로그램에 대해서 OS는 1본의 .so만 loading하여 공유함으로써 전체 실행 메모리 사용을 줄입니다. 또한 .so 파일에 있는 함수의 로직이 변경하였을 때, .so 파일만 다시 컴파일하고 배포한 후에 이를 사용하는 프로그램은 재컴파일 없이 프로세스만 재기동하면 바로 적용이 됩니다. 따라서 요즘은 라이브러를 shared object (.so) 형태로 제공하는 것이 추세입니다. 만약 shared object 파일의 명이 libmylib.so 파일이면 링크 시에 -lmylib 옵션을 줌으로써 그 라이브러리에 있는 모든 함수를 사용할 수 있게 됩니다.

 

위의 모든 경우에 컴파일/링크 시에 어떤 라이브러리 속의 어떤 함수를 사용할 것인지 정해진 경우에 한해서 유효합니다. 그런데, 어떤 업무에서는 함수의 I/O(파라미터, return type)는 같은 데, 처리 로직이 너무나 다양하여 선택적으로 실행해야 하는 경우가 있습니다. 즉, 개발 시에는 모르고 실행 시에 어떤 값에 의해서 호출할 함수가 달라지는 경우도 있습니다. (예, 금융상품에 따라서 계산 로직이 다른고 지속적으로 새로운 상품이 추가되는 경우) 

실행하다가 특정 코드(예, 상품코드)에 의해서 분기가 일어나는 경우에, 필요한 시점에 필요한 라이브러리를 로딩하고 그 속에 포함된 함수를 실행하는 것이 효율적인 경우가 있습니다.

 

이와 같이 필요 시점에 shared object 파일을 메모리에 로딩하고 필요한 함수를 찾아서 실행하고 실행 후에 다시 메모리에 내리는 함수들이 dlopen(3) ~ dlsym(3) ~ dlclose(3)함수입니다.

 


 

dlopen(3)함수는 shared object 파일을 메모리에 로딩하거나 이미 메모리에 loading되어 있으면 shared object에 대한 참조 count를 1 증가 시키는 함수입니다. dlopen(3)함수로 호출되는 라이브러리는 일반 명명규칙(lib*.so)을 따르지 않아도 되고, LD_LIBRARY_PATH 환경변수에 디렉토리도 추가할 필요없습니다. 

 

링크시 -ldl 추가 필요

 

파라미터

filename
    - shared object 파일명에 대한 상대 path 또는 full path
    - LD_LIBRARY_PATH 환경 변수에 등록된 디렉토리이면 파일명만 사용해도 됩니다.
    - 만약, filename을 NULL을 입력하면, 프로그램 자기 자신에 포함된 라이브러리 포인트를 return합니다. 
flag
    - shared object 파일을 메모리에 로딩 방법 또는 시점에 대한 설정값

필수 선택 flag
   RTLD_LAZY : shared object에 포함된 함수가 최초 호출되는 시점에 loading합니다.
             shared object에 포함된 전역변수는 호출과 동시에 생성됩니다.
   RTLD_NOW  : dlopen()함수 호출과 동시에 loading합니다.
 
추가 option (bit or 연산으로 추가)
   RTLD_GLOBAL  : loading한 이 라이브러리를 다른 프로그램에서도 사용
   RTLD_LOCAL   : loading한 이 라이브러리는 이 프로그램에 한해서 사용 (default)
   RTLD_NODELETE: dlclose()시에도 메모리에서 내리지 않는다.
   RTLD_NOLOAD  : dlopen()해서 메모리에 로딩하지 않는다. (라이브러리의 존재 여부 및 오류 체크용)
   RTLD_DEEPBIND: 이 라이브러리에 포함된 함수, 변수 등이 
       다른 라이브러리에 포함된 같은 이름의 함수, 변수들을 무시하고 우선 순위를 갖는다는 의미

 

RETURN

NULL
    - 오류가 발생하여 shared library를 로딩하지 못하였습니다.

NULL 아님
    - 정상적으로 로딩하였습니다.
    - 만약 같은 라이브러리를 이미 loading한 상태이면 같은 값이 return됩니다.

 


활용 예제

 

Sample). cos()함수 호출 예제

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

int main(int argc, char **argv)
{
    void *handle;
    double (*cosine)(double);
    char *error;

    handle = dlopen("libm.so", RTLD_LAZY);
    if (!handle) {
        fprintf(stderr, "%s\n", dlerror());
        exit(EXIT_FAILURE);
    }

    dlerror();    /* Clear any existing error */
    /* Writing: cosine = (double (*)(double)) dlsym(handle, "cos");
       would seem more natural, but the C99 standard leaves
       casting from "void *" to a function pointer undefined.
       The assignment used below is the POSIX.1-2003 (Technical
       Corrigendum 1) workaround; see the Rationale for the
       POSIX specification of dlsym(). */

    *(void **) (&cosine) = dlsym(handle, "cos");

    if ((error = dlerror()) != NULL)  {
        fprintf(stderr, "%s\n", error);
        exit(EXIT_FAILURE);
    }

    printf("%f\n", (*cosine)(2.0));
    dlclose(handle);
    
    return 0; 
}

 


 

see also: Shared Library 관련 API

 

 

 

반응형
블로그 이미지

자연&사람

행복한 개발자 programmer since 1995.

,