IBM®
메인 컨텐츠로 가기
    Korea [국가변경]    이용약관
 
 
   
        제품    서비스 & 솔루션    고객지원 & 다운로드    회원 서비스    
메인 컨텐츠로 가기

한국 developerWorks  >  리눅스  >

Linux 애플리케이션을 위한 DLL 작성하기

plugin 작성으로 전체 애플리케이션 구현 효과 얻기

developerWorks
문서 옵션

JavaScript가 필요한 문서 옵션은 디스플레이되지 않습니다.


난이도 : 초급

Allen Wilson, e-business architect , IBM

2001 년 10 월 01 일
2004 년 2 월 20 일 수정

Plugin과 DLL은 전체 애플리케이션을 새로 작성하지 않고 기능을 추가 할 수 있는 좋은 방법이다. Linux에서 plugin과 DLL은 동적 라이브러리로서 구현된다. e-business 컨설턴트이자 아키텍트인 Allen Wilson은 동적 라이브러리를 소개하고 그것들을 사용하여 애플리케이션이 실행된 후 애플리케이션을 변경하는 방법을 설명한다.

plugin은 인터넷 사용자들에게 친숙할 것이다. plugin은 웹에서 다운로드를 받아, 오디오, 비디오, 또는 브라우저 내의 특별한 효과를 지원한다. 일반적으로 plugin은 기존 애플리케이션을 변경하지 않고 기존 애플리케이션에 새로운 함수들을 제공한다.

DLL은 프로그램 함수이다. 애플리케이션의 주요 프로그램은 dll이 디스크상에서 주요 애플리케이션과 분리된 파일에 존재하고 있으면서 런타임시 요청된 dll을 선택적으로 로딩하는 프레임웍이나 backplan과 함께 구현된다. 이러한 패키징(packaging)과 동적 로딩으로 인해 업그레이드, 유지관리, 라이센싱 전략이 유연성을 갖추게 된다.

Linux에는 적어도 libc 라이브러리 함수에 필요한 수 천개의 명령어와 애플리케이션이 있다. libc 함수가 모든 프로그램과 함께 패키징된다면 수천 카피의 같은 함수들이 디스크상에 존재하게 된다. Linux는 디스크 공간을 낭비하지 않고 이러한 단일 시스템 규모의 카피를 사용하기 위해서 애플리케이션을 구현한다. 게다가 일반적인 시스템 라이브러리 함수를 필요로 하는 각 프로세스는 단일 시스템 규모의 카피를 사용하는데 이것은 메모리로 로딩되어 공유된다.

Linux 에서, plugin과 dll은 동적 라이브러리로서 구현된다. 이 글의 나머지 부분은 애플리케이션이 실행된 후에 애플리케이션을 변경하기 위해 동적 라이브러리를 사용하는 예제들이다.

Linux 동적 링크

Linux의 애플리케이션들은 한 가지 이상의 방법으로 외부 함수와 연결된다: 정적 라이브러리 (lib*.a)를 이용하고, 라이브러리 코드를 애플리케이션의 실행 파일에 포함시켜 구현할 때 정적으로 링크되는 방법이 그 중 하나이고 또 다른 하나는 공유 라이브러리(lib*.so)를 이용하여 런타임 시 동적으로 링크하는 것이다. 동적 라이브러리는 동적 링크 로더에 의해 애플리케이션 실행 메모리로 매핑된다. 애플리케이션이 시작되기 전에, 동적 링크 로더는 필요한 공유 객체 라이브러리를 애플리케이션의 메모리로 매핑하거나 시스템 공유 객체를 사용하거나 또는 애플리케이션에 필요한 외부 레퍼런스를 분석한다. 이제, 애플리케이션은 실행 할 준비가 되었다.

다음 예제는 Linux에서 동적 라이브러리의 기본적인 사용에 대한 작은 프로그램이다:


main()
{
   printf("Hello world
");
}

gcc hello.c로 컴파일 될 때, a.out 라는 이름의 실행파일 이름이 만들어진다. 공유 라이브러리를 프린트하는 Linux 명령어 ldd a.out 을 사용할 때에는, 필요한 공유 라이브러리는 다음과 같다:


   libc.so.6 => /lib/libc.so.6 (0x4001d000)
         /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)

같은 동적 링크 로더가 실행된 후에 dll을 애플리케이션의 메모리로 매핑하는 데 사용된다. 애플리케이션은 어떤 동적 라이브러리가 로딩되는지 또 라이브러리에서 어떤 함수가 호출되는 지를 제어한다. 로딩과 링크를 수행하고 필요한 엔트리 포인트의 주소를 리턴하기 위해서 Linux 동적 로더를 사용한다.




위로


Linux dll 함수

Linux는 동적 링크 로더를 지원하기 위해 네 가지의 라이브러리 함수(dlopen, dlerror, dlsym, dlclose), 한 개의 include file (dlfcn.h), 그리고 두 개의 공유 라이브러리 (정적 library libdl.a, 동적 library libdl.so)를 제공한다. 다음은 라이브러리 함수에 대한 설명이다:

  • dlopen 은 오픈되어 메모리를 공유 객체 파일로 매핑하고 핸들을 리턴한다.
  • dlsym 는 포인터를 요청된 엔트리 포인트로 리턴한다.
  • dlerror 는 NULL 또는 포인터를 최신의 에러를 설명하는 an ASCII 스트링으로 리턴한다.
  • dlclose 는 핸들을 닫고 공유 객체의 매핑을 푼다.

동적 링크 로더 루틴인 dlopen은 파일을 열고 핸들을 만들기 위해서 파일시스템에서 공유 객체를 찾아야 한다. 파일 위치를 지정하는 네 가지의 방법이 있다:

  • dlopen call 에서 절대적 파일 경로
  • LD_LIBRARY_PATH 환경 변수에서 지정된 디렉토리
  • /etc/ld.so.cache에 지정된 라이브러리 리스트
  • /usr/lib 그리고 /lib




위로


dll 예제: C 프로그램과 dlTest

동적 링크 로더 예제 프로그램은 dl 루틴들을 시험하기 위한 C 프로그램이다. 모든 사람이 작성한 C 프로그램에 기초하여 콘솔에 "Hello World" 을 프린트한다. 프린트 된 원래의 메시지는 "HeLlO WoRlD" 이다. 이 테스트는 메시지를 다시 프린트 하는 두 개의 함수에 연결된다: 첫 번째는 대문자로 그 다음은 소문자로 프린트 된다.

다음은 프로그램의 아웃라인이다:

  1. dll은 dlfcn.h 파일을 포함하고 있고 필요한 변수가 정의된다. 필요한 변수는 다음과 같다:
    • 공유 라이브러리 파일을 위한 핸들
    • 매핑된 함수 엔트리 포인트를 위한 포인터
    • 에러 스트링을 위한 포인터

  2. 원래 메시지인 "HeLlO WoRlD"가 프린트 된다.

  3. UPPERCASE dll 용 공유 객체 파일은 dlopen 에 의해 열리고 핸들은 absolute path인 "/home/dlTest/UPPERCASE.so"과 RTLD_LAZY 옵션을 사용하여 리턴된다.
    • RTLD_LAZY 옵션은 dll의 외부 레퍼런스 변환을 dll이 실행될 때까지 지연한다.
    • Option RTLD_NOW 옵션은 dlopen이 리턴되기 전에 모든 dll 외부 레퍼런스를 변환한다.

  4. 엔트리 포인트 printUPPERCASE 어드레스는 dlsym에 의해 리턴된다.

  5. printUPPERCASE는 호출되고 변경된 메시지인 "HELLO WORLD"가 프린트된다.

  6. UPPERCASE.so에 대한 핸들은 dlclose에 의해 닫히고 dll은 메모리에서 매핑이 해지된다.

  7. lowercase dll을 위한 공유 객체 파일인 lowercase.so는 dlopen에 의해 열리고 공유 객체를 검색하기 위해 환경 변수인 LD_LIBRARY_PATH를 사용하여 핸들은 리턴된다.

  8. 엔트리 포인트 printLowercase 어드레스는 dlsym에 의해 리턴된다.

  9. printLowercase가 호출되고 변경된 메시지인 "hello world"가 프린트된다.

  10. lowercase.so로의 핸들은 dlclose 에 의해 닫히고 dll은 메모리에서 매핑이 해지된다 .

dlopen, dlsym 또는 dlclose 로의 각 호출 후에 마지막 에러와 에러 스트링을 프린트하기 위해서 dlerror가 호출된다. 다음은 dlTest의 실행 테스트이다:


dlTest  2-Original message 
HeLlO WoRlD
	dlTest  3-Open Library with absolute path return-(null)- 
	dlTest  4-Find symbol printUPPERCASE return-(null)- 
HELLO WORLD
	dlTest  5-printUPPERCASE return-(null)- 
	dlTest  6-Close handle return-(null)-
	dlTest  7-Open Library with relative path return-(null)- 
	dlTest  8-Find symbol printLowercase return-(null)- 
hello world
	dlTest  9-printLowercase return-(null)- 
	dlTest 10-Close handle return-(null)-

dlTest.c, UPPERCASE.c, lowercase.c에 대한 소스리스트는 Listings 을 참조하라 .




위로


Major heading

런타임 동적 링크를 만드는 데에는 다음 세 단계 절차를 거친다:

  1. dll을 위치 독립 코드로서 컴파일
  2. dll 공유 객체 파일 구현
  3. 주요 프로그램의 컴파일 및 dl 라이브러리와 링크

UPPERCASE.c와 lowercase.c를 컴파일하는 gcc 명령어에는 -fpic 옵션이 포함되어 있다. -fpic 옵션과 -fPIC 옵션은 코드 생성이 위치 독립적이 되도록 한다. 이것은 공유 객체 라이브러리를 다시 만드는 데 필요하다. -fPIC 옵션은 위치 독립적인 코드를 만들고 이것은 큰 오프셋을 위해 실행된다. UPPERCASE.o 와 lowercase.o 를 위한 두 번째 gcc 명령어는 -shared 옵션으로 전달된다. 이것은 동적 링크에 적합한 *.so 공유 객체 파일을 만든다.

dltest를 컴파일 하고 실행하는 데 사용되는 ksh 스크립트이다:


#!/bin/ksh
#  Build shared library
#
#set -x
clear

#
#  Shared library for dlopen absolute path test
#
if [ -f UPPERCASE.o ]; then rm UPPERCASE.o
fi
gcc  -c -fpic UPPERCASE.c
if [ -f UPPERCASE.so ]; then rm UPPERCASE.so
fi
gcc -shared -lc  -o UPPERCASE.so  UPPERCASE.o 

#
#  Shared library for dlopen relative path test
#
export LD_LIBRARY_PATH=`pwd`
if [ -f lowercase.o ]; then rm lowercase.o
fi
gcc  -c -fpic lowercase.c
if [ -f lowercase.so ]; then rm lowercase.so
fi
gcc -shared -lc  -o lowercase.so  lowercase.o

#
#  Rebuild test program
#
if [ -f dlTest ]; then rm dlTest
fi
gcc -o dlTest dlTest.c -ldl
echo Current LD_LIBRARY_PATH=$LD_LIBRARY_PATH
dlTest




위로


요약

Linux 시스템에서 런타임 시 애플리케이션으로 동적 링크될 수 있는 공유 객체를 만드는 것은 간단하다. 애플리케이션은 dlopen, dlsym, dlclose같은 함수 호출을 동적 링크 로더에 사용함으로서 공유 객체에 액세스 할 수 있다. 모든 에러는 dlerror에 의해 스트링으로 리턴된다. 런타임 시, 주요 애플리케이션은 absolute path 또는 LD_LIBRARY_PATH에 path relative를 사용하여 공유 객체 라이브러리를 찾고 필요한 dll 엔트리 포인트의 어드레스를 요청한다. 필요할 경우 간접적인 함수 호출이 dll에 이루어지고, 마지막으로 공유 객체로의 핸들은 종료되고 객체들은 메모리에서 매핑이 해지되어 사용할 수 있게 된다.

공유 객체는 추가 옵션인 -fpic 또는 -fPIC으로 컴파일 되어 위치 독립적인 코드를 만들고 -shared 옵션으로 공유 객체 라이브러리에 위치한다.

Linux에서 사용할 수 있는 공유 객체 라이브러리와 동적 링크 로더는 애플리케이션에 추가적인 기능을 제공한다. 디스크와 메모리에서 실행파일의 사이즈를 줄인다. 선택적인 애플리케이션 함수들은 필요할 경우 로딩 될 수 있고, 전체 애플리케이션을 다시 구현하지 않고도 결함이 픽스될 수 있고 third-party plugin도 애플리케이션에 포함될 수 있다.




위로


Listings (애플리케이션과 dll)


dlTest.c:

/*************************************************************/
/*     Test Linux Dynamic Function Loading				*/
/*         								*/
/*     void       *dlopen(const char *filename, int flag)			*/
/*     		Opens dynamic library and return handle		*/
/*         								*/
/*     const char *dlerror(void)					*/
/*         	Returns string describing the last error.			*/
/*         								*/
/*     void       *dlsym(void *handle, char *symbol)			*/
/*         	Return pointer to symbol's load point. 			*/
/*         	If symbol is undefined, NULL is returned.			*/
/*         								*/
/*     int        dlclose (void *handle) 					*/
/*         	Close the dynamic library handle.				*/
/*         								*/
/*         								*/
/*         								*/
/*************************************************************/
#include<stdio.h>
#include	<stdlib.h>
 
/*								*/
/* 1-dll include file and variables	*/
/*								*/
#include	<dlfcn.h>
void  *FunctionLib;		/*  Handle to shared lib file	*/
int   (*Function)();		/*  Pointer to loaded routine	*/
const char *dlError;		/*  Pointer to error string		*/

main( argc, argv )
{
  int   rc;				/*  return codes 			*/
  char HelloMessage[] = "HeLlO WoRlD\n";
 
/*								*/
/* 2-print the original message					*/
/*								*/
  printf("	dlTest  2-Original message \n");
  printf("%s", HelloMessage);

/*                                               */
/*  3-Open Dynamic Loadable Libary with absolute path      */
/*                                              */
  FunctionLib = dlopen("/home/dlTest/UPPERCASE.so",RTLD_LAZY);
  dlError = dlerror();
  printf("	dlTest  3-Open Library with absolute path return-%s- \n", dlError);
  if( dlError ) exit(1);

/*								*/
/* 4-Find the first loaded function	*/
/*								*/
  Function    = dlsym( FunctionLib, "printUPPERCASE");
  dlError = dlerror();
  printf("	dlTest  4-Find symbol printUPPERCASE return-%s- \n", dlError);
  if( dlError ) exit(1);

/*								*/
/* 5-Execute the first loaded function				*/
/*								*/
  rc = (*Function)( HelloMessage );
  printf("	dlTest  5-printUPPERCASE return-%s- \n", dlError);

/*								*/
/* 6-Close the shared library handle					*/
/* Note:  after the dlclose, "printUPPERCASE" is not loaded		*/
/*								*/
  rc = dlclose(FunctionLib);
  dlError = dlerror();
  printf("	dlTest  6-Close handle return-%s-\n",dlError); 
  if( rc ) exit(1);


/*								*/
/*  7-Open Dynamic Loadable Libary using LD_LIBRARY path      	*/
/*								*/
  FunctionLib = dlopen("lowercase.so",RTLD_LAZY);
  dlError = dlerror();
  printf("	dlTest  7-Open Library with relative path return-%s- \n", dlError);
  if( dlError ) exit(1);

/*								*/
/* 8-Find the second loaded function				*/
/*								*/
  Function    = dlsym( FunctionLib, "printLowercase");
  dlError = dlerror();
  printf("	dlTest  8-Find symbol printLowercase return-%s- \n", dlError);
  if( dlError ) exit(1);

/*								*/
/* 8-execute the second loaded function				*/
/*								*/
  rc = (*Function)( HelloMessage );
  printf("	dlTest  9-printLowercase return-%s- \n", dlError);

/*								*/
/* 10-Close the shared library handle				*/
/*								*/
  rc = dlclose(FunctionLib);
  dlError = dlerror();
  printf("	dlTest 10-Close handle return-%s-\n",dlError); 
  if( rc ) exit(1);

  return(0);

}



UPPERCASE.c:

/************************************************/
/*      Function to print input string as UPPER case.         */
/*      Returns 1.                                                            */
/*********************************************** */

int printUPPERCASE ( inLine )
char inLine[];
{
   char UPstring[256];
   char *inptr, *outptr;
   
   inptr = inLine;
   outptr = UPstring;
   while ( *inptr != '\0' )
      *outptr++ = toupper(*inptr++);
  *outptr++ = '\0';
   printf(UPstring);
   return(1);
}



lowercase.c

/********************************************/
/*     Function to print input string as lower case.      */
/*     Returns 2.                                                      */
/******************************************* */
int printLowercase( inLine )
char inLine[];
{
   char lowstring[256];
   char *inptr, *outptr;
   inptr = inLine;
   outptr = lowstring;
   while ( *inptr != '' )
      *outptr++ = tolower(*inptr++);
  *outptr++ = '';
   printf(lowstring);
   return(2);
}




위로


참고자료




위로


필자소개

Allen Wilson 은 IBM에서 e-business 아키텍트 겸 컨설턴트로 일하고 있다. 또한, Linux, AIX, WebSphere Application Server, 애플리케이션 호스팅 전문가이다.





위로


기사에 대한 평가

매우 불만족 (1)
불만족 (2)
보통 (3)
만족 (4)
매우 만족 (5)




위로



    IBM 소개개인정보 보호정책문의