Книжарниците са прост и многофункционален начин за модулиране на и повторно използване на код. Тази публикация разглежда процеса на създаване на статични и динамични библиотеки, възможните им употреби и минималните ресурси, необходими за правилното им управление.

Библиотеките се използват като цяло, за да се избегне пренаписване на кода (или копиране и поставяне). Ще спестим време за компилиране всеки път, когато кодът вече е компилиран. Освен това вече знаем, че докато правим програма, тестваме и коригираме, трябва да компилираме между много и „още много“ пъти.
Вече компилираният код ще бъде тестван и надежден, за да бъде използван толкова пъти колкото е необходимо.

За да накараме нашия код да работи в библиотека, трябва да го организираме по следния начин:

  • Един или повече .c изходни файлове с кода на нашите функции.
  • Един или повече .h заглавни файлове с типовете (typedefs, structs и enum) и прототипите на функциите, които искаме да използваме.
holberton.h
#ifndef _LIBRARIES_1_H
#define _LIBRARIES_1_H
int add(int a, int b);
int sub(int a, int b);
#endif

В друг файл създаваме източниците:

libraries.c
int add(int a, int b)
{
    return a + b;
}
int sub(int a, int b)
{
   return a-b;
}

Това е файл с няколко прости функции за събиране () и изваждане ().

В Linux можем да създадем два типа библиотеки: статични и динамични.

Статичната библиотека е библиотека, която „копира“ в нашата програма, когато я компилираме. След като имаме изпълнимия файл на нашата програма, библиотеката е безполезна (тя е полезна за други бъдещи проекти). Можем да го изтрием и нашата програма ще продължи да работи, тъй като има копие на всичко необходимо. Копира се само тази част от библиотеката, която е необходима.

Например, ако библиотеката има две функции и нашата програма извиква само една, само тази функция се копира.

Динамична библиотека не се копира в нашата програма при компилиране. Когато имаме нашия изпълним файл и го изпълняваме, всеки път, когато кодът се нуждае от нещо от библиотеката, той ще го търси. Ако изтрием библиотеката, нашата програма ще даде грешка, че не може да я намери.

Една от най-известните разлики между статичните и динамичните библиотеки е

  • Ако променим статична библиотека, изпълнимите файлове не се засягат, а ако променим динамична, изпълнимите файлове се засягат.

Предимства и недостатъци на всеки от тези типове библиотеки:

  • Програма, компилирана със статични библиотеки, е по-голяма, тъй като всичко, от което се нуждаете, се копира.
  • Програма, компилирана със статични библиотеки, може да бъде пренесена на друг компютър, без да се налага да вземате библиотеките.
  • Програма, компилирана със статични библиотеки, по принцип се изпълнява по-бързо. Когато извикате функция на библиотека, вие я имате във вашия код и не е нужно да четете файла на динамичната библиотека, за да намерите функцията и да я изпълните.

Създаване на статична библиотека

Има файлове fich1.c и fich2.c, които съдържат кода на функциите, които искаме да включим в библиотеката libfich.a. За да получим обектните файлове, компилираме по следния начин:

$ gcc -c -o fich1.o fich1.c
$ gcc -c -o fich2.o fich2.c

След това пакетираме получените файлове с ar.

$ ar rcs libfich.a fich1.o fich2.o

Използване на статична библиотека

За да използвате функциите и символите, съдържащи се в статична библиотека, е необходимо да свържете тези символи, когато се монтира изпълним файл. За да направим това, трябва да кажем на компилатора какви библиотеки искаме да използваме и къде се намират.

Ако сме написали програма apli1.c, която използва библиотеката, която създадохме в предишната точка, и искаме да я компилираме, трябва да направим следното:

  1. Компилирайте програмата:
$ gcc -c -o apli1.o apli1.c

2. Програмата се качва с библиотеката, посочваща къде се намира и как се казва:

$ gcc -o apli1 apli1.o -Ldir_lib -lfich

Съображения:

  • Примерът предполага, че библиотеката и нейният интерфейсен файл са в директория, наречена dir_lib.
  • Опцията -I се използва за указване къде се намират интерфейсните файлове.
  • Опцията -L се използва за указване на директорията, където се намира библиотеката.
  • Опцията -l е да посочи имената на библиотеките, които ще се използват, но нито префиксът lib, нито разширението .a трябва да бъдат написани, тъй като компилаторът очаква гореспоменатото именуване правила, които трябва да се спазват.

Създаване на динамична библиотека

Когато създавате динамичен обект, е необходимо споменатият обектен код да бъде независим от позицията, за да получите този тип код опцията -fPIC (Независим от позиция код) трябва да се посочи на компилатора. Този флаг трябва да бъде посочен както в компилацията, така и в асемблирането на библиотеката.

$ gcc -fPIC -c -o fich1.o fich1.c
$ gcc -fPIC -c -o fich2.o fich2.c

За да монтирате обектите, също е необходимо да посочите опцията -shared, така че резултатът да бъде обектен файл „споделяем“.

$ gcc -shared -fPIC -o libfich.so fich1.o fich2.o

В този случай библиотеката има разширение .so, което означава споделен обект.

Използване на динамична библиотека

За да използвате тази библиотека от програма, не е нужно да правите нищо допълнително; Това е точно същото като в случая със статичната библиотека.

Когато използва библиотека, компилаторът първо търси динамична версия (.so), ако не я намери, търси статичната версия. Ако и двете версии на библиотека са налични и трябва да се използва статичната версия, флагът -static трябва да бъде указан на асемблера.

Когато една програма използва динамични библиотеки, системата трябва да ги открие по време на изпълнение (за разлика от статичните библиотеки). Местата, където програмата търси динамични библиотеки, са следните (в този ред):

  • В директориите на променливата LD_LIBRARY_PATH.
  • Във файла ld.so.cache.
  • В директориите / usr / lib и / lib.
  • В директориите, съдържащи се във файла ld.so.conf.

Пример

Имаме динамична библиотека, наречена libhola.so, компилирана от следния код:

#include <stdio.h>
void rehola(int i)
{
   printf("Hola mundo por %d vez.\n", i);
}

И имаме файл със следния код, наречен apli3.c:

#include <stdio.h>
#include <dlfcn.h>
#define RUTA_LIBHOLA "/home/user/libhola.so"
int main(){
   int i = 3;
   void *pLibHola;                       
   void (*pFuncion)(int);                
   if ((pLibHola = dlopen(RUTA_LIBHOLA, RTLD_LAZY)) ==NULL) {
      fprintf(stderr, dlerror());
      return 1;
   }
   if ((pFuncion = dlsym(pLibHola, "rehola")) == NULL) {
      fprintf(stderr, dlerror());
      return 1;
   }
   if ((pFuncion = dlsym(pLibHola, "rehola")) == NULL) {
      fprintf(stderr, dlerror());
      return 1;
   }
   pFuncion(i);
   dlclose(pLibHola);
   return(0);
}

Този код е просто компилиран с:

$ gcc -o apli3 apli3.c -ldl

Опцията -ldl е необходима, за да можете да използвате библиотеката libdl.so, която е отговорна за внедряването на динамични връзки, но в нито един момент има ли препратки към библиотеката libhola, само където се намира.

Ако стартираме програмата, виждаме следното:

$ ./apli3
   Hola mundo por 3 vez.