Dinamikusan betölthető (DL) programkönyvtárak olyan programkönyvtárak, amik nem a program indulásakor töltődnek be. Különösen hasznos modulok és plug-in-ek megvalósítására, mivel lehetővé teszi, hogy csak akkor töltsük be ezeket, ha szükséges. Például a Betölthető Hitelesítő Modul (Pluggable Authentication Modules; PAM) rendszer DL programkönyvtárakat használ, így lehetővé teszi a rendszergazdáknak, hogy beállítsák és átállítsák az hitelesítő/azonosító eljárásokat. A dinamikusan betölthető programkönyvtárak jól használhatóak továbbá olyan értelmezők megvalósítására, amik alkalmanként lefordítanak kódokat gépi kódra és a lefordított hatékony változatokat használják, mindezt leállás nélkül. Például ez az eljárás használható just-in-time fordítók vagy multi-user dungeon (MUD) megvalósítására.
Linuxon a DL programkönyvtárak jelenleg formailag semmilyen speciális tulajdonsággal nem rendelkeznek. Standard tárgykódként vagy megosztott programkönyvtárként készülnek, mint ahogy azt feljebb már bemutattuk. A fő különbség, hogy ezek a programkönyvtárak nem töltődnek be automatikusan a program csatolása vagy elindítása során. Ehelyett van egy API, aminek segítségével megnyithatjuk a programkönyvtárat, szimbólumokat kereshetünk benne, javíthatjuk a hibákat és bezárhatjuk a programkönyvtárat. C felhasználoknak a <dlfcn.h> header fájlt kell beszerkeszteni (include) ennek az API-nak a használatához.
Az interfész használata Linuxon alapvetően ugyanolyan mint Solaris-on, amit "dlopen()" API-nak fogok hívni. Ugyanakkor ezt az interfészt nem támogatja minden platform. HP-UX egy másik shl_load() mechanizmust használ és a Windows platform is egy teljesen más DLL interfészt használ. Ha a célod a széleskörű hordozhatóság, akkor valószínűleg valamilyen köztes programkönyvtárat kell használnod, ami elrejti a különbségeket a platformok között. Egy megoldás lehet a glib programkönyvtár, ami támogatja a modulok dinamikus betöltését. Ez az platformok dinamikus betöltő rutinjait használja ahhoz, hogy egy hordozható interfészt valósítson meg ezekhez a függvényekhez. Többet tudhatsz meg a glib-ről a http://developer.gnome.org/doc/API/glib/glib-dynamic-loading-of-modules.html honlapon. Mivel a glib interfész jól dokumentált nem részletezem itt. Egy másik lehetőség a libltdl használata, ami része a GNU libtool-nak. Ha többet akarsz ennél, akkor vess egy pillantást a CORBA Object Request Broker (ORB)-re. Ha még mindig érdekel, hogyan használhatod közvetlenül a Linux és Solaris interfészeket, akkor olvass tovább.
Azok a fejlesztők aki C++-t és dinamikusan betölthető (DL) programkönyvtárakat akarnak használni elolvashatják a "C++ dlopen mini-HOGYANt".
A dlopen(3) függvény megnyitja a programkönyvtárat és előkészíti használatra. C prototípusa az alábbi:
void * dlopen(const char *filename, int flag); |
Könyvtárak kettősponttal elválasztott listája a felhasználó LD_LIBRARY_PATH környezeti változójában.
Az /etc/ld.so.cache fájlban található programkönyvtárak listájában, amit az /etc/ld.so.conf-ból generáltunk.
/lib, aztán /usr/lib. Megjegyezzük, hogy ez pont a fordítottja a régi a.out betöltő viselkedésének. A régi a.out betöltő először az /usr/lib könyvtárban keresett aztán a /lib könyvtárban (lásd ld.so(8) man oldal). Normális körülmények között ez nem számít, a programkönyvtárnak az egyik vagy a másik könyvtárban kellene lennie (sohasem mindkettőben), mivel azonos névvel rendelkező különböző programkönyvtárak katasztrófát okoznak.
Ha a programkönyvtárak függnek egymástól (pl.: X függ Y-tól), akkor először a függőségeket kell betöltened (a példában Y-t és aztán X-et).
A dlopen() visszatérési értéke egy "handle", amire úgy tekinthetsz, mint egy opaque érték, amit más DL programkönyvtárak használhatnak. A dlopen() NULL-al fog visszatérni, ha a betöltés sikertelen volt. Ezt ellenőrizned is kell. Ha ugyanazt a programkönyvtárat többször töltöd be dlopen()-el, az ugyanazt az fájlkezelőt (handle) fogja visszaadni.
Ha a programkönyvtár tartalmaz egy _init nevű public eljárást, akkor az abban lévő kód lefut, mielőtt a dlopen visszatér. Ezt a saját program inicializációs eljárásnak használhatod. Ugyanakkor a programkönyvtáraknak nem kötelező _init és _fini nevű eljárásokat tartalmazniuk. Ez a mechanizmus elavult és nem kívánatos működést eredményezhet. Helyette a programkönyvtáraknak __attribute__((constructor)) és __attribute__((destructor)) függvény attribútumokkal megjelölt eljárásokat kellene használniuk (feltéve, hogy gcc-t használsz). További részleteket a "Programkönyvtár konstruktor és destruktor függvények" fejezetben olvashatsz erről.
Hibákat a dlerror() függvényhívással lehet kezelni. Ez visszatér az utolsó dlopen(), dlsym() vagy dlclose() függvényhívás okozta hibát leíró karaktersorozattal. Kellemetlen, hogy a dlerror() meghívása után a többi dlerror() függvényhívás NULL-al fog visszatárni mindaddig, amíg egy másik hiba nem keletkezik.
Nincs értelme betölteni egy DL programkönyvtárat, ha nem tudod használni. A DL programkönyvtár használatának alaprutinja a dlsym(3). Ez szimbólumokat keres a már megnyitott programkönyvtárakban. A függvénydefiníció így néz ki:
void * dlsym(void *handle, char *symbol); |
A dlsym() NULL-al tér vissza, ha a szimbólumot nem találta. Ha előre tudod, hogy a szimbólum értéke soha nem NULL vagy zero, akkor ez rendben van, de potenciális veszélyforrás minden más esetben. Ugyanis, ha kapsz egy NULL-t nem tudod eldönteni, hogy a szimbólumot nem találta a dlsym, vagy az értéke éppen NULL. A szokásos megoldás ilyenkor, hogy először meghívod a dlerror()-t (azért, hogy eltüntess, minden hibát ami létezik), aztán a dlsym()-et hívod a szimbólum keresésére, végül újra a dlerror()-t, hogy lásd történt-e hiba. A kódrészlet valahogy így nézhet ki:
dlerror(); /* clear error code */ s = (actual_type) dlsym(handle, symbol_being_searched_for); if ((err = dlerror()) != NULL) { /* handle error, the symbol wasn't found */ } else { /* symbol found, its value is in s */ } |
A dlopen() párja a dlclose(), ami bezárja a DL programkönyvtárat. A DL programkönyvtár kapcsolatszámlálókat (link counts) hoz létre a dinamikus fájlkezelőkhöz, így a dinamikus programkönyvtár mindaddig nem lesz felszabadítva, amíg a dlclose-t nem hívták meg annyiszor, ahányszor a dlopen-t. Így nem okoz problémát, ha ugyanaz a program ugyanazt a programkönyvtárat többször tölti be. Ha a programkönyvtár valóban felszabadul a régi programkönyvtárak esetén a _fini függvénye meghívódik (ha létezik). A _fini már egy elavult mechanizmus és nem ajánlatos a használata. Helyette a programkönyvtáraknak az __attribute__((constructor)) és __attribute__((destructor)) függvényattribútumukkal ellátott eljárásit kell használni. További részleteket a "Programkönyvtár konstruktor és destruktor függvények" fejezetben olvashatsz erről. Megjegyezzük, hogy a dlclose() 0-val tér vissza siker, és nem nullával hiba esetén. Néhány Linux kézikönyv oldal nem tesz említést erről.
Íme egy példa a dlopen(3) kézikönyvből. Ez a példa betölti a math programkönyvtárat és kiírja a 2.0 koszinuszát. Ellenőrzi a hibákat, minden lépésnél (ami melegen ajánlott):
#include <stdlib.h> #include <stdio.h> #include <dlfcn.h> int main(int argc, char **argv) { void *handle; double (*cosine)(double); char *error; handle = dlopen ("/lib/libm.so.6", RTLD_LAZY); if (!handle) { fputs (dlerror(), stderr); exit(1); } cosine = dlsym(handle, "cos"); if ((error = dlerror()) != NULL) { fputs(error, stderr); exit(1); } printf ("%f\n", (*cosine)(2.0)); dlclose(handle); } |
Ha ez a program a "foo.c" állományban van, akkor az alábbi paranccsal fordíthatod le:
gcc -o foo foo.c -ldl |
Előző | Tartalomjegyzék | Következő |
Megosztott programkönyvtárak | Egyéb |