9. díl - Céčko a Linux - Statické a dynamické knihovny

C++ Linux Céčko a Linux - Statické a dynamické knihovny

Dnes se podíváme na tvorbu a použití externích knihoven. Co to vůbec je? Knihovna je, jednoduše řečeno, sbírka funkcí, které můžeme volat z našeho programu.

Jistě znáte standardní knihovnu C (např. stdio) a nejspíš víte, že je možné tvořit knihovny vlastní. S největší pravděpodobností jste již i někdy nějakou vytvořili. Přesto bych se na dané téma chtěl dnes podívat blíže a je pravděpodobné, že se dozvíte i něco nového. :)

Vytvoříme si velmi jednoduchou knihovnu, která bude obsahovat pouze dvě funkce.

libvypocet.c

#include "libvypocet.h"

static int mezivypocet(int a);

int vypocet(int a, int b)
{
  return mezivypocet(a) + b;
}

int mezivypocet(int a)
{
  return a*a;
}

Následně k ní uděláme rozhraní (hlavičkový soubor), kde budou definovány všechny funkce, které je možné zavolat z jiných částí programu.

libvypocet.h

extern int vypocet(int a, int b);

Všimněte si funkce mezivypocet, která v hlavičce zapsána není - tuto funkci můžeme volat pouze v rámci naší knihovny. Za zmínku stojí také řádka static int mezivypocet(int a);. Té se říká úplný funkční vzor (pokud se pletu, prosím opravit) a je dobrým zvykem je napsat na začátek souboru pro každou funkci. Umožňují totiž volat z funkcí funkce, jejichž definice je až za funkcí, ze které se volá. V zásadě říká překladači, co funkce vrací a jaké má parametry. Ten to pak může zkontrolovat. ;)

Funkční vzory veřených funkcí jsou vypsány v hlavičkovém souboru a jeho vložením na začátek naší knihovny si vlastně ušetříme psaní.

Zde je náš jednoduchý program, který jen zavolá funkci z naší knihovny a vypíše výsledek na stdout.

main.c

#include <stdio.h>

#include "libvypocet.h"

int main()
{
  printf("Vysledek vypoctu z knihovny: %d\n", vypocet(5, 5));
  return 0;
}

Teď si ukážeme, jak můžeme náš program zkompilovat, aby použil námi vytvořenou knihovnu. Je nutné říct, že existují dva druhy knihoven - statické a dynamické.

Statické knihovny

Statické knihovny nejspíš znáte. Je to velmi jednoduchý způsob, jak rozdělit program do více modulů a zpřehlednit tak kód. Často se používá u projektů, kde spolupracuje více programátorů - každý udělá jeden modul (knihovnu), otestuje ho a nadefinuje rozhraní. Moduly se pak jen spojí a aplikace je hotová.

Z technického hlediska je statická knihovna přímo součást binárky. S každou úpravou knihovny musíme program znovu sestavit.

Samotná posloupnost příkazů je pro statické knihovny velmi jednoduchá:

$ gcc -c libvypocet.c     # přeložení knihovny
$ gcc -c main.c           # přeložení programu
$ gcc libvypocet.o main.o -o prog    # sestavení binárky

Program pak můžeme jednoduše spouštět a potřebujeme jen námi přeloženou binárku. Nevýhodou tohoto přístupu je větší velikost programů a také pracná změna v knihovně - nutnost nového sestavení programu. Tyto nevýhody jsou zanedbatelné, pokud knihovnu používáme jen v jednom programu - v takovém případě je použití statických knihoven rozhodně vhodné.

Dynamické knihovny

Problém nastává, když máme knihovnu, kterou používá velké množství aplikací. Zaprvé bude zabírat více místa - velikost knihovny * počet aplikací, které ji používají. Na disku to obvykle není problém. Z hlediska RAM už ale ano. Mnohem závažnější je ale otázka bezpečnosti. Aktualizovat knihovnu po objevení bezpečnostní díry není problém. Pokud bychom ale zároveň museli znovu zkompilovat 100 aplikací, nastává hodně velký problém..

Řešením obou problémů jsou dynamické knihovny. Ty nejsou přímo v programu, ale načítají se až když jsou potřeba a to většinou ze systémové složky lib (dnes většinou usr/lib). Aktualizace knihovny tedy spočívá v přepsání jednoho souboru. Navíc se naše knihovna do paměti načte jen jednou a všechny programy budou přistupovat k té stejné kopii. To vyřeší nejen problém s místem, ale také může přístup k ní urychlit. Programy ji nemusí při každém spuštění načítat a pokud je často používaná, určitě se vyskytne i v cache procesoru.

Dynamické knihovny je nutné překládat jiným způsobem. Nejdříve vytvoříme samotnou knihovnu:

$ gcc -fPIC -c libvypocet.c
$ gcc -shared libvypocet.o -o libvypocet.so

Přepínač -fPIC říká překladači, že má vytvořit tzv. "Position Independent Code" - tedy kód nezávislý na svém umístění. V praxi to znamená, že kód nebude obsahovat konkrétní adresy v paměti (pro funkce a proměné), které teď ani není možné zjistit.

Když máme objektový kód, tak už jen pomocí -shared překladači sdělíme, že chceme dynamickou (sdílenou) knihovnu. Je dobré dodržovat zavedené zvyklosti - jméno knihovny začíná lib a přípona je .so (Windows používá příponu .dll).

$ gcc -c main.c
$ gcc main.o -lvypocet -L. -o prog

U sestavování programu pak musíme překladači říct, že chceme přilinkovat sdílenou knihovnu vypocet (podobně jako používáme -lm). Kromě toho ještě použijeme přepínač -L., který umožňuje hledání této knihovny i v adresáři s naším kódem.

Program zkusíme spustit.

$ ./prog
./prog: error while loading shared libraries: libvypocet.so: cannot open shared object file: No such file or directory

Ten nám ale zahlásí, že nemůže najít knihovnu.. Jak to vyřešit si povíme hned. Nejdřív bych ale chtěl zmínit program ldd, který zjistí, které knihovny program potřebuje a pokusí se je najít.

$ ldd prog
        linux-vdso.so.1 =>  (0x00007ffd2bf3a000)
        libvypocet.so => not found
        libc.so.6 => /lib64/libc.so.6 (0x00007f8e3b2e1000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f8e3b69e000)

Vidíme, že naše knihovna v systému opravdu nikde není. Máme ji sice ve složce s programem, ale z důvodu bezpečnosti tam systém hledat nebude (nebylo by pak těžké, aby nám třeba někdo podstrčil knihovnu se škodlivým kódem).

Teď máme několik možností, jak naši knihovnu zpřístupnit programům. Jedna z nich je přidat cestu do /etc/ld.so.cong (konfigurační soubor programu, které dynamické knihovny načítá). Další, podstatně jednodušší možností je použít proměnnou prostředí LD_LIBRARY_PATH, do které můžeme přidat cestu k naší knihovně.

$ export LD_LIBRARY_PATH=`pwd`

Program se pak bez problému spustí. Nejjednodušší a nejlepší řešení je prostě naši knihovnu "nainstalovat" - tedy zkopírovat do /usr/lib. Po tom, co to uděláme, musíme ještě spustit program ldconfig, aby se obnovila cache dostupných knihoven.

$ sudo cp libvypocet.so /usr/lib
$ sudo ldconfig

Dalším efektem tohoto kroku je, že již nemusíme použít přepínač -L. při sestavování programů, které naši knihovnu využívají - gcc si ji jednoduše najde ve standardním umístění.

$ gcc -o prog -lvypocet main.o

Některé možná napadlo, jestli by nešel podobně zpřístupnit i hlavičkový soubor naší knihovny. Odpověď je samozřejmě ano.

$ sudo cp libvypocet.h /usr/include

Teď se naše knihovna chová jako jakákoliv jiná sdílená systémová knihovna. Náš progam můžeme upravit následovně:

#include <stdio.h>
#include <libvypocet.h>

int main()
{
  printf("Vysledek vypoctu z knihovny: %d\n", vypocet(5, 5));
  return 0;
}

Pokud bychom chtěli naši knihovnu zase odinstalovat, je to opravdu velmi jednoduché:

$ sudo rm /usr/include/libvypocet.h
$ sudo rm /usr/lib/libvypocet.so

Celá ukázka je dole ke stažení včetně ukázky jednoduchého Makefilu, který program přeloží a nainstaluje. Pokud máte někdo jakékoliv otázky nebo připomínky, určitě je napište do komentářů. :)

Příště se podíváme na GNU Build System, který udělá spoustu věcí při tvorbě balíčku k distribuci za nás.


 

Stáhnout

Staženo 6x (3 kB)
Aplikace je včetně zdrojových kódů v jazyce C

 

  Aktivity (1)

Článek pro vás napsal David Novák
Avatar
Autor v současné době studuje FIT VUT Brno a zajímá se především o nízkoúrovňové programování (C/C++, ASM) a návrh hardwaru (VHDL). Je zde také členem výzkumného týmu ANT@FIT (Accelerated Network Technologies).

Jak se ti líbí článek?
Celkem (1 hlasů) :
4444 4


 


Miniatura
Všechny články v sekci
Programování v jazyce C v Linuxu

 

 

Komentáře

Avatar
Filip Šohajek
Redaktor
Avatar
Filip Šohajek:

TIP: Při překládání shared knihoven buďte ve složce s .o soubory. Nevím jestli je to jen bug mé verze kompilátoru, ale potom bude hlásit podivné errory.

 
Odpovědět 25.6.2015 12:55
Avatar
David Novák
Tým ITnetwork
Avatar
Odpovídá na Filip Šohajek
David Novák:

Jestli chceš, můžeš mi poslat ukázku, kdy se ti to stalo.. To bude nějaká blbost ;)

V této ukázce zatím nebyla třeba žádná adresářová struktura, ale v budoucnu se k tomu dostanu..

Odpovědět 25.6.2015 12:59
Chyba je mezi klávesnicí a židlí.
Avatar
Filip Šohajek
Redaktor
Avatar
Odpovídá na David Novák
Filip Šohajek:

Už si přesně nepamatuju, ten Makefile už nemám, ale bylo to něco na způsob toho, že *.o byly ve složce build a shared knihovna se generovala do složky out.

 
Odpovědět 25.6.2015 13:19
Avatar
David Novák
Tým ITnetwork
Avatar
Odpovídá na Filip Šohajek
David Novák:

Tam byla nejspíš nějaká chyba v cestách.. Jinak by to určitě mělo fungovat ;)

Ale jak říkáš - linkovat ve stejné složce, jako jsou objektové soubory, je jistota.

Odpovědět 25.6.2015 13:23
Chyba je mezi klávesnicí a židlí.
Avatar
lastp
Redaktor
Avatar
lastp:

Soubor s příponou .o se nazývá object file a je výsledkem kompilace jednoho C souboru. Statické knihovny mají příponu .a a obvykle vznikají kompilací několika C souborů. Object file se zpravidla používá jen v rámci jedné aplikace, zatímco statické knihovny se využívají ke kompilaci více různých aplikací.

 
Odpovědět  +2 1.7.2015 10:01
Avatar
Martin Dráb
Redaktor
Avatar
Odpovídá na lastp
Martin Dráb:

Soubor s příponou .o se nazývá object file a je výsledkem kompilace jednoho C souboru. Statické knihovny mají příponu .a a obvykle vznikají kompilací několika C souborů. Object file se zpravidla používá jen v rámci jedné aplikace, zatímco statické knihovny se využívají ke kompilaci více různých aplikací.

Pokud vím, tak ten .a soubor je jenom jakýsi archív obsahující všechny "objektové" soubory dané statické knihovny. Minimálně je to jedna z možností.

Odpovědět  +1 1.7.2015 11:21
2 + 2 = 5 for extremely large values of 2
Avatar
David Novák
Tým ITnetwork
Avatar
Odpovídá na lastp
David Novák:

Statické knihovny se už pro více aplikací příliš nepoužívají - důvody jsou zmíněné v článku.

S objektovými soubory nemáš pravdu - můžou být i produktem kompilace více .c souborů ;)
Jinak přesněji - objektový soubor je typicky binární kód před linkováním - tj. jsou v něm stále symboly (např. jména proměnných, funkcí). Při linkování (sestavení) do spustitelného souboru jsou jednotlivé objektové soubory spojeny a symboly zaměněny za konkrétní adresy.

Odpovědět 1.7.2015 13:33
Chyba je mezi klávesnicí a židlí.
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 7 zpráv z 7.