앞선 글에서 공유 라이브러리를 소개하며 실행되기 직전에 동적 로더가 공유 라이브러리를 로드하고 링크하는 시나리오에 대해서 살펴보았다. 그러나 응용프로그램이 동적 링커에게 응용 프로그램이 돌고 있는 동안에 응용 프로그램을 컴파일 시에 라이브러리와 링크할 필요 없이 임의의 동적 라이브러리를 로드하고 링크할 것을 요청할 수도 있다.

  • 소프트웨어의 배포: MS의 응용 프로그램 개발자들은 소프트웨어 업데이트를 배포하기 위해 공유 라이브러리를 사용한다. 이들은 공유 라이브러리의 새로운 복사본을 생성하고, 사용자들은 이것을 다운로드하고 현재 벼전을 고체해서 사용할 수 있게 된다.다음번에 이들이 자신의 응용 프로그램을 실행할 때, 자동으로 새로운 공유 라이브러리와 링크하고 로드하게 된다.

  • 고성능 웹서버의 개발: 많은 웹 서버들은 개인화된 웹페이지들, 계좌 잔고, 광고 전단과 같은 동적 컨텐츠를 생성한다. 초기 웹 서버드른 자식 프로세스를 만들고 "CGI 프로그램"을 자식 프로세스의 컨텍스트에서 실행하기 위해 fork와 execve를 사용해서 동적 컨텍츠를 생성하였다. 그렇지만 현대의 고성능 웹 서버들은 동적 링킹에 기반한 보다 효율적이고 복잡한 방법을 사용하여 동적 컨텐츠를 생성할 수 있다. 
    아이디어는 공유 라이브러리에서 동적 컨텐츠를 생성하는 각 함수들을 패키징하는 것이다. 웹 브라우저가 요청을 받으면 fork와 execve를 사용해서 자식 프로세스를 로드하고 링크한 뒤에 직접 호출한다. 이 함수는 서버의 주소공간에 캐시된 상태로 남으며, 그래서 후속 요청들은 간단한 함수 호출 비용만으로 처리될 수 있다. 이것은 접속량이 많은 사이트의 처리량에 대해서 큰 영향을 줄 수 있다.


리눅스 시스템들은 응용 프로그램이 런타임에 공유 라이브러리들을 로드하고 링크하도록 하는 동적 링커로의 간단한 인터페이스를 제공한다.

#include <dlfcn.h>

void *dlopen(const char *filename, int flag);
				Returns: ptr to handle if OK, NULL on error

 

dlopen은 공유 라이브러리 파일 이름을 로드하고 링크한다. filename에 정의된 외부 심볼들은 이전에 RTLD_GLOBAL 플래그로 오픈된 라이브러리들을 사용해서 해석된다.

만일 현재 실행파일이 -rdynamic 플래그로 컴파일되었다면, 이 파일의 전역 심볼들 또한 심볼 해석을 위해서 사용 가능하다. flag 인자는 링커에게 외부 심볼들로의 참조를 즉시 해석하라고 하는 RTLD_NOW나 라이브러리로부터의 코드가 실행될 떄까지 심볼 참조를 연기하라고 링커에게 지시하는 RTLD_LAZY 플래그를 포함해야 한다.

#include <dlfcn.h>

void *dlsym(void *handle, char *symbol);
				Returs: ptr to symbol if OK, NULL on error

 

dlsym 함수는 이전에 오픈된 공유 라이브러리와 symbol 이름에 대한 handle을 받아서 만일 심볼이 존재하면 심볼의 주소를 리턴하고, 그렇지 않으면 NULL을 리턴한다.

#include <dlfcn.h>

int dlclose(void *handle);
				Returns: 0 if OK, -1 on error

 

dlclose 함수는 만일 여전히 사용하고 있는 다른 공유 라이브러리가 하나도 없다면 이 공유 라이브러리를 언로드(Unload)한다.

예시

 #include <stdio.h> 
 #include <stdlib.h> 
 #include <dlfcn.h>
int x[2] = {1, 2}; 
int y[2] = {3, 4}; 
int z[2];

int main()
{
    void *handle;
    void (*addvec)(int *, int *, int *, int); 
    char *error;
    
    /* Dynamically load the shared library that contains addvec() */
    handle = dlopen("./libvector.so", RTLD_LAZY); 
    if (!handle) {
        fprintf(stderr, "%s\n", dlerror());
        exit(1); 
    }
    
    /* Get a pointer to the addvec() function we just loaded */
    addvec = dlsym(handle, "addvec"); 
    if ((error = dlerror()) != NULL) { 
        fprintf(stderr, "%s\n", error);
        exit(1); 
     }
     
     /* Now we can call addvec() just like any other function */
     addvec(x, y, z, 2);
     printf("z = [%d %d]\n", z[0], z[1]);
     
     /* Unload the shared library */
     if (dlclose(handle) < 0) { 
         fprintf(stderr, "%s\n", dlerror());
         exit(1); 
      }
      return 0; 
}

Compile

gcc –L/usr/local/lib -I/usr/local/inc –o test test.c -lmylib

 

이때 -L 뒤에는 공유 라이브러리의 경로가, -I 뒤에는 Include 할 파일들의 경로가 제공된다.

마지막으로 -l 뒤에는 라이브러리의 이름이 오게 되는데, 앞서 제공된 공유 라이브러리 경로에서 해당 공유 라이브러리의 이름을 찾아서 활용한다.

실패 시에는 런타임 에러가 발생하게 된다.