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.

HistorieEditovat

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íEditovat

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++Editovat

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íEditovat

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í knihovnyEditovat

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.Editovat

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

Mac OS XEditovat

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
}

WindowsEditovat

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

Extrahování obsahu knihovnyEditovat

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.Editovat

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

WindowsEditovat

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

Převod extrahovaného obsahu knihovnyEditovat

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.

WindowsEditovat

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)Editovat

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ánkyEditovat

Externí odkazyEditovat