Menu
Jest wolny
rejestracja
Dom  /  Internet/ Ilustrowany samouczek dotyczący Macromedia Flash MX. Biblioteka współdzielona Flash soname

Ilustrowany samouczek dotyczący Macromedia Flash MX. Biblioteka współdzielona Flash soname

Wcześniej wspomnieliśmy o bibliotekach współdzielonych jako o jednej z zalet menedżerów stronicowania i segmentacji pamięci nad menedżerami podstawowymi i bankowymi. W podstawowym adresowaniu obraz każdego procesu musi zajmować ciągłe obszary zarówno w fizycznej, jak i logicznej przestrzeni adresowej. W tych warunkach nie jest możliwe wdrożenie biblioteki współdzielonej. Ale nawet w przypadku korzystania z adresowania stron sprawy nie są takie proste.

Użycie bibliotek współdzielonych i/lub bibliotek DLL (w tym przypadku różnica między nimi nie jest zasadnicza) zakłada jakąś formę asemblacji w momencie ładowania: moduł wykonywalny ma nierozwiązane referencje adresowe i nazwy bibliotek, których potrzebuje. Po załadowaniu te biblioteki są ładowane, a łącza są rozwiązywane. Problem polega na tym, że podczas ładowania biblioteki należy ją przenieść poprzez rekonfigurację odwołań do adresów bezwzględnych w jej kodzie i danych (patrz rozdział 3). Jeśli w różnych procesach biblioteka jest skonfigurowana pod różne adresy, nie będzie już udostępniana (rys. 5.14)! Jeśli biblioteki współdzielone mogą mieć nierozwiązane łącza do innych bibliotek, problem pogłębia się tylko po dodaniu łączy zewnętrznych do łączy relokowalnych.

Ryż. 5.14... Konfliktowe adresy mapowania DLL

W starszych systemach uniksowych, które wykorzystywały bezwzględnie ładowalne moduły tego formatu a.out Biblioteki współdzielone były również dostarczane w formacie modułu absolutnego, ustawionego na stałe adresy. Każda biblioteka ma ustawiony adres ITS. Dostawca nowych bibliotek musiał uzgodnić ten adres z twórcami systemu. Było to bardzo niepraktyczne, więc było bardzo mało bibliotek współdzielonych (szczególnie poza tymi, które były dostarczane z systemem operacyjnym).

Bardziej akceptowalne rozwiązanie tego problemu jest zaimplementowane w OS/2 2.xi Win32 (obie te architektury są rozwojem systemów z pojedynczą przestrzenią adresową). Pomysł polega na przydzieleniu obszaru adresowego do ładowania bibliotek DLL i odwzorowaniu tego obszaru na przestrzenie adresowe wszystkich procesów. W ten sposób wszystkie biblioteki DLL załadowane do systemu są widoczne dla wszystkich (rys. 5.15).

Duże programy często wymagają gotowych bibliotek. Biblioteka to zbiór już skompilowanych funkcji (na przykład biblioteka matematyczna). Biblioteka jest wymagana podczas łączenia, gdy generowany jest exe-shnik. Odbywa się to za pomocą opcji -l: cc -o badmath badmath.o -lm Jeśli musisz określić ścieżkę do niestandardowej biblioteki: cc -o badmath badmath.o -lm -L / usr / śmieci / lib -lcrud Jeśli nazwa biblioteki kończy się na .a, to jest to biblioteka statyczna. Kiedy biblioteka statyczna jest połączona, linker po prostu kopiuje z niej całe fragmenty do wygenerowanego pliku exe, który w rezultacie może działać autonomicznie bez niej. Jeśli exe-shnik używa biblioteki współdzielonej, kod jest ładowany tylko w razie potrzeby. Biblioteki zwykle znajdują się w katalogach /lib i /usr/lib. W katalogu / lib nie powinno być żadnych bibliotek statycznych. Nazwa biblioteki współdzielonej zawiera przedrostek .so. Aby określić, których bibliotek używa program, musisz uruchomić polecenie: ldd prog Program ładujący ld.so ładuje współdzielone biblioteki. Ścieżka do biblioteki może leżeć wewnątrz exe-shnik. Jeśli go tam nie ma, program ładujący sprawdza plik /etc/ld.so.cache, który zawiera listę katalogów. Istnieje również plik /etc/ld.so.conf. Jeśli ręcznie dodałeś ścieżkę do pliku /etc/ld.so.cache, musisz uruchomić polecenie: ldconfig -v Istnieje również zmienna systemowa LD_LIBRARY_PATH. Dodawanie ścieżek do pliku /etc/ld.so.conf nie jest dobrym pomysłem, duże biblioteki trafiają do systemowej pamięci podręcznej i można mieć kłopoty. Najlepszym sposobem jest zszycie ścieżki do biblioteki w exe-shnik: cc -o myprog myprog.o -Wl, -rpath = / opt / obscure / lib -L / opt / obscure / lib -lweird Wraz z rozwojem bibliotek współdzielonych możliwe są problemy z nimi z powodu nakładania się itp. Używanie LD_LIBRARY_PATH również jest problematyczne. Zamiast tego można użyć skryptu: LD_LIBRARY_PATH = / opt / crummy / lib export LD_LIBRARY_PATH exec / opt / crummy / bin / mój_prog [e-mail chroniony] Głównym narzędziem do kompilacji w systemie Unix jest make. Najbardziej szczegółowy opis znajduje się w Środowisko programistyczne UNIX lub w Zarządzanie projektami z make... Make zawsze ma cel - cel - który może być plikiem lub etykietą. Cel może być pierwszorzędny i drugorzędny. Aby zbudować cel, poszukaj reguł. Na przykład: OBJS = aux.o main.o # pliki obiektowe all: myprog myprog: $ (OBJS) $ (CC) -o myprog $ (OBJS) Pierwszy cel - wszystkie - jest domyślnie głównym celem. Zasadą dla tego jest myprog, który może być inną regułą, innym celem lub plikiem. OBJS to makro do budowania celu. Ten plik makefile wygeneruje następujące dane wyjściowe: cc -c -o aux.o aux.c cc -c -o main.o main.c cc -o myprog aux.o main.o make można uruchomić w połączeniu z opcjami poleceń, na przykład: make aux.o Opcje Make: -n -f Często używane makra: CFLAGS - opcje kompilatora LDFLAGS - opcje linkera make ma kilka standardowych celów: clean distclean install test zależny Makefile zwykle się uruchamia z którymkolwiek i obejmuje na przykład: X_INCLUDES = -I / usr / X11R6 / include X_LIB = -L / usr / X11R6 / lib -lX11 -Xt NG_INCLUDES = -I / usr / local / include PNG_LIB = -L / usr / local / lib -lpng Dalej są opcje dla kompilatora i linkera CFLAGS = $ (CFLAGS) $ (X_INCLUDES) $ (PNG_INCLUDES) LDFLAGS = $ (LDFLAGS) $ (X_LIB) $ (PNG_LIB) Następnie można podać kod źródłowy: UTIL_OBJS = util.o BORING_OBJS = $ (UTIL_OBJS) nudne.o TRITE_OBJS = $ (UTIL_OBJS) banał.o PROGS = nudny banał I dopiero teraz są cele i zasady: all: $ (PROGS) nudne: $ (BORING_OBJS) $ (CC) -o [e-mail chroniony]$ (BORING_OBJS) $ (LDFLAGS) banalne: $ (TRITE_OBJS) $ (CC) -o [e-mail chroniony]$ (TRITE_OBJS) $ (LDFLAGS) Biblioteki współdzielone są podstawowym składnikiem systemu Unix. Standardowa biblioteka C, na przykład w Suse 9.1, ma rozmiar 1,3 MB. Kopia tej biblioteki dla wszystkich programów korzystających z niej w /usr/bin zajmie więcej niż jeden gigabajt. Takie biblioteki wymagają nie tylko miejsca na dysku, ale także pamięci. Jądro zostało zaprojektowane tak, aby przechowywać w pamięci jedną kopię biblioteki współdzielonej. Należy pamiętać, że przy linkowaniu statycznym kod biblioteki jest statycznie dodawany do kodu pliku wykonywalnego, a po jego uruchomieniu biblioteka nie jest nam potrzebna - jej kod jest już w trakcie kompilacji linkowany do treści pliku wykonywalnego. Dzięki dynamicznemu łączeniu biblioteka współdzielona jest łączona w czasie wykonywania.
Łączenie dynamiczne to dominujący typ bibliotek łączących. Sama biblioteka standardowa składa się z ponad tysiąca wywołań systemowych. W przypadku naprawdę działających programów, tzw. Procedure Linkage Table (PLT) to tabela składająca się z wywołań funkcji wywoływanych z bibliotek współdzielonych. Pierwszym problemem, który może się z tym pojawić, jest problem ze zgodnością. W przypadku kompilacji statycznej problem ten jest rozwiązywany w czasie kompilacji. Przy dynamicznym linkowaniu nie ma takiej gwarancji, ponieważ bibliotekę współdzieloną możemy aktualizować po kompilacji. W tym przypadku sprawdzaniem jest numer wersji - gdy dynamiczny linker próbuje połączyć bibliotekę, sprawdza numer wersji, a jeśli numer nie pasuje, to nie jest linkowany. Numer wersji składa się z 2 części - głównej (głównej) i drugorzędnej (pobocznej). Jeśli główny numer jest taki sam, to z reguły nie powinno być problemów z ładowaniem biblioteki. Jeśli mamy bibliotekę libexample.so, to będzie miała link do wersji libexample.so.N, gdzie N jest głównym numerem wersji, z którego z kolei będzie link do libexample.so.NM, gdzie M to główny numer wersji pomocniczej. W takim przypadku program wyszuka dokładnie taką wersję N, jakiej potrzebuje.
W bibliotekach statycznych kod znajduje się w plikach z rozszerzeniem .a, w przypadku bibliotek dynamicznych pliki mają rozszerzenie .so. Format biblioteki statycznej jest generowany za pomocą narzędzia ar, a format jest podobny do tego, który generuje narzędzie tar. W przypadku bibliotek dynamicznych w najnowszych wersjach systemu Linux ten format jest zwykle binarny ELF. Składa się z nagłówka i segmentów, które podzielone są na sekcje. Podczas łączenia kod z bibliotek statycznych jest bezpośrednio dodawany do pliku wykonywalnego programu, a łącza z bibliotek dynamicznych są pobierane i dodawane do PLT. Po załadowaniu program ładuje dynamiczny linker, który ładuje wymagane biblioteki. Podczas dynamicznego łączenia programu możesz dodać ścieżki do wyszukiwania bibliotek w czasie kompilacji. W przypadku gcc ta składnia wygląda tak: -Wl, -R / ścieżka Jeśli program jest już połączony, możesz użyć zmiennej LD_LIBRARY_PATH.Możesz również uruchomić specjalny skrypt opakowujący przed uruchomieniem programu, który konkretnie konfiguruje katalogi bibliotek, tak jak ma to miejsce , na przykład podczas uruchamiania Mozilli. Dynamiczny linker uzyskuje dostęp do konfiguracji /etc/ld.so.conf, która zawiera listę katalogów. Ale najpierw linker uzyskuje dostęp do LD_LIBRARY_PATH. ldd (lista zależności dynamicznych)- narzędzie do debugowania bibliotek współdzielonych. Zawiera listę bibliotek współdzielonych dla danego modułu, na przykład: ldd / bin / sh linux-gate.so.1 => (0xffffe000) libreadline.so.4 => /lib/libreadline.so.4 (0x40036000) libhistory.so.4 => /lib/libhistory.so.4 (0x40062000) libncurses.so.5 => /lib/libncurses.so.5 (0x40069000) libdl.so.2 => /lib/libdl.so.2 (0x400af000) libc.so.6 => / lib / tls /libc.so.6 (0x400b2000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
  • Dla początkujących: artykuł w Linux Journal „Linkers and Loaders” autorstwa Sandeep Grover
  • Rękopis książki „Łączniki i ładowarki” online.
  • Polecany także 2002 przez Ulricha Dreppera pod tytułem "Jak pisać biblioteki współdzielone" (pdf). Slajdy (pdf) i skrypt wspomniany na slajdach

Budowanie wspólnej biblioteki

Napiszmy małą bibliotekę, która nie robi nic, prawie nic, poza „hello world”. Pierwszy obiekt:

/ * print1.c * / #include void print1 (void) (printf ("1 \ n");)

Podobnie drugi przedmiot:

/ * print2.c * / #include void print2 (void) (printf ("2 \ n");)

Skompilujmy:

gcc -Wall -g -c -fpic print1.c gcc -Wall -g -c -fpic print2.c

Utwórz bibliotekę współdzieloną:

gcc -shared -Wlsoname = libprint.so.1 -g \ -o libprint.so.1.0.1 print1.o print2.o

Tworzymy link do biblioteki:

ln -sf libprint.so.1.0.1 libprint.so

Aby inni mogli korzystać z tej biblioteki, musisz napisać nagłówek:

/ * print.h * / #ifndef PRINT_H #define PRINT_H void print1 (void); void print2 (unieważniony); #endif

Napiszmy program testowy:

/ * print.c * / #include "print.h" int main() (print1 (); print2 (); return 0;)

Skompilujmy to:

gcc -Ściana -g -L. -lprint -o print print.c

Aby uruchomić program, linker musi powiedzieć, gdzie znajduje się biblioteka:

Przeciążanie funkcji

Przed załadowaniem biblioteki zawsze możemy powiedzieć loaderowi, od czego zacząć wyszukiwanie i ładowanie.

Dodajmy nową funkcję do biblioteki, którą właśnie napisaliśmy. Ta funkcja nazywa się fopen.

/ * fopen.c * / #include PLIK * fopen (const char * fn, const char * mode) (printf ("fopen wywołane \ n"); return NULL;)

Skompilujmy tę funkcję i umieśćmy ją w bibliotece:

gcc -Wall -g -c -fpic fopen.c gcc -shared -Wlsoname = libprint.so.1 -g \ -o libprint.so.1.0.2 print1.o print2.o fopen.o ln -sf libprint.so .1.0.2 libprint.so

Napiszmy program do testowania:

/ * print-2.c * / #include int main() (PLIK * f; f = fopen ("abc", "r"); if (f == NULL) (printf ("1");) else (printf ("2"); fclose (f);) return 0;)

Skompilujmy ten program:

gcc -Wall -g -o print print.c

Musimy go uruchomić z następującym prefiksem:

LD_PRELOAD =. / Libprint.so ./print

Jeśli coś zostanie zrobione nie tak, wyświetli się linia: fopen call

Proces tworzenia programu.

Tłumaczenie na język rosyjski: Władimir Popow

Dziś, w stale zmieniającym się środowisku, proces tworzenia oprogramowania jest wynikiem ewolucji umiejętności i doświadczenia rozwijanych przez programistów i programistów.

Ten proces składa się z następujących kroków:

    Generowanie kodu źródłowego języka wysokiego poziomu w edytorze tekstu. Jeśli spróbujemy zmieścić bardzo duży program w jednym pliku, zarządzanie staje się trudne. Z tego powodu kod źródłowy jest dzielony na moduły funkcjonalne, które są tworzone z jednego lub więcej plików kodu źródłowego. Kod źródłowy w tych modułach niekoniecznie musi być napisany w tym samym języku, ponieważ niektóre języki lepiej nadają się do rozwiązania konkretnego problemu niż inne.

    Po utworzeniu plików z kodem źródłowym programu należy je przetłumaczyć na bloki kodu, które może wykonać maszyna. Ten kod jest zwykle określany jako kod obiektu... Ten kod wykonuje te same czynności, co kod oryginalny, z wyjątkiem tego, że jest napisany w specjalnym języku, który może być bezpośrednio wykonany przez maszynę. Proces tłumaczenia kodu źródłowego na kod obiektowy jest znany jako kompilacja... Kompilacja odbywa się w częściach i kompiluje (w zależności od kompilatora) część programu i zwykle jeden lub więcej plików za jednym razem. Skompilowany kod obiektowy zawiera program, podprogram, zmienne itp. - części programów, które zostały przetłumaczone i są gotowe do następnego kroku.

    Po utworzeniu wszystkich plików programu z kodem maszynowym należy je połączyć lub połączyć za pomocą operacji wykonywanej przez specjalne narzędzie o nazwie linker... Na tym etapie wszystkie odwołania, które kod modułu tworzy do kodu należącego do innego modułu, są „rozwiązane” (takie jak wywołanie podprogramu lub odwołania do zmiennych posiadanych lub zdefiniowanych w innym module). Wynikowy produkt to program, który można bezpośrednio załadować i wykonać.

    Wykonywanie programu odbywa się za pomocą specjalnego rodzaju oprogramowania, które jest istotną częścią systemu operacyjnego, w przypadku Linuksa jest to wywołanie systemowe exec(). Funkcja ta znajduje plik, przydziela pamięć procesowi, ładuje określone fragmenty zawartości pliku (zawierające kod i początkowe wartości zmiennych) i przekazuje kontrolę procesorowi w punkcie „tekst” w programie, który zwykle wskazuje na plik wykonywalny samo.

2.- Krótka historia procesu tworzenia programu.

Proces tworzenia programu nieustannie ewoluuje, konieczne jest osiągnięcie jak najwydajniejszego wykonywania programów lub jak najlepszego wykorzystania zasobów systemowych.

Na początku programy pisane były bezpośrednio w kodzie maszynowym. Później stało się jasne, że można pisać programy w języku wyższego poziomu, ponieważ późniejsze tłumaczenie na kod maszynowy można było zautomatyzować ze względu na systematyczny charakter tłumaczenia. Zwiększyło to wydajność programów.

Po nauczeniu się kompilacji programów (uprościłem ewolucję kompilacji, w rzeczywistości był to bardzo trudny krok, bo jest to bardzo złożony proces), proces tworzenia programów zaczął polegać na tworzeniu pliku z kodem źródłowym programu, kompilując go i ostatecznie wykonując.

Szybko zauważono, że proces kompilacji był bardzo pracochłonny i pochłaniał wiele zasobów, w tym czas komputera, a wiele funkcji zawartych w tych programach było wielokrotnie używanych w różnych programach. Co więcej, gdy ktoś zmienił część programu, kompilacja wstawionego kodu oznaczała ponowną kompilację całego kodu źródłowego, w tym nowe tłumaczenie całego niezmienionego kodu.

Z tego powodu zaczęto stosować kompilację modułową. Jego znaczenie polegało na podzieleniu programu z jednej strony na program główny, a z drugiej na te funkcje, które były często używane w kółko, a które zostały już skompilowane i przechowywane w specjalnym miejscu (będziemy nazywać to poprzednik biblioteki).

Wtedy stało się możliwe tworzenie programów obsługujących te funkcje bez konieczności ciągłego ich kodowania. Już wtedy proces był skomplikowany ze względu na to, że przy łączeniu programów należało połączyć wszystkie części i musiały one być znane programiście (powodowało to dodatkowy koszt testowania możliwości wykorzystania znanej funkcji, która wykorzystuje / wymaga innych nieznanych funkcji).

3.- Co to jest biblioteka?

Opisany powyżej problem doprowadził do powstania bibliotek. To nic innego jak specjalny rodzaj pliku (dokładnie archiwum, tar (1) lub cpio (1)) ze specjalnymi parametrami, których format rozumie linker, a kiedy wskażemy go na archiwum biblioteczne, LINKER WYBIERA TYLKO TE MODUŁY, KTÓRE POTRZEBUJĄ PROGRAMU i wyklucza wszystko inne. Pojawiła się nowa zaleta. Teraz można było tworzyć programy wykorzystujące duże biblioteki funkcji, a programista nie musiał znać wszystkich zależności funkcji w tej bibliotece.

Biblioteki, o których mówiliśmy do tej pory, nie są dalej rozwijane. Po prostu dodali plik, często znajdujący się na początku archiwum, zawierający opisy modułów i identyfikatory, które linker musi rozwiązać bez czytania całej biblioteki (a tym samym eliminując potrzebę wielokrotnego czytania biblioteki). Ten proces (dodawanie tablicy symboli do archiwum biblioteki) w Linuksie jest wykonywany przez polecenie ranlib (1). Opisane do tej pory biblioteki są znane jako BIBLIOTEKI STATYCZNE.

Postęp nastąpił po pojawieniu się pierwszego systemu wielozadaniowego: kod dzielenia... Gdyby dwie kopie tego samego kodu działały w tym samym systemie, byłoby pożądane, aby oba procesy mogły współdzielić ten sam kod, ponieważ program zwykle nie modyfikuje własnego kodu. Pomysł ten eliminuje potrzebę alokowania wielu kopii w pamięci, co zwalnia dużo pamięci w ogromnych systemach z wieloma użytkownikami.

Idąc krok dalej z tą ostatnią innowacją, ktoś (nie wiem kto to był, ale pomysł był świetny ;-) pomyślał, że bardzo często wiele programów korzysta z tej samej biblioteki, ale będąc różnymi programami, część używanych bibliotek była niekoniecznie te same części, których używa inny program. Co więcej, główny kod był inny (są to różne programy), więc ich teksty nie były udostępniane. Otóż ​​nasz bohater pomyślał, że gdyby różne programy korzystające z tej samej biblioteki mogły współdzielić tę bibliotekę między sobą, to moglibyśmy zyskać trochę pamięci. Teraz różne programy, mające inny tekst, pracują z tym samym kodem biblioteki.

Jednak proces stał się teraz bardziej skomplikowany. Program wykonywalny nie jest w pełni powiązany, ale rozwiązywanie odwołań do identyfikatorów bibliotek jest odroczone do momentu załadowania programu. Konsolidator (ld (1) w przypadku Linuksa) rozpoznaje wywołania bibliotek współdzielonych i nie zawiera ich kodu w programie. Sam system, jądro, podczas wykonywania exec() rozpoznaje uruchomienie programu korzystającego z bibliotek dzielonych i wykonuje specjalny kod, który ładuje biblioteki dzielone (alokując pamięć dzieloną dla ich tekstu, alokując pamięć prywatną dla wartości bibliotek itp.). Teraz ten proces jest wykonywany podczas ładowania pliku wykonywalnego, a cała procedura stała się znacznie bardziej skomplikowana.

Oczywiście, gdy linker napotka zwykłą bibliotekę, zachowuje się tak jak poprzednio.

Biblioteka współdzielona nie jest archiwum zawierającym kod wynikowy, jest raczej plikiem zawierającym sam kod wynikowy. Łącząc program z biblioteką współdzieloną, linker nie sprawdza biblioteki, które moduły należy dodać do programu, a które nie. Zapewnia jedynie rozwiązanie nierozwiązanych łączy i określa, co należy dodać do listy, gdy biblioteka jest uwzględniana. Możliwe jest utworzenie archiwum ar (1) biblioteki wszystkich bibliotek dzielonych, ale często się tego nie robi, ponieważ biblioteki dzielone są często wynikiem łączenia różnych modułów, więc biblioteka będzie potrzebna później, w czasie wykonywania. Być może nazwa biblioteka współdzielona nie jest najwłaściwsza i trafniej byłoby nazwać ją obiektem współdzielonym (jednak, ponieważ możemy nie być zrozumiani, nie używamy tego terminu).

4.- Rodzaje bibliotek.

Jak powiedzieliśmy, w Linuksie są dwa rodzaje bibliotek: statyczne i współdzielone. Biblioteki statyczne to zbiór modułów, które są łączone w archiwum za pomocą narzędzia ar (1) i indeksowane za pomocą narzędzia ranlib (1). Te moduły są często przechowywane w pliku kończącym się na .a (nie używam terminu rozszerzenie, ponieważ Linux nie używa pojęcia rozszerzenia pliku). Konsolidator rozpoznaje koniec pliku .a i zaczyna szukać modułów tak, jakby była biblioteką statyczną, a następnie wybiera i dodaje do programu te moduły, które rozwiązują nierozwiązane odwołania.

W przeciwieństwie do bibliotek statycznych, biblioteki współdzielone nie są archiwami, lecz obiektami relokowalnymi, wyznaczonymi przez specjalny kod (który oznacza je jako biblioteki współdzielone). Linker ld (1), jak już wspomniano, nie dodaje modułów do kodu programu, ale wybiera identyfikatory dostarczone przez bibliotekę jako dozwolone, dodaje te, które są wymagane przez samą bibliotekę i kontynuuje pracę bez dodawania żadnego kodu, zakładając, że wymagany kod został już dodany do kodu głównego. Konsolidator ld (1) rozpoznaje biblioteki współdzielone przez zakończenie .so (nie .so.xxx.yyy, omówimy to później).

5.- Proces łączenia w Linuksie.

Każdy program składa się z modułów obiektowych połączonych w plik wykonywalny. Odbywa się to przez linker ld (1) używany w Linuksie.

ld (1) obsługuje różne opcje, które zmieniają jego zachowanie, ale ograniczymy się tutaj tylko do tych, które dotyczą ogólnie korzystania z bibliotek. ld (1) nie jest wywoływany bezpośrednio przez użytkownika, ale przez sam kompilator gcc (1) w jego końcowej fazie. Mała wiedza o nim sposób działania pomóż nam zrozumieć sposób korzystania z bibliotek w Linuksie.

Aby ld (1) działał poprawnie, potrzebuje listy obiektów do połączenia z programem. Obiekty te mogą być określone w dowolnej kolejności (*), o ile przestrzegamy powyższej konwencji, jak już wspomniano, biblioteka współdzielona jest rozpoznawana przez końcówkę .so (nie .so.xx.yy), a bibliotekę statyczną przez .a (i oczywiście proste pliki obiektowe to te, których nazwa kończy się na .o).

(*) To nie do końca prawda. ld (1) zawiera tylko te moduły, które rozwiązują łącza w momencie włączenia biblioteki, więc moduł dołączony później może nadal mieć łącze, które później, ponieważ nie pojawia się w momencie włączenia tej biblioteki, może spowodować polecenie, aby uwzględnić niezbędne biblioteki ...

Z drugiej strony, ld (1) umożliwia dołączanie standardowych bibliotek dzięki opcjom -l i -L.

Ale... Co rozumiemy przez standardową bibliotekę, jaka jest różnica? Nie. Tyle tylko, że ld (1) wyszukuje standardowe biblioteki w określonych miejscach, podczas gdy te opisane na liście parametrów jako obiekty są wyszukiwane według ich nazw plików.

Domyślnie biblioteki są wyszukiwane w katalogach /lib i /usr/lib (chociaż słyszałem, że w zależności od wersji/implementacji ld(1) mogą istnieć dodatkowe katalogi). -L pozwala nam dodawać katalogi do tych przeszukiwanych przez normalne przeszukiwanie biblioteki. Jest używany przez określenie katalogu -L dla każdego katalogu, który chcemy dodać. Biblioteki standardowe są określane za pomocą opcji -l Nazwa (gdzie Nazwa określa bibliotekę do załadowania), a ld (1) przeszuka, w odpowiedniej kolejności, w odpowiednich katalogach plik o nazwie Nazwalib.so. Jeśli nie zostanie znaleziony, zostanie podjęta próba znalezienia libName.a, jego wersji statycznej.

Jeśli ld (1) znajdzie bibliotekę Nazwa.so, połączy ją jako bibliotekę współdzieloną, natomiast jeśli znajdzie bibliotekę Nazwa.a, połączy moduły pochodzące z niej, jeśli rozwiążą jakiekolwiek nierozwiązane odniesienia.

Łączenie dynamiczne jest wykonywane, gdy plik wykonywalny jest ładowany przez specjalny moduł (w rzeczywistości ten specjalny moduł jest samą biblioteką współdzieloną) o nazwie /lib/ld-linux.so.

W rzeczywistości istnieją dwa moduły do ​​łączenia bibliotek dynamicznych: /lib/ld.so (dla bibliotek używających starego formatu a.out) i /lib/ld-linux.so (dla bibliotek używających nowego formatu ELF).

Specyfiką tych modułów jest to, że muszą być ładowane za każdym razem, gdy program jest dynamicznie łączony. Ich nazwy są standardowe (powodem jest to, że nie można ich przenieść z katalogu / lib, a także nie można zmienić ich nazw). Jeśli zmienimy nazwę /etc/ld-linux.so, automatycznie przestaniemy używać dowolnego programu korzystającego z bibliotek współdzielonych, ponieważ ten moduł jest odpowiedzialny za rozwiązywanie wszystkich linków, które nie są rozwiązywane w czasie wykonywania.

Ostatni moduł jest wspomagany istnieniem pliku /etc/ld.so.cache, który określa dla każdej biblioteki najbardziej odpowiedni plik wykonywalny, który zawiera tę bibliotekę. Do tego tematu wrócimy później.

7.- imię. Wersje bibliotek wykonywalnych. Zgodność.

To prowadzi nas do najbardziej mylącego tematu bibliotek współdzielonych: ich wersji.

Często widzimy komunikat „nie znaleziono biblioteki libX11.so.3”, co wprawia nas w zakłopotanie: mając bibliotekę libX11.so.6, nie jesteśmy w stanie nic zrobić. Jak to możliwe, że ld.so (8) rozpoznaje libpepe.so.45.0.1 i libpepe.so.45.22.3 i nie rozpoznaje libpepe.so.46.22.3 zamiennie?

W systemie Linux (i we wszystkich systemach operacyjnych używających formatu ELF) biblioteki są identyfikowane przez ich charakterystyczną sekwencję znaków: soname.

soname jest zawarte w samej bibliotece, a kolejność ta jest określana podczas łączenia obiektów tworzących bibliotekę. Tworząc bibliotekę współdzieloną, aby nadać wartość temu ciągowi znaków, musisz przekazać opcję ld (1) (-soname<имя_библиотеки>).

Ta sekwencja znaków jest używana przez dynamiczny program ładujący do identyfikowania biblioteki współdzielonej do załadowania oraz do identyfikowania pliku wykonywalnego. Wygląda to mniej więcej tak:
Ld-linux.so wykrywa, że ​​program wymaga biblioteki i definiuje jej soname. Następnie wyszukiwana jest nazwa w /etc/ld.so.cache i określana jest nazwa pliku zawierającego tę bibliotekę. Następnie żądane soname jest porównywane z nazwą istniejącej biblioteki, a jeśli są identyczne, to jest nam potrzebne! Jeśli nie, wyszukiwanie będzie kontynuowane, dopóki nie zostanie znaleziony, lub jeśli nie zostanie znaleziony, zostanie wyświetlony komunikat o błędzie.

Soname może służyć do określenia, czy biblioteka nadaje się do załadowania, ponieważ ld-linux.so sprawdza, czy wymagana nazwa soname jest taka sama jak wymagany plik. W przypadku różnicy możemy uzyskać słynne „libXXX.so.Y not found”. Szukane jest soname, a zwracany błąd jest określany przez soname.

Jeśli zmienimy nazwę biblioteki, może być sporo zamieszania, a sam problem pozostaje. Ale zmiana soname również nie jest dobrym pomysłem, ponieważ w społeczności linuksowej istnieje konwencja przypisywania soname:

Soname biblioteki domyślnie powinna identyfikować odpowiednią bibliotekę i INTERFEJS tej biblioteki. Jeżeli dokonamy zmian w bibliotece, które wpływają tylko na funkcjonalność wewnętrzną, ale interfejs pozostaje niezmieniony (liczba funkcji, zmiennych, parametry funkcji), to obie biblioteki będą wymienne i generalnie powiemy, że zmiany były niewielkie (obie biblioteki są kompatybilne i możemy je zastępować). Jeśli tak się stanie, to pomniejszy numer (który nie jest częścią soname) często się zmienia i bibliotekę można wymienić bez większych problemów.

Jednak gdy dodamy funkcje, usuniemy funkcje i ogólnie ZMIENIMY INTERFEJS biblioteki, nie można już powiedzieć, że ta biblioteka jest wymienna z poprzednią (na przykład zastąpienie libX11.so.3 libX11. so.6 jest częścią przejścia z X11R5 na X11R6, kiedy wprowadza nowe funkcje, a tym samym zmienia interfejs). Przejście z X11R6-v3.1.2 na X11R6-v3.1.3 prawdopodobnie nie zmieni interfejsu i biblioteka będzie miała tę samą soname - choć aby zachować starą wersję musimy nadać jej inną nazwę (z tego powodu numer wersji kończy nazwę biblioteki , natomiast w soname podane są tylko numery główne).

8.- ldconfig (8)

Jak powiedzieliśmy wcześniej, /etc/ld.so.cache pozwala ld-linux.so na konwersję soname pliku zawartego w bibliotece. Jest to plik binarny (dla wydajności) wygenerowany przez narzędzie ldconfig (8).
ldconfig (8) tworzy dowiązanie symboliczne z nazwą biblioteki soname dla każdej biblioteki DLL znalezionej w katalogach określonych w /etc/ld.so.conf. W tym przypadku, gdy ld.so chce uzyskać nazwę pliku, wybiera plik o wymaganej nazwie soname z listy katalogów. Nie ma więc potrzeby uruchamiania ldconfig (8) za każdym razem, gdy dodajesz bibliotekę. Uruchamiamy ldconfig tylko wtedy, gdy dodajemy katalog do listy.

9.- Chcę stworzyć dynamiczną bibliotekę.

Zanim zaczniemy tworzyć bibliotekę dynamiczną, musimy zastanowić się, czy jest ona naprawdę potrzebna. Biblioteki dynamiczne powodują przeciążenie systemu z kilku powodów:
    Ładowanie programu odbywa się w kilku etapach; jeden do ładowania programu głównego, reszta dla każdej biblioteki dołączanej dynamicznie, której ten program używa (rozważymy to dla odpowiedniej biblioteki dołączanej dynamicznie, ponieważ ten ostatni punkt nie jest już rzadkością i staje się zaletą).

    Biblioteki DLL muszą zawierać kod relokowalny, ponieważ adres przydzielony procesowi w wirtualnej przestrzeni adresowej jest nieznany do momentu załadowania. W takim przypadku kompilator jest zmuszony zarezerwować rejestr do przechowywania pozycji ładowania biblioteki iw efekcie mamy o jeden rejestr mniej na optymalizację kodu. Nie jest to taki problem, ponieważ wynikowe przeciążenie w większości przypadków nie przekracza 5% przeciążenia.

W przypadku biblioteki dynamicznej najbardziej akceptowalnym przypadkiem jest sytuacja, w której jest ona stale używana przez jakiś program (pomaga to uniknąć wyładowania tekstu biblioteki po zakończeniu procesu, który ją załadował. Podczas gdy inne procesy korzystają z modułów biblioteki, pozostaje on w pamięci) .

Biblioteka współdzielona jest w pełni ładowana do pamięci (a nie tylko modułów, których potrzebuje), więc musi być używana w całości, aby była użyteczna. Najgorszym przykładem użycia biblioteki dołączanej dynamicznie jest użycie tylko jednej funkcji, a 90% biblioteki jest rzadko używane.

Biblioteka standardowa C jest dobrym przykładem biblioteki dołączanej dynamicznie (jest używana przez wszystkie programy napisane w C). Wszystkie funkcje są używane średnio.

Biblioteka statyczna nie musi zawierać rzadko używanych funkcji; dopóki są zawarte we własnym module, nie będą powiązane z żadnym programem, który ich nie wymaga.

9.1.- Kompilowanie kodów źródłowych
Kompilacja kodu źródłowego jest dokładnie taka sama jak w przypadku zwykłego kodu źródłowego, z wyjątkiem tego, że użyjemy "-f PIC" (kod niezależny od pozycji).

Ten krok jest fundamentalny, ponieważ w bibliotece statycznej pozycja obiektów biblioteki jest ustalana podczas łączenia, więc zajmuje to określoną ilość czasu. Ten krok nie był możliwy w starych plikach binarnych a.out, co skutkowało umieszczeniem każdej biblioteki współdzielonej w stałej pozycji w wirtualnej przestrzeni adresowej. W rezultacie konflikty mogły powstać w dowolnym momencie, gdyby program chciał użyć dwóch bibliotek i załadował je do nakładających się obszarów pamięci wirtualnej. Oznaczało to, że byłeś zmuszony utrzymywać listę, na której każdy, kto chciał uczynić bibliotekę dynamiczną, musiał zadeklarować zakres adresów do użycia, aby nikt inny nie mógł jej użyć.

Jak już zauważyliśmy, rejestracja biblioteki dynamicznej na oficjalnej liście nie jest konieczna, ponieważ w momencie wczytywania biblioteki jest ona umieszczana na aktualnie zdefiniowanej pozycji, mimo że kod musi zostać przeniesiony.

9.2.- Łączenie obiektów z biblioteką
Po skompilowaniu wszystkich obiektów należy je połączyć, określając opcję, która tworzy dynamicznie ładowany obiekt. gcc -shared -o Nazwalibi.so.xxx.yyy.zzz -Wl, -soname, Nazwalibi.so.xxx biblioteka współdzielona. Zajmijmy się każdym z osobna:
    -wspólny.
    To mówi konsolidatorowi, że w końcu musi utworzyć bibliotekę współdzieloną i dlatego musi zawierać kod wykonywalny w pliku wyjściowym odpowiadającym bibliotece.

    O lib Nazwa.tak.xxx.yyy.zzz.
    To jest nazwa pliku wyjściowego. Wcale nie jest konieczne przestrzeganie konwencji nazewnictwa, ale jeśli chcemy, aby ta biblioteka stała się standardem dla przyszłego rozwoju, lepiej jej przestrzegać.

    Wl, -soname, lib Nazwa.so.xxx.
    Opcja -Wl mówi gcc (1), aby podążał za opcjami (oddzielonymi przecinkami), które są dla linkera. Ten mechanizm jest używany przez gcc (1) do przekazywania opcji do ld (1). W przykładzie przekazujemy linkerowi następujące opcje:

-soname nazwa_lib.so.xxx Ta opcja zmienia nazwę so biblioteki, więc ta biblioteka zostanie załadowana, gdy zażądają tego programy, które wymagają określonej nazwy so.
9.3.- Instalacja Biblioteki
Cóż, mamy już odpowiedni plik wykonywalny. Teraz, aby móc z niego korzystać, należy go zainstalować w odpowiednim miejscu.

Aby skompilować program, który wymaga naszej nowej biblioteki, musimy użyć następującego polecenia:

Gcc -o program lib Name.so.xxx.yyy.zzz lub, jeśli biblioteka była zainstalowana w katalogu (/usr/lib), to wystarczy: gcc -o program -l Name (jeśli biblioteka jest w /usr /local/lib, a następnie dodaj opcję "-L/usr/local/lib"). Aby zainstalować bibliotekę, wykonaj następujące czynności:

    Skopiuj bibliotekę do katalogu /lib lub /usr/lib. Jeśli zdecydujesz się skopiować go do innej lokalizacji (na przykład /usr/local/lib), nie możesz być pewien, że linker ld (1) znajdzie go automatycznie podczas łączenia programów.

    Uruchom ldconfig (1), aby utworzyć dowiązanie symboliczne libName.so.xxx.yyy.zzz do libName.so.xxx. Na tym etapie będziemy wiedzieć, czy poprawnie wykonaliśmy wszystkie poprzednie kroki i czy biblioteka jest rozpoznawana jako dynamiczna. Ten krok ma wpływ tylko na ładowanie biblioteki w czasie wykonywania, a nie na łączenie programów.

    Aby konsolidator mógł znaleźć bibliotekę z opcją -l, utwórz dowiązanie symboliczne z NazwaBiblioteki.so.xxx.yyy.zzz (lub NazwaBiblioteki.so.xxx, nazwaso) do NazwaBiblioteki.so. Aby ten mechanizm działał, nazwa biblioteki musi być zgodna ze wzorcem Nazwa.so

10.- Tworzenie biblioteki statycznej

Jeśli z drugiej strony musisz utworzyć bibliotekę statyczną (lub potrzebujesz dwóch wersji, aby móc tworzyć kopie połączone statycznie), musisz wykonać następujące czynności:

Uwaga: Podczas wyszukiwania bibliotek konsolidator najpierw szuka pliku o nazwie libName.so, a dopiero potem libName.a. Jeśli nazwiemy obie te biblioteki (wersja statyczna i dynamiczna) taką samą nazwą, to na ogół nie będzie możliwe określenie, która z nich będzie linkowana w każdym przypadku (dynamiczna jest zawsze linkowana jako pierwsza, ponieważ linker wykrywa ją jako pierwszy ).

Z tego powodu, jeśli konieczne jest posiadanie dwóch wersji tej samej biblioteki, zawsze zaleca się nazwanie statycznej w postaci NazwaBiblioteki_s.a, a dynamicznej NazwaBiblioteki.so. Następnie podczas łączenia będziesz musiał określić:

Gcc -o program -l Name_s do łączenia z wersją statyczną, natomiast dla wersji dynamicznej: gcc -o program -l

10.1.- Kompilacja kodu źródłowego
Nie musisz podejmować żadnych specjalnych kroków, aby skompilować kod źródłowy. Podobnie pozycja obiektów jest ustalana na etapie łączenia, nie jest konieczna kompilacja z opcją -f PIC (chociaż można jej nadal używać).
10.2.- Łączenie obiektów z biblioteką
Dla bibliotek statycznych nie wykonuje się linkowania. Wszystkie obiekty są archiwizowane w pliku biblioteki za pomocą polecenia ar (1). Ponadto, aby szybko rozwiązać symbole, zaleca się wykonanie polecenia ranlib (1) w bibliotece. Chociaż nie jest to wymagane, niewykonanie tego polecenia może rozłączyć moduły w pliku wykonywalnym, ponieważ gdy linker przetwarza moduł podczas tworzenia biblioteki, nie wszystkie pośrednie zależności między modułami są natychmiast rozwiązywane: powiedzmy, że moduł na końcu archiwum potrzebuje innego modułu w na początku archiwum, oznacza to, że rozwiązanie wszystkich łączy wymaga wielokrotnych przejść przez tę samą bibliotekę.
10.3.- Instalacja Biblioteki
Zaleca się nazywanie bibliotek statycznych w formacie Nazwalib.a tylko wtedy, gdy chcesz mieć tylko biblioteki statyczne. W przypadku dwóch rodzajów bibliotek sugerowałbym nazwanie ich nazwa_biblioteki_s.a, aby łatwiej było odróżnić, kiedy ładować bibliotekę statyczną, a kiedy ładować bibliotekę dynamiczną.

Proces budowania umożliwia wprowadzenie opcji -static. Ta opcja kontroluje ładowanie modułu /lib/ld-linux.so i nie wpływa na kolejność przeszukiwania biblioteki, więc jeśli ktoś określi -static i ld (1) znajdzie bibliotekę dynamiczną, będzie z nią pracować (a nie szukaj biblioteki statycznej). Doprowadzi to do błędów w czasie wykonywania z powodu wywołań procedur w bibliotece, która nie jest częścią pliku wykonywalnego — moduł automatycznego ładowania dynamicznego nie jest połączony i dlatego nie można wykonać procesu.

11.- Porównanie układu statycznego i dynamicznego

Załóżmy, że chcemy stworzyć dystrybucję programu korzystającą z biblioteki, którą możemy rozpowszechniać tylko statycznie zawartej w programie i w żadnej innej formie (przykładem tego przypadku są aplikacje zbudowane przy użyciu Motif).

Istnieją dwa sposoby stworzenia takiego programu. Pierwszym z nich jest utworzenie statycznie połączonego pliku wykonywalnego (używając tylko bibliotek .a i nie używając dynamicznego programu ładującego). Tego rodzaju programy są ładowane raz i nie wymagają żadnych bibliotek z systemu (nawet /lib/ld-linux.so). Mają jednak tę wadę, że wszystko, czego potrzebujesz, musi znajdować się w jednym pliku binarnym i dlatego są to zwykle bardzo duże pliki. Drugą opcją jest stworzenie dynamicznie linkowanego programu, czyli środowisko, w którym będzie działała nasza aplikacja, musi posiadać wszystkie odpowiadające mu biblioteki dynamiczne. Plik wykonywalny może być bardzo mały, chociaż czasami niemożliwe jest posiadanie absolutnie wszystkich bibliotek (na przykład są osoby, które nie mają Motif).

Istnieje trzecia opcja, mieszana, w której niektóre biblioteki są łączone dynamicznie, a inne statycznie. W takim przypadku logiczne byłoby wybranie biblioteki będącej w konflikcie w jej formie statycznej, a wszystkich pozostałych w formie dynamicznej. Ten wariant jest bardzo powszechną formą dystrybucji oprogramowania.

Na przykład możesz skompilować trzy różne wersje programu w następujący sposób:

Gcc -static -o program.static program.o -lm_s -lXm_s -lXt_s -lX11_s \ -lXmu_s -lXpm_s gcc -o program.dynamic program.o -lm -lXm -lXt -lX11 -lXmu -lXpm gcc -o program. program mieszany.o -lm -lXm_s -lXt -lX11 -lXmu -lXpm W trzecim przypadku tylko biblioteka Motif (-lXm_s) jest dołączona statycznie, a wszystkie inne są łączone dynamicznie. Środowisko, w którym będzie działał program musi posiadać odpowiednie wersje bibliotek libm.so.xx libXt.so.xx libX11.so.xx libXmu.so.xx oraz libXpm.so.xx

Udostępniona biblioteka lub biblioteka współdzielona to plik, który ma być udostępniany między programami. Moduły używane przez program są ładowane z oddzielnych obiektów współdzielonych do pamięci, a nie kopiowane przez linker, gdy kopiuje pojedynczy plik wykonywalny dla programu.

Biblioteki współdzielone mogą być łączone statycznie, co oznacza, że ​​odniesienia do modułów bibliotecznych są rozwiązywane, a modułom przydzielana jest pamięć podczas tworzenia pliku wykonywalnego. Ale często łączenie bibliotek współdzielonych jest opóźnione, dopóki nie zostaną załadowane.

Niektóre starsze systemy, takie jak Burroughs MCP Multics ma również tylko jeden format plików wykonywalnych, niezależnie od tego, czy są one ogólne, czy nie. Mają pliki bibliotek współdzielonych w tym samym formacie, co pliki wykonywalne. Ma to dwie główne zalety: po pierwsze, każdy wymaga tylko jednego bootloadera, a nie dwóch (posiadanie oddzielnego bootloadera zwiększa złożoność). Po drugie, pozwala również na używanie plików wykonywalnych jako bibliotek współdzielonych, jeśli mają one tablicę symboli. Typowe formaty połączonych bibliotek wykonywalnych i współdzielonych to ELF i Mach-O (zarówno w systemie Unix) jak i (Windows).

W niektórych starszych środowiskach, takich jak 16-bitowy Windows lub MPE dla HP 3000, w kodzie z biblioteką współdzieloną dozwolone były tylko dane oparte na stosie (lokalne) lub nałożono inne znaczące ograniczenia na kod biblioteki współdzielonej.

Pamięć współdzielona

Kod biblioteki może być współdzielony w pamięci z procesami, jak również na dysku. Jeśli używana jest pamięć wirtualna, procesy będą wykonywane na fizycznej stronie pamięci RAM, która jest odwzorowywana na różne przestrzenie adresowe procesów. Ma to swoje zalety. Na przykład w systemie OpenStep aplikacje często mają tylko kilkaset kilobajtów i szybko się ładują; większość ich kodu znajdowała się w bibliotekach, które zostały już załadowane przez system operacyjny do innych celów.

Programy mogą współdzielić pamięć RAM za pomocą niezależnego kodu, tak jak w Uniksie, co skutkuje złożoną, ale elastyczną architekturą. Zapewnia to większe prawdopodobieństwo współdzielenia za pomocą różnych technik, takich jak wstępne mapowanie przestrzeni adresowej i rezerwowanie stron dla każdej biblioteki współdzielonej. Trzecia opcja to magazyn jednopoziomowy używany przez System IBM / 38 i jego następców.

W niektórych przypadkach różne wersje bibliotek współdzielonych mogą powodować problemy, zwłaszcza gdy biblioteki różnych wersji mają te same nazwy plików i są używane do różnych aplikacji zainstalowanych w systemie, a każda z nich wymaga określonej wersji. Taki scenariusz jest znany jako

Biblioteka współdzielona pozwala używać zawartych w niej symboli w wielu filmach bez kopiowania tych symboli do bibliotek filmów. Z tego powodu obiekty biblioteki współdzielonej są nazwane Surowce(Aktywa). W takim przypadku biblioteka współdzielona jest używana jako plik zewnętrzny i nie jest dodawana do tworzonego (lub edytowanego) filmu.

Korzystanie z bibliotek współdzielonych jest wskazane na przykład w następujących przypadkach:

  • podczas korzystania z tej samej ścieżki dźwiękowej na kilku stronach serwisu;
  • podczas używania znaków tekstowych czcionki na kilku stronach serwisu;
  • gdy chcesz zapewnić jedno źródło dla elementów animacji używanych w wielu scenach w filmie lub w wielu filmach;
  • gdy potrzebujesz centralnej biblioteki zasobów, aby łatwiej kontrolować wprowadzane zmiany.

Flash MX obsługuje dwa typy bibliotek współdzielonych:

  • Czas pracy- współdzielona biblioteka uruchomieniowa; symbole zawarte w takiej bibliotece są dostępne do współdzielenia przez kilka filmów, jednak takie symbole można edytować tylko bezpośrednio w filmie źródłowym;
  • Czas autora- biblioteka udostępniona w momencie tworzenia; symbole zawarte w takiej bibliotece są dostępne do współdzielenia przez kilka filmów, przy czym dozwolona jest edycja zawartości biblioteki w dowolnym filmie będącym współwłaścicielem.

Aby zasoby biblioteki współdzielonej były dostępne w filmach hostowanych w witrynie zdalnej, plik Flash z biblioteką należy wyeksportować do formatu SWF i przesłać do witryny sieci Web.

Komentarz
Poprzednia wersja Flasha obsługuje tylko współdzieloną bibliotekę uruchomieniową
.

Aby utworzyć udostępnioną bibliotekę, taką jak Czas pracy, niezbędny:

  1. Określ jego zasoby (symbole w nim zawarte) w osobnym filmie.
  2. Zezwalaj na eksport wspólnych znaków.
  3. Podaj adres URL witryny, w której będzie hostowana biblioteka.
  4. Wyeksportuj plik Flash z tą biblioteką do formatu SWF i prześlij na stronę internetową.

Aby móc używać symboli z biblioteki współdzielonej Czas pracy w innych filmach („współwłaściciele”) konieczne jest w każdym z nich stworzenie linku do wspólnych postaci.

Przyjrzyjmy się teraz bardziej szczegółowo powyższym krokom.

Po utworzeniu biblioteki współdzielonej musisz określić, które zawarte symbole można wyeksportować do innych filmów. Wymaga to następujących kroków:

  1. Wybierz symbol z listy, który chcesz „udostępnić”.
  2. W menu kontekstowym symbolu wybierz polecenie Łączenie(Wiążący).
  3. W otwartym oknie dialogowym Właściwości połączenia symboli(Parametry łączenia symboli), Rys. 10.12, zaznacz pole Eksportuj w celu udostępniania środowiska wykonawczego(Zezwalaj na eksport w czasie wykonywania).
  4. W polu tekstowym Identyfikator wpisać nazwę (identyfikator) symbolu, pod którym zostanie wyeksportowany do filmu współwłaściciela; chociaż domyślnie jest to nazwa symbolu biblioteki, jeśli zawiera spacje, usuń je.
  5. W polu tekstowym URL wprowadź adres internetowy filmu źródłowego (tj. pliku SWF biblioteki współdzielonej).
  6. Jeśli wyeksportowany symbol ma być używany bezpośrednio z pierwszej klatki filmu będącego współwłaścicielem, wybierz Eksportuj w pierwszej klatce.
  7. Jeśli chcesz, aby wyeksportowany symbol był dostępny w ActionScript, wybierz Eksportuj do ActionScript.

Ryż. 10.12... Okno dialogowe Ustawienia symboli biblioteki współdzielonej

Aby korzystać z zasobów biblioteki udostępnionej Czas pracy w filmie współwłaścicielskim wymagane są następujące kroki:

  1. Otwórz bibliotekę tego filmu, wybierając z menu Okno Komenda Biblioteka.
  2. Z menu rozwijanego biblioteki wybierz polecenie Nowy symbol; w rezultacie na ekranie pojawi się okno dialogowe Utwórz nowy symbol(Utwórz nowy symbol), którego środkowa część ma format zbliżony do okna dialogowego Właściwości połączenia symboli(rys.10.13).
  3. W polu tekstowym Identyfikator wprowadź nazwę symbolu, który ma zostać zaimportowany do filmu będącego współwłaścicielem.
  4. W polu tekstowym URL wprowadź adres internetowy filmu źródłowego.


Ryż. 10.13... Okno dialogowe do ustawiania parametrów podzielonego znaku

Biblioteki statyczne i dynamiczne.

Biblioteki nazywane są „zbiorami” podprogramów lub obiektów, z reguły koncentrujących się na rozwiązywaniu zestawu powiązanych problemów. Z punktu widzenia organizacji i użytkowania biblioteki są statyczne i dynamiczne.

Biblioteki statyczne (biblioteki statyczne) mogą być zbiorem kodów źródłowych, dołączonych przez programistę do jego programu, lub w postaci prekompilowanych plików obiektowych, połączonych ze sobą w czasie kompilacji. W systemie Windows te pliki zwykle mają rozszerzenie<.lib>... W wyniku powiązania z biblioteką statyczną program zawiera wszystkie funkcje, z których korzysta, co zwiększa jego rozmiar, ale czyni go bardziej samowystarczalnym.

Biblioteki dynamiczne (biblioteka współdzielona, ​​biblioteka dołączana dynamicznie) są ładowane przez system operacyjny na „żądanie” uruchomionego programu już podczas jego wykonywania. Jeśli wymagana biblioteka została już załadowana do pamięci, ponowne ładowanie nie jest wykonywane. W takim przypadku ten sam zestaw funkcji lub obiektów bibliotecznych może być używany jednocześnie przez kilka uruchomionych programów, co umożliwia efektywne wykorzystanie zasobów pamięci RAM. Biblioteki współdzielone w systemie Windows zwykle mają rozszerzenie<.dll>.

Aby skorzystać z biblioteki, musisz powiedzieć kompilatorowi, że musisz ją podłączyć i wywołać funkcję z biblioteki (przy użyciu odpowiedniego pliku nagłówkowego), podczas gdy tekst źródłowy funkcji nie jest potrzebny.

Tworzenie biblioteki dynamicznej.

W programie Microsoft Visual Studio tworzenie projektu do kompilowania biblioteki dołączanej dynamicznie jest podobne do zwykłej procedury tworzenia projektu dla niemej aplikacji. Typ projektu to Win32 Console Application, dopiero teraz musisz wybrać pozycję "DLL" w kreatorze nowego projektu:

Jeśli w kreatorze nie ma takiej pozycji, można utworzyć zwykły projekt aplikacji konsolowej, a następnie ustawić jego typ we właściwościach projektu:

Właściwości konfiguracji => Ogólne => Typ konfiguracji: Biblioteka dynamiczna (.dll)

Declspec (dllexport) void display (const char * str);

#include "dlltest.h"

void display (const char * str)

Modyfikator __declspec (dllexport) umożliwia bibliotece wyeksportowanie określonej funkcji do użycia przez inne aplikacje.

W wyniku budowy projektu powstał plik biblioteki dynamicznej z rozszerzeniem<.dll>a także plik importu biblioteki z rozszerzeniem<.lib>... Biblioteka importu ma na celu ułatwienie późniejszego korzystania z biblioteki dynamicznej. Chociaż rozszerzenie pliku biblioteki importu jest takie samo jak standardowe rozszerzenie dla bibliotek statycznych, nie należy ich mylić.

Korzystanie z biblioteki dynamicznej.

Istnieją dwa sposoby wykorzystania biblioteki dynamicznej w programie. Niejawne łączenie obejmuje użycie biblioteki importu do określenia adresów funkcji dostarczanych przez bibliotekę. System operacyjny ładuje bibliotekę DLL po załadowaniu pliku wykonywalnego programu. Plik wykonywalny wywołuje wyeksportowane funkcje DLL, tak jakby funkcje były zawarte w samym pliku wykonywalnym.

W przypadku jawnego łączenia plik wykonywalny korzystający z biblioteki DLL musi wykonywać wywołania funkcji, aby jawnie ładować i zwalniać bibliotekę DLL oraz uzyskiwać dostęp do wyeksportowanych funkcji DLL. Plik wykonywalny klienta wywołuje wyeksportowane funkcje za pomocą wskaźnika funkcji.

Niezależnie od wybranej metody, plik wykonywalny może korzystać z tej samej biblioteki DLL. Co więcej, te mechanizmy nie wykluczają się wzajemnie, ponieważ podczas gdy jeden plik wykonywalny jest niejawnie połączony z biblioteką DLL, inny może być jawnie połączony.

Jako przykład użyjemy niejawnego mechanizmu linkowania (jako najprostszego), aby połączyć skonstruowaną prostą bibliotekę.

Stwórzmy osobny projekt zwykłej aplikacji konsolowej (jest to możliwe w ramach tego samego rozwiązania, co projekt samej biblioteki). Aby skorzystać z funkcji wyświetlania zaimplementowanej w bibliotece należy:

    Podłącz odpowiedni plik nagłówkowy „dlltest.h”. Aby to zrobić, użyj ścieżki do pliku bezpośrednio w dyrektywie #include (jeśli projekty znajdują się w tym samym folderze rozwiązania, lepiej użyć ścieżki względnej) lub we właściwościach projektu dodaj ścieżkę do tego pliku nagłówkowego w dziale

Parametry konfiguracyjne => C / C ++ => Ogólne => Dodatkowe katalogi dołączania

    Użyj odpowiedniego pliku biblioteki importu. Aby to zrobić, możesz użyć dyrektywy formularza (tutaj używana jest ścieżka względna do zbudowanego pliku lib).

Alternatywnie możesz dodać ścieżkę do pliku importowanej biblioteki we właściwościach projektu w obszarze

Parametry konfiguracyjne => Linker => Dane wejściowe => Dodatkowe zależności

Teraz stwórzmy główny plik programu o następującej zawartości:

#pragma komentarz (lib, "../Debug/dlltest.lib")

#include "dlltest.h"

wyświetlacz ("Cześć");

system („pauza”);

Aby pomyślnie uruchomić program, pozostaje tylko upewnić się, że odpowiednia biblioteka DLL jest dostępna. Po uruchomieniu programu system operacyjny wyszuka wszystkie wymagane pliki bibliotek w folderze, z którego uruchomiono program, a także w ścieżkach systemowych zdefiniowanych przez zmienną środowiskową PATH. Odpowiednio plik DLL zbudowanej biblioteki musi znajdować się albo w tym samym folderze co plik wykonywalny programu wywołującego, albo w jednym z folderów zdefiniowanych w PATH.