Dynamic loading

Dynamic loading (dynamické načítání) je v informatice označení pro mechanismus, který načte knihovnu do paměťového prostoru za běhu procesu. Běžící proces tak získá možnost volat procedury a funkce, které se nacházejí v načtené knihovně. Knihovnu lze později z paměti uvolnit. Mechanismus umožňuje programátorovi v případě nedostupnosti knihovny provést náhradní činnost (použít alternativní knihovnu, zobrazit hlášení uživateli a podobně). Tím se odlišuje od klasického zavádění dynamických knihoven při spuštění programu, kdy musí být přítomny všechny potřebné knihovny.

Historie

Dynamické načítání bylo běžným mechanismem pro operační systém IBM/360 (začátek 60. let až do současné Z/Architektury) a to zejména pro obsluhu vstupně-výstupních rutin, COBOL a PL/1 běhových knihoven. Co se týče programování aplikací, nahrávání je do značné míry transparentní, protože je většinou vyřešené na úrovni operačního systému. IBM využívá dynamické načtení od 70. let 20. století u transakčního zpracování strategického systému CICS a to jak pro jádro operačního systému, tak i pro normální aplikační programy. Opravy pak mohou být uskutečněny za běhu systému nebo programu bez nutnosti jejich restartu.

Mezi hlavní výhody patří:

  • opravy subsystému opravují celý program bez nutnosti jeho nového linkování
  • knihovny mohou být chráněny před neautorizovanou úpravou

Použití

Dynamické načítání je nejčastěji používáno v implementaci softwarových pluginů (modulární struktura Apache HTTP Serveru). Nebo v programech, které nabízejí více podporovaných knihoven a uživatel si je jednotlivě může volit (rozšiřující knihovny pro PHP).

C/C++

Dynamické načítání nepodporují všechny systémy. Mac OS X, Linux a Solaris nabízí dynamické načítání prostřednictvím knihovních „dl“ funkcí v jazyce C. Operační systém Windows podporuje dynamické načítání pomocí volání Windows API.

Shrnutí

Název Standard POSIX/UNIX API Microsoft Windows API
Zařazení hlavičkového souboru #include <dlfcn.h> #include <windows.h>
Definice hlavičky dl

(libdl.so, libdl.dylib, závisí na operačním systému)

Kernel32.dll
Načtení knihovny dlopen LoadLibrary
LoadLibraryEx
Extrahování obsahu dlsym GetProcAddress
Zavření knihovny dlclose FreeLibrary

Načtení knihovny

Načtení knihovny se provede prostřednictvím LoadLibrary nebo LoadLibraryEx na operačním systému Windows a pomocí dlopen na operačních systémech na bázi Linuxu. Následují příklady:

Linux, *BSD, Solaris, etc.

void* sdl_library = dlopen("libSDL.so", RTLD_LAZY);
if(sdl_library == NULL) {
   // oznámení chyby ...
} else {
   // výsledek zavoláním dlsym
}

Mac OS X

UNIX library:

void* sdl_library = dlopen("libsdl.dylib", RTLD_LAZY);
if(sdl_library == NULL) {
   // oznámení chyby ...
} else {
   //  výsledek zavoláním dlsym
}

OS X Framework:

void* sdl_library = dlopen("/Library/Frameworks/SDL.framework/SDL", RTLD_LAZY);
if(sdl_library == NULL) {
   // oznámení chyby ...
} else {
   // výsledek zavoláním dlsym
}

Windows

HMODULE sdl_library = LoadLibrary("SDL.dll");
if( sdl_library == NULL) {
   //oznámení chyby ...
} else {
   // výsledek zavoláním GetProcAddress
}

Extrahování obsahu knihovny

Extrahování obsahu dynamicky načítané knihovny je dosaženo pomocí příkazu GetProcAddress ve Windows a dlsym v Unix systému.

Linux, *BSD, Mac OS X, Solaris, etc.

void* initializer = dlsym(sdl_library,"SDL_Init");
if(initializer == NULL) {
   // oznámení chyby ...
} else {
   ...
}

Windows

FARPROC initializer = GetProcAddress(sdl_library,"SDL_Init");
if(initializer == NULL) {
   // report error ...
} else {
   ...
}

Převod extrahovaného obsahu knihovny

Vrácený výsledek prostřednictvím dlsym() nebo GetProcAddress() musí být převeden do požadované destinace předtím, než může být použit.

Windows

V případě systému Windows, konverze je jednoduchá, protože FARPROC je v podstatě již ukazatel funkce:

typedef INT_PTR (*FARPROC)(void);

Může nastat problém, pokud adresy objektu jsou vyvolány místo funkce. Nicméně, obvykle jeden chce získat funkce stejně, takže to není ve výsledku problém.

typedef void (*sdl_init_function_type)(void);
sdl_init_function_type init_func = (sdl_init_function_type) initializer;

UNIX (POSIX)

Dle specifikace POSIX je výsledkem provedení dlsym() prázdný ukazatel (pointer), což způsobuje problém. Je to způsobeno tím, že programovací jazyky C a C++ nedovolují provést konverzi mezi ukazateli na objekty a ukazateli funkcí (není vyžadováno aby měl ukazatel funkce stejnou velikost jako ukazatel na objekt). Je tedy striktně zakázáno provést konverzi mezi typem void* a ukazatelem na funkci. Na většině v současnosti používaných systémů jsou ukazatele na funkce s ukazateli na objekty mezi sebou již konvertibilní.

Následující fragment kódu ukazuje jeden ze způsobů řešení, který umožňuje provádět tuto konverzi v různých systémech:

typedef void (*sdl_init_function_type)(void);
sdl_init_function_type init_func = (sdl_init_function_type)initializer;

Výše uvedená část programového kódu může u některých kompilátorů vyvolat toto varovné hlášení: „Přejmenování ukazatele dereferenčního typu porušuje pravidla aliasingu“. Řešením je použití následujícího programového kódu:

typedef void (*sdl_init_function_type)(void);
union { sdl_init_function_type func; void * obj; } alias;
alias.obj = initializer;
sdl_init_function_type init_func = alias.func;

Tím se zakáže varovné hlášení.

Související články

Externí odkazy

Zdroj