IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

9. diel - Céčko a Linux - Statické a dynamické knižnice

V minulej lekcii, Céčko a Linux - getopt_long a shell , sme dokončili filtre. Dnes sa pozrieme na tvorbu a použitie externých knižníc.

Knižnica

Čo to vôbec je? Knižnica je, jednoducho povedané, zbierka funkcií, ktoré môžeme volať z nášho programu.

Iste poznáte štandardné knižnicu C (napr. Štádiách) a najskôr viete, že je možné tvoriť knižnice vlastné. S najväčšou pravdepodobnosťou ste už aj niekedy nejakú vytvorili. Napriek tomu by som sa na danú tému chcel dnes pozrieť bližšie a je pravdepodobné, že sa dozviete aj niečo nové. :)

Vytvoríme si veľmi jednoduchú knižnicu, ktorá bude obsahovať iba dve funkcie.

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ásledne k nej urobíme rozhrania (hlavičkový súbor), kde budú definované všetky funkcie, ktoré je možné zavolať z iných častí programu.

libvypocet.h

extern int vypocet(int a, int b);

Všimnite si funkcie mezivypocet, ktorá v hlavičke zapísaná nie je - túto funkciu môžeme volať iba v rámci našej knižnice. Za zmienku stojí tiež riadok static int mezivypocet(int a);. Tej sa hovorí úplný funkčný vzor (ak sa mýlim, prosím opraviť) a je dobrým zvykom je napísať na začiatok súboru pre každú funkciu. Umožňujú totiž volať z funkcií funkcie, ktorých definícia je až za funkcií, z ktorej sa volá. V zásade hovorí prekladači, čo funkcia vracia a aké má parametre. Ten to potom môže skontrolovať. ;)

Funkčné vzory poverených funkcií sú vypísané v hlavičkovom súbore a jeho vložením na začiatok našej knižnice si vlastne ušetríme písania.

Tu je náš jednoduchý program, ktorý len zavolá funkciu z našej knižnice a vypíše výsledok na stdout.

main.c

#include <stdio.h>

#include "libvypocet.h"

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

Teraz si ukážeme, ako môžeme náš program skompilovať, aby použil nami vytvorenú knižnicu. Je nutné povedať, že existujú dva druhy knižníc - statické a dynamické.

Statické knižnice

Statické knižnice zrejme poznáte. Je to veľmi jednoduchý spôsob, ako rozdeliť program do viacerých modulov a sprehľadniť tak kód. Často sa používa pri projektoch, kde spolupracuje viac programátorov - každý urobí jeden modul (knižnicu), otestuje ho a nadefinuje rozhrania. Moduly sa potom len spoja a aplikácie je hotová.

Z technického hľadiska je statická knižnica priamo súčasť binárky. S každou úpravou knižnice musíme program znovu zostaviť.

Samotná postupnosť príkazov je pre statické knižnice veľmi 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 potom môžeme jednoducho spúšťať a potrebujeme len nami preloženú binárku. Nevýhodou tohto prístupu je väčšia veľkosť programov a tiež prácna zmena v knižnici - potrebu nového zostavenie programu. Tieto nevýhody sú zanedbateľné, ak knižnicu používame len v jednom programe - v takom prípade je použitie statických knižníc rozhodne vhodné.

Dynamické knižnice

Problém nastáva, keď máme knižnicu, ktorú používa veľké množstvo aplikácií. Po prvé bude zaberať viac miesta - veľkosť knižnice * počet aplikácií, ktoré ju používajú. Na disku to zvyčajne nie je problém. Z hľadiska RAM už ale áno. Oveľa závažnejšie je ale otázka bezpečnosti. Aktualizovať knižnicu po objavení bezpečnostné diery nie je problém. Ak by sme ale zároveň museli znova skompilovať 100 aplikácií, nastáva veľa veľký problém ..

Riešením oboch problémov sú dynamické knižnice. Tie nie sú priamo v programe, ale načítavajú sa až keď sú potrebné a to väčšinou zo systémovej zložky lib (dnes väčšinou usr / lib). Aktualizácia knižnice teda spočíva v prepísanie jedného súboru. Navyše sa naša knižnica do pamäte načíta len raz a všetky programy budú pristupovať k tej rovnaké kópiu. To vyrieši nielen problém s miestom, ale tiež môže prístup k nej urýchliť. Programy ju nemusí pri každom spustení načítať a ak je často používaná, určite sa vyskytne aj v cache procesora.

Dynamické knižnice je potrebné prekladať iným spôsobom. Najskôr vytvoríme samotnú knižnicu:

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

Prepínač -fPIC hovorí prekladači, že má vytvoriť tzv. "Position Independent Code" - teda kód nezávislý na svojom umiestnení. V praxi to znamená, že kód nebude obsahovať konkrétne adresy v pamäti (pre funkcie a premien), ktoré teraz ani nie je možné zistiť.

Keď máme objektový kód, tak už len pomocou -shared prekladači oznámime, že chceme dynamickú (zdieľanú) knižnicu. Je dobré dodržiavať zavedené zvyklosti - meno knižnice začína lib a prípona je .so (Windows používa príponu .dll).

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

U zostavovaní programu potom musíme prekladači povedať, že chceme prilinkovať zdieľanú knižnicu vypocet (podobne ako používame -lm). Okrem toho ešte použijeme prepínač -L., Ktorý umožňuje hľadanie tejto knižnice aj v adresári s naším kódom.

Program skúsime spustiť.

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

Ten nám ale zahlási, že nemôže nájsť knižnicu .. Ako to vyriešiť si povieme hneď. Najskôr by som ale chcel spomenúť program ldd, ktorý zistí, ktoré knižnice program potrebuje a pokúsi sa ich nájsť.

$ 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ša knižnica v systéme naozaj nikde nie je. Máme ju síce v priečinku s programom, ale z dôvodu bezpečnosti tam systém hľadať nebude (nebolo by potom ťažké, aby nám treba niekto podstrčil knižnicu so škodlivým kódom).

Teraz máme niekoľko možností, ako našu knižnicu sprístupniť programom. Jedna z nich je pridať cestu do /etc/ld.so.cong (konfiguračný súbor programu, ktoré dynamické knižnice načítava). Ďalšie, podstatne jednoduchšie možnosťou je použiť premennú prostredia LD_LIBRARY_PATH, do ktorej môžeme pridať cestu k našej knižnici.

$ export LD_LIBRARY_PATH=`pwd`

Program sa potom bez problému spustí. Najjednoduchšie a najlepšie riešenie je jednoducho našu knižnicu "nainštalovať" - teda skopírovať do /usr/lib. Po tom, čo to urobíme, musíme ešte spustiť program ldconfig, aby sa obnovila cache dostupných knižníc.

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

Ďalším efektom tohto kroku je, že už nemusíme použiť prepínač -L. pri zostavovaní programov, ktoré našu knižnicu využívajú - gcc si ju jednoducho nájde v štandardnom umiestnení.

$ gcc -o prog -lvypocet main.o

Niektoré možno napadlo, či by nešiel podobne sprístupniť aj hlavičkový súbor našej knižnice. Odpoveď je samozrejme áno.

$ sudo cp libvypocet.h /usr/include

Teraz sa naša knižnica chová ako akákoľvek iná zdieľaná systémová knižnica. Náš Progam môžeme upraviť nasledovne:

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

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

Ak by sme chceli našu knižnicu zase odinštalovať, je to naozaj veľmi jednoduché:

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

Celá ukážka je dole k stiahnutiu vrátane ukážky jednoduchého Makefile, ktorý program preloží a nainštaluje. Ak máte niekto akékoľvek otázky alebo pripomienky, určite je napíšte do komentárov. :)


 

Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.

Stiahnuť

Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami

Stiahnuté 46x (3 kB)
Aplikácia je vrátane zdrojových kódov v jazyku C

 

Predchádzajúci článok
Céčko a Linux - getopt_long a shell
Všetky články v sekcii
Linux a programovanie v jazyku C
Článok pre vás napísal David Novák
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Autor se zajímá především o nízkoúrovňové programování (C/C++, ASM) a návrh hardwaru (VHDL).
Aktivity