Knihovna (programování)
Knihovna (anglicky library) je v informatice označení pro souhrn procedur a funkcí, často také konstant a datových typů (v objektovém programování též tříd, objektů a zdrojů), který může být využíván více počítačovými programy. Knihovny usnadňují programátorovi tvorbu aplikací tím, že umožňují využití hotového kódu, použití jednou vytvořeného kódu v jiných programech; při týmové práci mohou sloužit k dělbě práce. Knihovna poskytuje své služby navenek pomocí API (aplikační rozhraní), které zahrnuje názvy funkcí, předávané parametry a návratové hodnoty. Podle způsobu spojení s programem, který je používá, lze knihovny rozdělit na statické a dynamické. Z hlediska práce s kódem knihovny v operační paměti je dělíme na sdílené a nesdílené.
Historie
Nejstarší programové koncepty obdobné knihovnám byly vyvinuty, aby oddělily definici dat od realizace programu. Kolem roku 1959 jazyk JOVIAL zpopularizoval koncept COMPOOL (Communication Pool). „Záměrem konceptu COMPOOL bylo povolení sdílení systémových dat mezi více programy pomocí centralizovaného popisu dat.“[1]
V roce 1959 byly do jazyku COBOL zahrnuty „primitivní funkce knihovního systému“,[2] které Jean Sammet ve zpětném pohledu popsal jako „nedostatečná knihovní zařízení“.[3]
Další významné přispění k modernímu pojetí knihovny přišlo v podobě podprogramu, který byl součástí inovace jazyka FORTRAN. Podprogramy mohou být v jazyce FORTRAN kompilovány nezávisle na sobě, ale kompilátoru chybí linker. Takže před zavedením modulů v jazyce Fortran-90 nebyla mezi podprogramy možná typová kontrola.[4]
S významným konceptem přišla též Simula 67. Simula byl první objektově orientovaný programovací jazyk a jeho třídy jsou téměř totožné s moderním pojetím, které je používáno v jazyce Java, C++ nebo C#. Třídy konceptu Simula byly také předchůdcem balíčku v programovacím jazyku Ada a modulu v Modula-2.[5] Třídy Simula mohly být zahrnuty v knihovních souborech a přidány v době kompilace, přestože byly vyvinuty už v roce 1965.[6]
Typy knihoven
Knihovna je soubor, který obsahuje soubor funkcí a procedur v podobě strojového kódu, které jsou uloženy do společného souboru. Je tak vytvořen archiv, který je souhrnem objektových kódů a se kterým lze dále pracovat.
Z technického hlediska je možné rozdělit knihovny podle způsobu propojení s programem, který je bude využívat:
- statická knihovna (anglicky static linking library)
- dynamická knihovna (anglicky dynamic linking library)
Druhé možné rozdělení je z hlediska možnosti sdílení kódu knihovny mezi více běžícími programy (tj. procesy):
- sdílená knihovna (anglicky shared library)
- nesdílená knihovna
Statické knihovny
Statická knihovna tvoří s přeloženým programem kompaktní celek. V závěrečné fázi překladu programu ze zdrojového kódu do strojového kódu jsou volané funkce (a další jimi kaskádově volané funkce) připojovány linkerem (odtud označení linkování) do výsledného spustitelného souboru. V horším případě je k programu připojena celá knihovna bez ohledu na to, jaké části program skutečně využívá. Výsledný spustitelný soubor tak v sobě obsahuje všechny části, které jsou nutné pro jeho běh. Staticky slinkovaný spustitelný soubor proto typicky při spuštění nepotřebuje žádné další soubory, takže je ho možné překopírovat na jiný systém a jednoduše spustit (tj. bez instalace, tak, jak je známá například pro běžné programy v Microsoft Windows).
Typickou příponou souboru statické knihovny je .lib
nebo .a
. Přípona souboru s knihovnou závisí na použitém operačním systému, programovacím jazyku i linkeru. Staticky jde s programem slinkovat i dynamické knihovny (přípona .so
).
Dynamické knihovny
Dynamické knihovny nejsou (na rozdíl od statických knihoven) k výslednému spustitelnému souboru přidávány. V závěrečné fázi překladu programu ze zdrojového kódu do strojového kódu jsou odkazy na volané knihovní funkce pomocí linkeru (odtud označení linkování) zapsány do speciální tabulky symbolů, která je připojena k výslednému spustitelnému souboru. Pro chod programu (tj. spuštění výsledného spustitelného souboru) je pak nutné mít k dispozici též příslušné dynamické knihovny. Pokud dynamická knihovna využívá ke své činnosti jiné dynamické knihovny, vzniká řetězec závislostí a všechny potřebné knihovny musí být při spuštění programu přítomny.
V Microsoft Windows mají dynamické knihovny příponu .DLL
, v unixových systémech je používána přípona .o
nebo .so
(Linux). Dynamickou knihovnu je typicky možné slinkovat s programem i staticky (viz výše).
Zavaděč
Pro propojení dynamicky linkovaného programu a dynamických knihoven je při spuštění programu používán speciální zavaděč (anglicky dynamic loader). Do operační paměti je zaveden kód vlastního programu (spustitelný soubor) a též kód potřebných dynamických knihoven podle odkazů z připravené tabulky symbolů. Po rozmístění knihoven do operační paměti jsou vyčísleny odkazy z tabulky symbolů podle skutečného umístění jednotlivých částí kódu knihoven v operační paměti, čímž dojde k propojení kódu z dynamicky linkovaného spustitelného souboru s kódem dynamických knihoven.
Relokace
Knihovny jsou typicky předem připraveny tak, aby mohly být umístěny na konkrétní místo v paměti. Pokud je toto místo již obsazeno (např. jinou knihovnou), musí být provedena relokace na volné místo, která opraví příslušné absolutní adresy použité ve strojovém kódu (v Linuxu mohou být optimální umístění knihoven předem připravena programem prelink, v Microsoft Windows není podobný nástroj volně k dispozici).
Dynamic loading
Většina dnešních operačních systémů umožňuje načítat dynamické knihovny do paměti až za běhu programu, což umožňuje programy modularizovat a spouštět i v případě že při spuštění programu nejsou k dispozici všechny knihovny, které program používá (takže bude například jen omezena funkčnost programu). Toho se dá využít například k implementaci plug-inů.
Sdílené knihovny
Kód sdílených knihoven je možné sdílet mezi více programy, čímž jsou efektivně snižovány nároky na velikost potřebné operační paměti. V takovém případě se úsek paměti s kódem knihovních funkcí nalézá ve sdílené paměti, která je přístupná více procesům (je namapována do adresních prostorů všech procesů, které ji používají). Protože knihovny typicky pracují se svými interními daty, musí být možné zajistit každé knihovně privátní datový prostor pro data pro každý proces zvlášť. Přitom se s výhodou využívají možnosti virtuální paměti, jejíž podpora musí však být přítomna v procesoru.
Sdílet je možné jen identické knihovny. Z technického hlediska možné sdílet jen dynamické knihovny, protože jen u nich je zaručeno, že se vždy jedná o naprosto stejný kód (z principu není možné při startu programu porovnávat kód statický linkované knihovny s obsahem paměti ostatních spuštěných procesů). Nastavení sdílení paměťového prostoru se kódem sdílených dynamických knihoven zajišťuje při startu dynamicky linkovaného programu výše zmíněný dynamický zavaděč (anglicky dynamic loader).
Podpora v operačních systémech
Princip sdílených dynamických knihoven je propracován zejména v unixových systémech, kde již v dobách jeho vzniku umožňoval efektivní využití drahé a poměrně malé operační paměti (v 70. a 80. letech 20. století byla operační paměť tehdejších sálových počítačů veliká pouze desítky kilobajtů).
V systémech Microsoft Windows je sdílení DLL knihoven omezeno a od Windows Vista firma Microsoft doporučuje, aby každý program měl vlastní dynamické knihovny, protože ve Windows neexistuje centrální správa sdílených knihoven na úrovni systému. Tím je jejich sdílení v paměti fakticky znemožněno (i přesto, že mohou být některé identické) a proto prudce eskalovaly nároky na minimální velikost operační paměti. Již od počátku návrhu DLL knihoven nebylo maximální sdílení stejné knihovny různými procesy prioritním cílem, protože se nepředpokládalo, že na tehdejším dostupném hardware bude možné spouštět desítky až stovky programů současně (tím také byly sníženy paměťové nároky jdoucí na vrub možnosti sdílení knihovny mezi více procesy) a zároveň procesor Intel 8086 neposkytoval podporu virtuální paměti. Knihovny DLL se proto soustředily na maximalizaci úspory paměťového prostoru a na vytvoření společného paměťového prostoru knihoven a programu.
Správa sdílených knihoven
Pokud mají být dynamické knihovny sdíleny více spuštěnými programy (tj. více procesy), musí všechny tyto programy využít naprosto stejnou dynamickou knihovnu. Toho lze dosáhnout jedině tak, že všechny programy jako knihovnu použijí stejný soubor. Knihovny jsou proto soustředěny na centrální místo. V unixových systémech jsou to zejména základní adresáře /lib
a /usr/lib
, ale mohou to být i další adresáře, například /lib64
, /usr/local/lib
atd. (seznam doplňujících adresářů je v souboru /etc/ld.so.conf
, nově též v souborech obsažených v adresáři /etc/ld.so.conf.d/
).
Protože i knihovny obsahují chyby, jsou vydávány novější verze a navzájem jsou odlišeny čísly. Číslo je typicky uvedeno uvnitř knihovny. Někdy jsou změny v novější verzi knihovny větší a v systému je vhodné mít kromě starší verze knihovny pro dříve přeložené programy (resp. pro programy linkované proti starší knihovně) zároveň i novější verzi knihovny pro novější verze programů. Aby mohly být různé verze knihoven umístěny ve stejném adresáři, jsou v jejich názvu uvedena čísla verze (nemusí být nutně stejné, jako jsou dříve zmíněná čísla uvedená uvnitř knihoven). V názvech se typicky používají tři čísla: major, minor a patchlevel (hlavní číslo generace pro velké změny v rozhraní knihovny, vedlejší číslo pro odlišení kompatibility a poslední číslo označuje opravu chyb se zachováním kompatibility).
Pro zajištění pořádku v různých verzích knihoven a usnadnění nalezení nejvýhodnější verze je unixových systémech nástroj ldconfig, který projde všechny adresáře s knihovnami a vytvoří databázi všech dynamických knihoven do souboru /etc/ld.so.cache
, který je dále využíván zavaděčem (resp. jeho částí – dynamickým linkerem). Zároveň jsou vytvářeny symbolické odkazy, ve kterých název a zkrácené číslo verze, přičemž odkaz míří na nejnovější verzi knihovny (program si při spuštění vyžádá konkrétní verzi knihovny pomocí major a minor čísla a na patchlevel se typicky nehledí, protože knihovny lišící se pouze tímto číslem jsou navzájem kompatibilní).
Nejdůležitějším rysem správy dynamických sdílených knihoven je jejich údržba, kdy tvůrce systému musí vydávat opravené verze knihoven a zajišťovat, že nedojde ke ztrátě kompatibility (nebo je nekompatibilní knihovna označena jiným číslem verze). Tuto správu běžně dělají tvůrci linuxových distribucí. Naproti tomu firma Microsoft pro své systémy Microsoft Windows nikdy takovou údržbu nedělala. Dynamické knihovny (soubory DLL) nemají v názvu číslo verze, a proto novější verze knihovny přepíše starší verzi. Pokud je novější verze nekompatibilní (číslo verze je uvnitř DLL souboru), mají starší programy jiných tvůrců problém (Microsoft si kompatibilitu v rámci svých produktů může snadno zajistit). Tomuto neorganizovanému a nepředvídatelnému stavu se říká DLL hell (DLL peklo). Proto je v novějších systémech Windows doporučeno, aby každý program měl vlastní soubory s knihovnami, což však brání sdílení kódu knihoven v paměti pro více procesů. Tímto opatřením se samozřejmě snižují náklady na údržbu systému na straně tvůrce systému, ale ztěžuje se použití knihoven programátorům aplikací a zvyšují se nároky na velikost nutné operační paměti RAM (tj. přenáší se náklady na uživatele počítačů, protože knihovny se v paměti nesdílí, pro které je potřeba mnohem více operační paměti).
Srovnání statické a dynamické knihovny
- Statická knihovna se spojuje se spustitelným souborem v době linkování programu, zatímco dynamická knihovna se spojuje se spustitelným souborem v době spuštění programu nebo až za jeho běhu.
- Pokud se spustí několik programů, které využívají jednu statickou knihovnu, je tato knihovna v operační paměti opakovaně (v každé instanci programu). Kód dynamické knihovny naproti tomu může být v paměti sdílen všemi instancemi.
- Staticky slinkovaný program je lépe přenosný, stačí přenést jeden soubor. U dynamicky slinkovaného programu je nutno zajistit, aby na počítači byly nainstalovány správné verze požadovaných dynamických knihoven nebo při přenosu přidat správné verze jako další soubory. Toto pravidlo platí rekurzivně (dynamické knihovny mohou vyžadovat další dynamické knihovny).
- Pokud je chyba ve statické knihovně, je nutné po jejím opravení znovu sestavit všechny programy, které danou knihovnu používají. U dynamické knihovny stačí nahradit jediný soubor dynamické knihovny její novější (opravenou) verzí. Všechny programy používající tuto dynamickou pak automaticky pracují s opravenou knihovnou.
- Automatické použití novější verze knihovny lze využít i jindy než při opravě chyby, novější verze může obsahovat například optimalizace a v některých případech i novou funkčnost pro nové programy (za předpokladu, že knihovna zachová funkčnost starého rozhraní pro staré programy).
- Spuštění staticky slinkovaného programu typicky trvá o něco kratší dobu, protože při spouštění dynamicky linkovaného programu operační systém musí zajistit načtení dynamické knihovny do paměti zvlášť a provést relokaci, tedy nahradit odkazy na data v knihovně jejich výslednou adresou.
- Dynamicky slinkovaný program je výrazně menší. Pokud knihovnu používá více než jeden program, ušetříme tedy místo i na disku.
Návrh knihovny
Součástí návrhu většího softwarového projektu je obvykle i oddělení části funkčnosti do jedné nebo několika samostatných knihoven. Při návrhu knihovny se berou v úvahu především některé vlastnosti známé (na nižší úrovni) z objektově orientovaného programování:
- Zapouzdřenost – komplexní funkčnost knihovny by měla být dostupná přes jednoduché a snadno použitelné rozhraní. Na hotovou knihovnu se pak dá dívat jako na černou skříňku.
- Znovupoužitelnost – dobře navržená knihovna může dobře posloužit při vývoji budoucích projektů, kdy bude potřeba stejná nebo podobná funkčnost.
Dynamické knihovny v platformě Linux/Unix
Dynamické knihovny jsou v platformě Linux a Unix označované jako sdílené objekty. Sdílený objekt (ang.Shared object), také označován jako je soubor, který je určen ke sdílení spustitelnými soubory (programy) nebo jinými sdílenými objekty. Jedná se o unixovou/Linuxovou obdobu DLL knihoven z operačního systému Windows. Z toto objektu jsou při startu programu nebo během jeho chodu načítány do paměti systému jednotlivé rutiny knihovny, které jsou využívány za běhu programu. Díky tomu nemusí linker při kompilaci programu tyto metody zakomponovávat přímo do programů a vytvářet tak ohromné monolitické programy.
Dynamické knihovny mohou být použity také jako statické knihovny a je možné je do programu linkovat staticky při kompilaci programu. Toto ale není doporučováno, častěji se připojují až za běhu programu.
Většina moderních operačních systémů může mít soubory sdílené knihovny ve stejném formátu jako spustitelné soubory. To nabízí dvě hlavní výhody. za prvé, místo dvou loaderů pro každý typ zvlášť stačí jen jeden loader pro oba. Za druhé, umožňuje spustitelné také použít jako knihovny, pokud mají tabulku symbolů. Typické kombinace spustitelný soubor a knihovny jsou formáty ELF a Mach-O (oba Unix) a PE (Windows).
Vytvoření SO pomocí GCC
Postup vytvoření sdílené knihovny probíhá v několika krocích. Následující příklad objasňuje symbolicky celý postup. Zdrojový kód knihovny je uložen v souborech prvni.c
a druhy.c
, ze kterých má být vytvořena knihovna (typu ELF). Postup vytvoření sdílené knihovny bude pak následující:
$ gcc -fPIC -g -c -Wall prvni.c
$ gcc -fPIC -g -c -Wall druhy.c
$ gcc -shared -Wl,-soname,libMyMethods.so.1 -o libMyMethods.so.1.0.1 prvni.o druhy.o
První dva příkazy přeloží díky přepínači -c
soubory prvni.c
a druhy.c
do podoby objektového kódu (tj. do souborů prvni.o
a druhy.o
, které jsou určitou formou polotovaru, protože se nepoužije linker). Přepínač -fPIC
zapne vytvoření pozičně nezávislého kódu. Přepínač -g
zapne generování ladících informací, které jsou umístěny do výsledného kódu. Přepínač -Wall
zapne výpis všech varování při překladu.
Poslední příkaz vytvoří vlastní knihovnu (přepínačem -shared
) jako soubor libMyMethods.so.1.0.1
, který je dále využitelný jako dynamická knihovna v jiných programech. Verze knihovny (tj. interní označení), které indikuje verzi API knihovny, je nastaveno na libMyMethods.so.1
. Při překladu jiného programu může být knihovna nabídnuta linkeru přepínačem -lMyMethods
. Pro programování v jazyce C bude ke knihovně nutné dodat hlavičkový soubor s názvem libMyMethods.h
, který bude definovat prototypy v knihovně obsažených funkcí.
Příklady různých knihoven
- standardní knihovna jazyka C
- standardní knihovna šablon (Standard Template Library) v jazyce C++
- grafické knihovny jako DirectX, OpenGL, SDL, Swing apod.
- matematická knihovna LINPACK pro řešení soustav lineárních rovnic
Reference
- ↑ WEXELBLAT, Richard. History of Programming Languages. [s.l.]: Academic Press (A subsidiary of Harcourt Brace), 1981. (ACM Monograph Series). Dostupné online. ISBN 0-12-745040-8. S. 369.
- ↑ Wexelblat, op. cit., p. 274
- ↑ Wexelblat, op. cit., p. 258
- ↑ WILSON, Leslie B.; CLARK, Robert G. Comparative Programming Languages. [s.l.]: Addison-Wesley, 1988. ISBN 0-201-18483-4. S. 126.
- ↑ Wilson and Clark, op. cit., p. 52
- ↑ Wexelblat, op. cit., p. 716
Externí odkazy
- Obrázky, zvuky či videa k tématu knihovna na Wikimedia Commons