반응형

UNIX/LINUX에서 shared library 또는 shared object를 compile시가 아닌 runtime시에 동적으로 메모리에 loading하여 함수를 실행하기 위해서는 dlopen(3), dlsym(3), dlclose(3), dlerror(3)를 이용하여 수행할 수 있습니다.

 

이들은 library call 함수로 아래와 같은 prototype을 가집니다.

#include <dlfcn.h>

void *dlopen (const char *filename, int flag);
const char *dlerror(void);
void *dlsym(void *handle, const char *symbol);
int dlclose (void *handle);

 

 

dlopen(3)함수는 정상적으로 호출될 때마다 shared lib에 대한 참조 count를 1증가 시킵니다. 참조 count가 0에서 1로 참조 count가 증가할 때에 memory loading합니다(option에 따라서 dlsym(3)에서 loading함). shared library는 여러 프로세스에서 사용하더라도 OS는 한번만 memory에 loading합니다. text(실행코드)영역을 여러 프로세스가 공유하지만 전역 변수영역은 공유하지 않고 프로세스마다 별도의 영역을 차지합니다. 

dlopen(3)에 대한 자세한 내용은 (dlopen(3) - dynamic library open)참조바랍니다.

 

 

Sample

void *handle = NULL;

handle = dlopen("libmyutil.so", RTLD_LAZY);

if(handle == NULL) 
{
    fprintf(stderr, "%s\n", dlerror());
    return -1;
}

 


 

dlsym(3) 함수는 shared library에 포함된 symbol(함수 또는 전역변수)의 위치에 대한 pointer를 얻습니다. (쉽게 말하면 함수 pointer를 얻습니다.) dlsym(3)의 자세한 사용법은 ( dlsym(3) - loading된 shared library에서 symbol 찾기 )을 참조바랍니다.

만약, shared library에 있는 함수를 호출해야 한다면 함수 Pointer 변수를 선언해야 합니다. 우리가 C언어를 배우면서 가장 어려워하는 것이 pointer입니다만, 더구나 함수 pointer 변수를 선언해야 한다니까 앞이 캄캄할 수 있습니다. 그러나 별로 어렵지 않습니다. 

shared library에서 만들어진 함수의 prototype이 char *util_strcat(char *dest, const char *src);이면 함수 Pointer 변수를 선언하는 방법은 아래와 같습니다.

 

함수포인터 변수 선언하는 방법

함수 Pointer 변수명을 ptr_strcat이라고 정하였다면

1. 사용할 함수의 prototype에서 함수명만 함수 Pointer 변수명으로 변경합니다.
    char *util_strcat(char *dest, const char *src);
    ->
    char *ptr_strcat(char *dest, const char *src);

2. 함수명을 괄호로 묶은 후에 함수명 앞에 *를 붙여줍니다. 
    char *(*ptr_strcat)(char *dest, const char *src); 


3. 파라미터의 가변수 명을 없앱니다.
    char *(*ptr_strcat)(char *, const char *); 

4. NULL로 초기화까지 하려면 = NULL 해주면 됩니다.
    char *(*ptr_strcat)(char *, const char *) = NULL; 

이렇게 하면 ptr_strcat은 함수 pointer 변수가 됩니다.

 

실제 사용예는 아래와 같습니다.

 

Sample

    char *(*ptr_strcat)(char *, const char *) = NULL; 

    ...

    ptr_strcat = dlsym(handle, "util_strcat");

    ...

    ptr_strcat(dest, src);

위와 같이 ptr_strcat은 함수 pointer이므로 이후부터는 일반 함수처럼 똑같이 함수를 호출하듯이 사용합니다. 함수 포인터와 함수의 차이는 함수 포인터는 변수이므로 타입이 같은 다른 함수를 대입할 수 있고 함수는 상수이므로 함수명에 다른 함수를 대입할 수 없다는 것만 다릅니다.

 

dlsym(3)은 함수가 아닌 symbol들도 읽을 수 있기 때문에 NULL이 return되었다고 해서 오류라고 할 수 없습니다. 다만, 함수를 얻고자 했을 경우에는 NULL이면 오류입니다. 그렇지만, 일반적으로는 dlsym(3) 호출후에 dlerror(3)의 return 값이 NULL이 아니면 오류라고 인식하면 됩니다. dlerror(3)는 두번 연속 호출하면 두 번째는 NULL이 return되므로 첫번째 호출할 때에 다른 변수에 저장해야 합니다.

 

Sample

    char *error;

    ......
    
    ptr_strcat = dlsym(handle, "util_strcat");

    if((error = dlerror()) != NULL) {
        printf("dlsym error: %s\n", error);
        dlclose(handle);
        return -1;
    }

 

 


 

dlclose(3)함수는 이제 더 이상 shared library를 사용하지 않게 되어 사용을 끝내려는 함수입니다. 이 함수가 호출되면 shared library에 대한 참조 count를 1감소시킵니다. OS는 전체 process에서 참조 count를 관리하는 데 참조 count가 0이면 memory에서 shared library를 내립니다.  dlclose(3)함수의 자세한 사용법은 dlclose(3) - dynamic library unload를 참고 바랍니다.

 

 


활용 예제

 

Sample

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

int main(int argc, char **argv) 
{
    char buf[1024], buf2[1024];
    char *error;
    char * (*ptr_strcat)(char *, size_t, const char *) = NULL;

    void *handle = dlopen ("libmyutil.so", RTLD_LAZY);

    if(handle == NULL) 
    {
        printf("%s\n", dlerror());
        return 1;
    }

    ptr_strcat = dlsym(handle, "util_strcat");
    if((error = dlerror()) != NULL) 
    {
        dlclose(handle);

        return 1;
    }

    strcpy(buf, "1234567890");
    strcpy(buf2, "ABCD");
    ptr_strcat(buf, sizeof(buf), buf2);

    printf("%s\n", buf);
    dlclose(handle);

    return 0;
}

dlopen ~ dlclose를 사용하려면 컴파일시 -ldl link option을 추가해야 합니다. 참고로 LINUX에서 shared library를 만들려면 compile된 여러개의 object 파일들을 하나의 shared library에 묶으려면

gcc -shared -fPIC -o libmyutil.so myutil.o myutil2.o 

하면됩니다.

 

 


see also :   Shared Library 관련 API

 

 

 

반응형
블로그 이미지

자연&사람

행복한 개발자 programmer since 1995.

,