biblioteki współdzielone
TRANSCRIPT
Projektowanie
oprogramowania
systemów BIBLIOTEKI WSPÓŁDZIELONE
plan
biblioteki programistyczne
statyczne
współdzielone
łączenie dynamiczne
interfejs biblioteki
wtyczki programowe
kompatybilność wsteczna
„DLL hell”
przegląd użytecznych bibliotek
biblioteki programistyczne
biblioteka – kolekcja zasobów i procedur/funkcji używanych
przez programy komputerowe, posiadająca ściśle
zdefiniowany interfejs
kod dostarczany przez bibliotekę może być wykorzystany przez wiele
niepowiązanych programów – reużycie kodu
program nie musi znać implementacji funkcji bibliotecznych – polega
na ich interfejsie
usługi dostarczane w ramach biblioteki zwykle dotyczą wspólnego
zakresu zagadnień, dotyczą tego samego tematu (zestawu
tematów)
rodzaje bibliotek biblioteki możemy podzielić ze względu na rodzaj łączenia (linking) - na
którym etapie tworzenia programu biblioteka jest łączona z programem
wykonywalnym
biblioteki statyczne (łączone statycznie) – stanowi kolekcję
skompilowanych plików obiektowych, które są wprost łączone do pliku
wykonywalnego podczas jego tworzenia, każdy plik wykonywalny
otrzymuje prywatną kopię kodu zawartego w bibliotece
biblioteki współdzielone (łączone dynamicznie) – kod biblioteki nie jest
kopiowany do pliku wykonywalnego, tylko dodawane są „łącza” do
biblioteki w osobnym pliku – ta sama biblioteka może być
współdzielona równocześnie przez wiele programów
porównanie bibliotek
statyczne
kopiowanie kodu
zmiana biblioteki wymaga
rekompilacji/ponownego łączenia każdego
programu, który z niej korzysta
otrzymany program wykonywalny składa się
z pojedynczego pliku (łatwość dystrybucji)
program wykonywalny nie wymaga łączenia
bibliotek w czasie wykonania – brak
opóźnień przy starcie
program jest samowystarczalny (self-
contained)
współdzielone
użycie współdzielonego kodu
zmiana biblioteki możliwa poprzez zastąpienie
pliku biblioteki, o ile jest zachowany spójny
interfejs binarny i wsteczna kompatybilność
program wykonywalny do działania wymaga
dodatkowych plików bibliotek
podczas uruchamiania programu następuje
etap łączenia/relokacji bibliotek, który wiąże się
z określonym kosztem czasu wykonania
poprawki w bibliotekach nie wymagają
instalowania nowych wersji programów, które z
nich korzystają
niższe użycie pamięci dyskowej (tylko 1 plik
biblioteki) i operacyjnej (obraz biblioteki jest
współdzielony pomiędzy procesami)
łączenie dynamiczne w programie wykonywalnym zapisywana jest nazwa używanej biblioteki
współdzielonej oraz nazwy symboli eksportowanych przez bibliotekę lub
indeksy do tablicy symboli biblioteki
po uruchomieniu programu, używa on usługi linkera dynamicznego
dostarczanej przez OS, do
zlokalizowania biblioteki
załadowania jej obrazu do przestrzeni adresowej
wykonania niezbędnych relokacji symboli
umieszczenia referencji do symboli z biblioteki w odpowiednich slotach tablicy symboli programu
uruchomienia kodu startowego biblioteki (np. DllMain(), dlinit(),
_declspec_(constructor))
ładowanie bibliotek w czasie
wykonania łączenie dynamiczne nie musi odbywać się podczas startu programu
– możliwe jest w dowolnym momencie jego wykonania
ładowanie w czasie wykonania (run-time linking)
niejawne – następuje automatycznie podczas pierwszego odwołania do
jakiegokolwiek symbolu z biblioteki o opóźnionym czasie ładowania (lazy binding,
delay loading) – przyspiesza czas uruchomienia programu
jawne – kod programu w sposób jawny wywołuje funkcje służące do ładowania
biblioteki i uzyskania dostępu do symboli o określonej nazwie – biblioteka nie musi
być dostępna do uruchomienia programu – jej funkcjonalność może być
wykorzystana opcjonalnie – „wtyczki”
ładowanie bibliotek w czasie
wykonania
POSIX Windows
ładowanie bibliotek w czasie
wykonania typ symbolu (prototyp funkcji, jej sposób łączenia, typ parametrów
i wyniku) musi być znany a’priori – błędny typ spowoduje
zniszczenie stosu i crash programu
nazwa symbolu musi być znana a’priori – w przypadku C++ nazwa
dekorowana – nieprzenośne, dlatego stosujemy zwykle łączenie
typu „extern C”
symbol może odnosić się do eksportowanej funkcji lub zmiennej
globalnej
interfejs biblioteki interfejs biblioteki to lista wszystkich eksportowanych symboli oraz ich typ
zwykle interfejs biblioteki jest dostarczany w postaci towarzyszących jej
plików nagłówkowych (.h), zawierających prototypy eksportowanych
funkcji/zmiennych globalnych
eksport symbolu z biblioteki
POSIX – domyślnie, wszystkie symbole zawarte w bibliotece są z niej eksportowane
(udostępniane klientom biblioteki) – przy dużych bibliotekach powoduje to znaczący
spadek wydajności
kompilatory zgodne z GCC udostępniają atrybut visibility określający czy symbol jest eksportowany
Windows - kompilator Visual C++ udostępnia dyrektywy dllexport i dllimport dla
określenia symboli eksportowanych i importowanych z bibliotek
eksportowanie symbolu na POSIX
kompilujemy całą bibliotekę z visibility=hidden (opcja -fvisibility=hidden)
używamy atrybutu visibility=„default” dla symboli, które chcemy eksportować
na Windows
dodajemy atrybut __declspec(dllexport) do symboli, które chcemy eksportować
w programie korzystającym z biblioteki używamy dla tych samych symboli
atrybutu __declspec(dllimport)
patrz przykład ->
eksportowanie
symboli
wtyczki programowe „wtyczka” to specjalny rodzaj biblioteki współdzielonej, która jest
zaprojektowana, aby być ładowana w czasie działania programu
wtyczki udostępniają zunifikowany interfejs oparty o symbolach o
znanych nazwach i/lub klasach z „czysto wirtualnym” interfejsem
wymagana zgodność interfejsu binarnego pomiędzy wtyczkami
ukrywanie implementacji
obiekty „nieprzezroczyste” (opaque)
dostęp do stanu i zachowań obiektu tylko poprzez wywołania funkcji,
enkapsulacja danych
unikanie funkcji o dekorowanych nazwach (C++ name mangling) dla
zapewnienia przenośności dostępu do symboli
wtyczki programowe
wtyczki programowe i
kompatybilność wsteczna gdyby implementacja klasy plugin_interface była jawna i użylibyśmy
funkcji nie-wirtualnych, wówczas jakakolwiek zmiana implementacji
powodowałaby, że dostęp do danych tej klasy wymagałby innego kodu
– zmiana interfejsu binarnego klasy
dla zapewnienia kompatybilności wstecznej, kolejne wersje klasy
plugin_interface muszą zachowywać dokładnie tą samą semantykę,
składnię (prototypy) oraz kolejność funkcji wirtualnych zdefiniowanych w
tablicy funkcji wirtualnych (vtable) poprzednich wersji klasy
kolejne wersje interfejsu plugin_interface powinny więc powstawać
poprzez dziedziczenie interfejsu z wersji wcześniejszej
kompatybilność wsteczna typowe założenia wersjonowania bibliotek
kod korzystający z wcześniejszej wersji biblioteki powinien działać z nowszą
wersją biblioteki, o ile nie zmienił się główny numer wersji
zmiany „łamiące” kompatybilność (breaking changes) powinny wiązać się ze zmianą głównego numeru wersji (major version number)
typowy format numerów wersji:
major_number.minor_number[.patch_number[.build_number]]
major_number – numer wersji interfejsu binarnego biblioteki, znaczna zmiana funkcjonalności lub paradygmatu projektowania/użycia interfejsu
zmiana minor_number – rozszerzenia funkcjonalności nie łamiące
kompatybilności wstecznej
zmiana patch_number – poprawki błędów nie rozszerzające interfejsu biblioteki
build_number – numer kompilacji, używany np. do namierzenia, w którym dniu
pojawił się błąd w kodzie
„dll hell” termin dll hell (piekło bibliotek łączonych dynamicznie) odnosi się do sytuacji, w której
w systemie istnieje kilka wersji tej samej biblioteki i nie jesteśmy w stanie określić, która z
nich jest ładowana przez nasz program wykonywalny – również niekompatybilne wersje
generalnie występuje to tylko na Windows ze względu na kiepską implementację
mechanizmu linkera dynamicznego
linker dynamiczny sprawdza również katalog roboczy programu i wszystkie lokalizacje na
ścieżce systemowej (PATH)
brak ścisłego mechanizmu wersjonowania bibliotek
zdarzało się że program instalował wcześniejszą wersję biblioteki w katalogu systemowym
nadpisując wersję późniejszą i powodując niedziałanie wszystkich innych programów z niej
korzystających
nikłe szanse na POSIX
linker dynamiczny sprawdza tylko ściśle określone lokalizacje w poszukiwaniu bibliotek
mechanizm wersjonowania bibliotek umożliwiający precyzyjne określenie, która wersja
interfejsu jest niezbędna do działania (np. 2.X,1.3.X itp.)
przegląd użytecznych bibliotek
(C/C++)
C++ - Boost zestaw bibliotek o bardzo szerokim zakresie tematycznym
gwarantują przenośne działanie na wielu platformach
zbudowane na bazie i rozszerzające bibliotekę standardową C++
jedne z najbardziej starannie zaprojektowanych i zaimplementowanych bibliotek dla
C++
biblioteki należące do Boost często stają się podstawą nowych specyfikacji w
rozwoju języka C++
ogółem kilkadziesiąt bibliotek dotyczących wszystkich możliwych aspektów
programowania
przystępując do rozwiązywania problemu zawsze warto sprawdzić, czy Boost już tego
nie robi
www.boost.org
Boost Boost.Thread – przenośna implementacja wątków i obiektów
synchronizacji (stała się podstawą nagłówka <thread> C++11)
Boost.Atomic – implementacja atomowych zmiennych (podstawa dla
C++11 <atomic>)
Boost.Chrono, Boost.Date Time – pomiar czasu, formatowanie i
parsowanie dat i interwałów
Boost.Filesystem – przenośne operacje w systemie plików
Boost.Format – formatowanie napisów
Boost.Functional – programowanie funkcjonalne
Boost.Interprocess – obiekty IPC
Boost Boost.Asio – przenośna biblioteka nieblokującej komunikacji sieciowej
i IPC
Boost.Lexical Cast – konwersja typów
Boost.Lockfree – kolejki nieblokujące
Boost.Log – logowanie błędów i wykonania programu
Boost.Math – funkcje matematyczne, m.in. interfejs C++ do biblioteki
BLAS (basic linear algebra subsystems)
Boost.Program Options – parsowanie plików konfiguracyjnych i
wiersza poleceń
Boost.Regex – wyrażenia regularne
Boost.Smart Ptr – „sprytne” wskaźniki zapobiegające wyciekom
pamięci
wiele wiele innych…
C++ - OpenCV biblioteki algorytmów przetwarzania grafiki komputerowej, ale nie
tylko
przenośne działanie na wielu systemach
m.in. operacje matematyczne na macierzach wielowymiarowych
algorytmy mocno zoptymalizowane, korzystające z operacji
wektorowych procesorów
wsparcie dla obliczeń na GPU
kilkanaście różnych bibliotek do różnych celów
jedno z najpotężniejszych i najszerzej stosowanych narzędzi tego typu
„wrappery” do m.in. Javy, Pythona
www.opencv.org
OpenCV core – operacje na macierzach, podstawowe typy graficzne,
rysowanie
imgproc – filtracja i transformacja obrazów, śledzenie obiektów
highgui – podstawowe elementy GUI
video – analiza ruchu, przepływ optyczny, usuwanie tła, filtracja
Kalmana
objdetect – wykrywanie obrazów
ml – uczenie maszynowe
portaudio przenośna biblioteka (C) służąca do obsługi interfejsów
dźwiękowych
wsparcie m.in. dla API ASIO2 – niskie opóźnienia
zunifikowany interfejs na Windows, Mac OS X, Linux i wielu
innych systemach
www.portaudio.com
dsp++ biblioteka służąca do tworzenia algorytmów CPS w języku C++ z
wykorzystaniem programowania uogólnionego (szablonów)
m.in.
FFT (+ wrapper dla FFTW)
filtry cyfrowe FIR i IIR (projektowanie i uruchamianie)
korelacja
OLA, splot
przetwarzanie dźwięku – pomiar głośności (LUFS), procesory dynamiki
buforowanie, partycjonowanie, zakładkowanie
operacje stałopozycyjne
działa również na platformach mobilnych (Android NDK, iOS)
https://bitbucket.org/andrzejc/dsp
fftw przenośna, zoptymalizowana biblioteka transformat FFT i innych
„fastest FFT in the west”
ciężki do ogarnięcia interfejs w C, ale olbrzymie możliwości
transformaty o rozmiarach pierwszysch
transformaty wielowymiarowe
DCT, MDCT, FHT
w C++ warto używać z wrapperem w dsp++
www.fftw.org
libsndfile przenośna (C) biblioteka do odczytu i zapisu plików dźwiękowych w
różnych formatach
m.in. WAV, AIFF, FLAC…
również format MAT pochodzący z Matlaba – łatwe przenoszenie
danych podczas implementacji algorytmów prototypowanych w
Matlabie
w C++ warto używać wrappera w dsp++
http://www.mega-nerd.com/libsndfile/
Qt przenośna biblioteka (C++) służąca do tworzenia GUI na wielu
platformach
zawiera również wiele innych funkcji poza GUI – komunikacja sieciowa,
multimedia
oryginalnie rozwijana przez Trolltech AG, potem Nokia – jest również
wersja OpenSource
zawiera język opisu UI QML i wsparcie dla JavaScript
prawdopodobnie najbardziej rozbudowane i najbardziej eleganckie UI
z wszystkich tego typu bibliotek
używa dodatkowego kompilatora metaobiektów – MOC –
skomplikowane użycie
wrapper dla Java
www.qt.io