programing

C에서 모듈 시스템 생성(동적 로딩)

easyjava 2023. 6. 8. 22:43
반응형

C에서 모듈 시스템 생성(동적 로딩)

런타임에 컴파일된 C 코드를 로드한 다음 그 안에서 함수를 호출하려면 어떻게 해야 합니까?단순히 exec()을 부르는 것과는 다릅니다.

EDIT: 모듈을 로드하는 프로그램이 C에 있습니다.

dlopen이 가는 길입니다.다음은 몇 가지 예입니다.

dlopen으로 플러그인 로드:

#include <dlfcn.h>
...
int
main (const int argc, const char *argv[])
{

  char *plugin_name;
  char file_name[80];
  void *plugin;
  ...
  plugin = dlopen(file_name, RTLD_NOW);
  if (!plugin)
  {
     fatal("Cannot load %s: %s", plugin_name, dlerror ());
  }

위의 컴파일:

% cc  -ldl -o program program.o 

그런 다음 플러그인에 대해 이 API를 가정합니다.

/* The functions we will find in the plugin */
typedef void (*init_f) ();
init_f init;
typedef int (*query_f) ();
query_f query;

플러그인에서 init()의 주소 찾기:

init = dlsym(plugin, "init");
result = dlerror();
if (result)
{
   fatal("Cannot find init in %s: %s", plugin_name, result);
}
init();

다른 함수 query()를 사용하여 값을 반환합니다.

query = dlsym (plugin, "query");
result = dlerror();
if (result)
{
    fatal("Cannot find query in %s: %s", plugin_name, result);
}
printf("Result of plugin %s is %d\n", plugin_name, query ());

온라인으로 전체 예제를 검색할 수 있습니다.

Linux/UNIX를 할 수 .dlopen/dlsym/dlerror/dlclose공유 라이브러리를 동적으로 열고 제공하는 기호(기능 포함)에 액세스하는 기능입니다. 자세한 내용은 man 페이지를 참조하십시오.

GNU/리눅스 사용자용

동적 로딩 라이브러리는 프로그램을 실행하고 런타임에 어떤 기능을 사용할지 결정할 수 있는 메커니즘입니다.어떤 경우에는static변수도 가능합니다.

처음보를 처음 합니다.man 3 dlopen아니면 온라인으로 보기

은 다음과 dlfcn표준의 일부가 아니므로 이 라이브러리를 사용하여 개체 파일에 연결해야 합니다.libdl.(so/a)따라서 다음과 같은 것이 필요합니다.

gcc yours.c -ldl

그러면 당신은 파일 이름을 가지고 있습니다.a.out실행할 수는 있지만 제대로 작동하지 않습니다. 그 이유를 설명하겠습니다.


완전한 예:

처음에 두 개의 파일을 크레이트합니다.func1.c그리고.func2.c각각 다음과 같다.우리는 이 함수들을 런타임에 호출하고 싶습니다.

func.c

int func1(){
    return 1;
}

func2.c

const char* func2(){
    return "upgrading to version 2";
}

이제 두 가지 기능이 있습니다. 모듈을 만들어 보겠습니다.

ALP ❱ gcc -c -fPIC func1.c
ALP ❱ gcc -c -fPIC func2.c
ALP ❱ gcc -o libfunc.so -shared -fPIC func1.o func2.o 

에 대한 탐구심 때문에.-fPIC=> PIC

당신은 당제신은이가 .dynamic library 이름:libfunc.so

메인 프로그램을 만들자 (=temp.c기능을 . 이러한 기능을 사용하고자 합니다.

머리글 파일

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

그리고 메인 프로그램.

int main()
{
    // pointer function to func1 and func2
    int         ( *f1ptr )();
    const char* ( *f2ptr )();

    // for pointing to the library
    void* handle = NULL;

    // for saving the error messages
    const char* error_message = NULL;

    // on error dlopen returns NULL
    handle = dlopen( "libfunc.so", RTLD_LAZY );

    // check for error, if it is NULL
    if( !handle )
    {
        fprintf( stderr, "dlopen() %s\n", dlerror() );
        exit( 1 );
    }

    /*
        according to the header file:

        When any of the above functions fails, call this function
        to return a string describing the error.  Each call resets
        the error string so that a following call returns null.

        extern char *dlerror (void) __THROW;
    */

    // So, reset the error string, of course we no need to do it just for sure
    dlerror();

    // point to func1
    f1ptr = (int (*)()) dlsym( handle, "func1" );

    // store the error message to error_message
    // because it is reseted if we use it directly
    error_message = dlerror();
    if( error_message ) //   it means if it is not null
    {
        fprintf( stderr, "dlsym() for func1 %s\n", error_message );
        dlclose( handle );
        exit( 1 );
    }

    // point the func2
    f2ptr = (const char* (*)()) dlsym( handle, "func2" );

    // store the error message to error_message
    // because it is reseted if we use it directly
    error_message = dlerror();
    if( error_message ) //   it means if it is not null
    {
        fprintf( stderr, "dlsym() for func2 %s\n", error_message );
        dlclose( handle );
        exit( 1 );
    }

    printf( "func1: %d\n", ( *f1ptr )() );
    printf( "func2: %s\n", ( *f2ptr )() );

    // unload the library
    dlclose( handle );

    // the main return value
    return 0;
}

이제 이 코드만 컴파일하면 됩니다(=temp.c), 따라서 시도해 보십시오.

ALP ❱ gcc temp.c -ldl
ALP ❱ ./a.out
libfunc.so: cannot open shared object file: No such file or directory

그것은 작동하지 않습니다! 쉽습니까; 왜냐하면 우리의a.out라이브러리를 수 있는지 .libfunc.so그러므로 그것은 우리에게 말합니다.cannot not open ...

프로그램을 말하는 방법 (=a.out도서관을 찾으시려고요?

  1. 용사를 ld 커링커
  2. 변수 사용하기LD_LIBRARY_PATH
  3. 표준 경로 사용

번째 ▁of법▁help방▁first,▁with▁way째번.ld

사용하다-Wl,-rpath,그리고.pwd그리고 그것에 대한 주장으로 경로를 제시합니다.

ALP ❱ gcc temp.c -ldl
ALP ❱ ./a.out
libfunc.so: cannot open shared object file: No such file or directory
ALP ❱ pwd
/home/shu/codeblock/ALP
ALP ❱ gcc temp.c -ldl -Wl,-rpath,/home/shu/codeblock/ALP
ALP ❱ ./a.out
func1: 1
func2: upgrading to version 2

제2의 방법

ALP ❱ gcc temp.c -ldl
ALP ❱ ./a.out
libfunc.so: cannot open shared object file: No such file or direc
ALP ❱ export LD_LIBRARY_PATH=$PWD
ALP ❱ echo $LD_LIBRARY_PATH
/home/shu/codeblock/ALP
ALP ❱ ./a.out
func1: 1
func2: upgrading to version 2
ALP ❱ export LD_LIBRARY_PATH=
ALP ❱ ./a.out
libfunc.so: cannot open shared object file: No such file or 

제3의 길

당신은 가지고 있다libfunc.so현재 경로에서 라이브러리의 표준 경로로 복사할 수 있습니다.

ALP $ sudo cp libfunc.so /usr/lib
ALP ❱ gcc temp.c -ldl
ALP ❱ ./a.out
func1: 1
func2: upgrading to version 2

수있다니습에서 제거할 수./usr/lib그리고 그것을 사용합니다.당신에게 달렸어요.

메모

우리의 것을 알아내는 방법.a.out그것의 경로에 대해 알고 있습니까?
예:예:

ALP ❱ gcc temp.c -ldl -Wl,-rpath,/home/shu/codeblock/ALP
ALP ❱ strings a.out  | grep \/
/lib/ld-linux.so.2
/home/shu/codeblock/ALP

C에서 어떻게 사용할 수 있습니까?
내가 아는 한 당신은 할 수 없습니다 왜냐하면g++ 반면, 함이망반면는치름을수▁mang는gcc 다음을하면 안 .extern "C" int func1();예를들면.

자세한 내용은 man 페이지와 Linux 프로그래밍 책을 참조하십시오.

이 질문에 답했지만 이 주제에 관심이 있는 다른 사람들은 이전 플러그인 기반 응용프로그램의 플랫폼 간 예를 높이 평가할 수 있다고 생각했습니다.이 예제는 win32 또는 linux에서 작동하며 file 인수에 지정된 동적으로 로드된 .so 또는 .dll에서 'constructor'라는 함수를 검색하고 호출합니다.예제는 c++에 있지만 c에 대해서는 절차가 같아야 합니다.

//firstly the includes
#if !defined WIN32
   #include <dlfcn.h>
   #include <sys/types.h>
#else
   #include <windows.h>
#endif

//define the plugin's constructor function type named PConst
typedef tcnplugin* (*PConst)(tcnplugin*,tcnplugin*,HANDLE);

//loads a single specified tcnplugin,allmychildren[0] = null plugin
int tcnplugin::loadplugin(char *file) {
    tcnplugin *hpi;
#if defined WIN32               //Load library windows style
    HINSTANCE hplugin=LoadLibrary(file);
    if (hplugin != NULL) {
            PConst pinconstruct = (PConst)GetProcAddress(hplugin,"construct");
#else                                   //Load it nix style
    void * hplugin=dlopen(file,RTLD_NOW);
    if (hplugin != NULL) {
            PConst pinconstruct = (PConst)dlsym(hplugin,"construct");
#endif   
            if (pinconstruct != NULL) { //Try to call constructor function in dynamically loaded file, which returns a pointer to an instance of the plugin's class
                    hpi = pinconstruct(this, this, hstdout);
            } else {
                    piprintf("Cannot find constructor export in plugin!\n");
                    return 0;
            }
    } else {
            piprintf("Cannot open plugin!\n");
#if !defined WIN32
            perror(dlerror());
#endif
            return 0;
    }
    return addchild(hpi); //add pointer to plugin's class to our list of plugins
}

호출할 모듈의 함수가 c++로 작성된 경우 다음과 같이 외부 "C"로 함수를 선언해야 합니다.

extern "C" pcparport * construct(tcnplugin *tcnptr,tcnplugin *parent) {
    return new pcparport(tcnptr,parent,"PCPARPORT",0,1);
}

또한 cpluff를 볼 수 있습니다.이것은 pure c에 있는 플러그인 관리 라이브러리입니다.

프레임워크를 고려할 의향이 있다면 Qt는 QPluginLoader를 제공합니다.Qt 5 문서(또는 이전 Qt 4.8 문서의 경우 여기 참조)

보다 세밀한 제어가 필요하거나 원하는 경우 Qt는 QLibriary: Qt 5 문서(또는 이전 Qt 4.8 문서의 경우 여기 참조)를 통해 라이브러리를 즉시 로드할 수 있는 수단도 제공합니다.

더 좋은 것은 플랫폼 간에 이동할 수 있다는 점입니다.

Perl과 같은 동적 언어는 항상 이 작업을 수행합니다.Perl 인터프리터는 C로 작성되며 많은 Perl 모듈은 부분적으로 C로 작성됩니다.이러한 모듈이 필요한 경우 컴파일된 C 구성 요소가 즉시 동적으로 로드됩니다.다른 답변에서 언급한 것처럼 이러한 모듈을 저장하는 메커니즘은 윈도우즈의 DLL과 UNIX의 공유 라이브러리(.so 파일)입니다.UNIX에 공유 라이브러리를 로드하라는 요청은 dlopen()이라고 생각합니다.해당 호출에 대한 설명서부터 시작하여 UNIX에서 이 작업을 수행하는 방법에 대한 포인터를 찾을 수 있습니다.Windows의 경우 DLL을 조사하고 런타임에 동적으로 로드하는 방법을 배워야 합니다.[또는 Cygwin UNIX 에뮬레이션 계층을 거치면 UNIX 에뮬레이션 계층과 동일한 호출을 Windows에서 사용할 수 있지만, Cygwin에 대해 이미 사용하고 컴파일하고 있지 않는 한 권장하지 않습니다.]

공유 라이브러리에 대한 링크와는 다릅니다.호출할 코드를 미리 정확히 알고 있으면 공유 라이브러리를 기반으로 빌드할 수 있으며 빌드가 해당 라이브러리에 "동적으로 연결"됩니다. 특별한 처리 없이 라이브러리의 루틴이 실제로 호출될 때만 라이브러리의 루틴이 메모리로 로드됩니다.그러나 임의의 객체 코드를 로드할 수 있는 코드를 작성할 계획이라면, 빌드 시에는 식별할 수 없지만 실행 시에는 어떻게든 선택되기를 기다리는 코드를 작성할 수 없습니다.이를 위해서는 dlopen()과 Windows 사촌을 사용해야 합니다.

실제 예제를 보려면 Perl 또는 다른 동적 언어가 이를 수행하는 방식을 살펴볼 수 있습니다.이런 종류의 동적 로딩을 담당하는 펄 라이브러리는 DynaLoader입니다. 펄과 C 구성 요소를 모두 가지고 있다고 생각합니다.Python과 같은 다른 동적 언어들도 당신이 볼 수 있는 것과 유사한 것을 가지고 있다고 확신합니다. 그리고 출시되지 않은 Perl 6의 가상 머신인 Parot은 확실히 이를 수행하기 위한 메커니즘을 가지고 있습니다(또는 미래에 그럴 것입니다).

따라서 Java는 JNI(Java Native Interface) 인터페이스를 통해 이러한 작업을 수행하므로 OpenJDK의 소스 코드를 보고 Java가 UNIX와 Windows에서 이러한 작업을 수행하는 방법을 확인할 수 있습니다.

DIY 방식이 있습니다.이를 수행하는 방법(및 가능성)은 시스템에 따라 다르지만 일반적인 아이디어는 파일을 열고, 파일의 내용을 메모리로 읽고, 해당 메모리를 실행 가능하게 만들고, 이 메모리 내의 유효한 위치로 함수 포인터를 초기화하면 됩니다.

물론 이것은 실행 가능한 코드일 뿐이라고 가정하고 있습니다. 그럴 가능성은 거의 없습니다.이 코드는 RAM에도 데이터를 로드해야 하며 전역/정적 변수를 위한 공간이 필요할 수 있습니다.이 모든 것을 직접 로드할 수는 있지만 실행 코드로 들어가 모든 메모리 참조를 조정해야 합니다.

대부분의 운영 체제는 동적 연결을 허용하며, 이 모든 것을 가능하게 합니다.

Windows에서는 다음과 같이 작업합니다.

  • 코드 생성(컴파일러를 찾기 쉽고 라이브러리 요구 사항이 최소이므로 C)
  • DLL에 컴파일/연결할 작업 생성
  • Load Library와 함께 로드
  • GetProcAddress를 사용하여 함수 포인터 가져오기

생성/컴파일/링크 단계는 일반적으로 1초 미만의 시간이 소요됩니다.

언급URL : https://stackoverflow.com/questions/384121/creating-a-module-system-dynamic-loading-in-c

반응형