• CAM 6 - Współdzielone biblioteki w OS4

18.02.2005 22:58, autor artykułu: Hans-Jörg Frieden, Hyperion Entertainment
odsłon: 3592, powiększ obrazki, wersja do wydruku,

Współdzielone biblioteki w OS4 - autor Hans-Jörg Frieden

Współdzielone biblioteki mają za zadanie dostarczyć programiście tworzącemu aplikację interfejs, sposób porozumiewania się z systemem operacyjnym. Praktycznie wszystko w AmigaOS jest zaimplementowane we współdzielonych bibliotekach (z kilkoma wyjątkami). Klasyczne współdzielenie bibliotek składa się ze struktury danych w pamięci (zwane jest to Library Base) i tablicy skoków, która umieszczona jest na początku struktury danych.

Program, który chce wywołać funkcję biblioteczną najpierw musi pobrać wskaźnik do Library Base przy użyciu wywołania execa OpenLibrary. To zwróci wskaźnik do Library Base. Od kiedy tablica funkcji znajduje się na początku Library Base, dostęp do funkcji następuje przez negatywny offset z Library Base, z pierwszym wektorem zlokalizowanym na -6, drugim -12 itd. Te wektory zawierają prostą instrukcje JMP z 68k, po której następują cztery bajty funkcji adresowej (w związku z tym, że wektory są odległe od siebie o 6 bajtów). Program wykonuje bezpośredni skok podprocedury do tego wektora, który następnie rozgałęzia się i wędruje do właściwej procedury.

Dwa miejsca w powyższym paragrafie są ważne: "bezpośredni" i "instrukcja JMP 68k". "Bezpośredni" jest istotny ponieważ wskazuje, że nie istnieje nic pomiędzy samym programem, a kodem biblioteki. Nie następuje żadne oddziaływanie systemu. Nie zajdzie sytuacja, że jakikolwiek zewnętrzny mechanizm wedrze się do środka. Instrukcja "68k JMP" jest ważna z prostego powodu: klasyczne programy spodziewają się znaleźć tę instrukcję, gdy wykonują skok do kodu 68k. Program napisany pod PowerPC nie byłby w stanie tego zrobić, gdyż nie isnieje kod PowerPC na końcu wywołania. Powstaje więc dylemat: albo biblioteka jest pod 68k i nie może byc wykorzystana przez PowerPC (chyba, że program jest przygotowany na emulację każdego wywołania biblioteki), albo biblioteka jest pod PowerPC i nie może być wykorzystana przez 68k. Skoro powzięliśmy wyzwanie przeniesienia systemu operacyjnego na PowerPC, odnalezienie odpowiedzi na to pytanie jest bardzo ważne. Nasze rozwiązanie jest dobre z kilku powodów: biblioteki powinny być albo pod PowerPC lub pod 68k, lub obu typów. Program 68k powinien być zdolny wywołać bibliotekę PowerPC bez potrzeby jego rekompilacji. Podobnie, program PowerPC powinien potrafić wywołać bibliotekę bez potrzeby wiedzy na temat tego, czy biblioteka jest pod 68k czy PowerPC. Idąc dalej, powinno być mozliwe, zastąpienie funkcji/kodu 68k w bibliotece bez potrzeby rekompilacji programu korzystającego z tej biblioteki.

Jedna z możliwości zakłada, że punktem wejścia każdej biblioteki jest zawsze 68k. To byłoby jednak wyciąganiem starej koncepcji bez powodu i w większości wymagało by to zawsze wykorzystywania emulatora (nawet dla tylko kilku instrukcji) w celu wywołania funkcji z biblioteki. Prostym jest więc, że nie jest to dobre rozwiązanie. Wywołuje to niedopuszczalne koszty prędkościowe w przypadku wywołania każdej funkcji. Szczególnie irytujące może to być przy częstym dokonywaniu takich wywyołań. Założenie, że zawsze będziemy korzystać z biblioteki PPC również nie jest dorbym rozwiązaniem. Stare programy nie byłyby w stanie z nich korzystać, a posiadanie dwóch kopii biblioteki (PPC i 68k) również nie stanowi zbyt wielkiej pomocy, gdyż byłoby koszmarem dla programistów.

Biblioteki Multi-Interface

Rozwiązanie, które zastosowaliśmy nazwaliśmy bibliotekami "multi-interface" ("multi-interface libraries"). Jak sugeruje nazwa, biblioteka taka potrafi eksportować dowolną liczbę skoków do tabeli. Interfejs jest zawsze taki sam jak biblioteka; zawiera tabelę wskaźników funkcji i własną część danych. Nie ma instrukcji skoków. Program, który chce wywołać po prostu odczyta wskaźnik funkcji i rozgałęzi się w tamtym kierunku (ten sposób wywoływania funkcji przebiega z korzyścią dla architektury PowerPC).

Aby osiągnąć kompatybilność z aplikacjami, skok do tabeli znany z kodu 68k nadal istnieje. Na bibliotece stricte 68k, nakierowuje to nadal na kod 68k. Jakkolwiek możliwe jest przekierowanie wywołania do krótkiego odcinka, który odcina się od emulatora do natywnej wersji wywołania. W ten sposób, nawet stare programy uzyskają przyspieszenie poprzez wykorzystanie natywnych dla PowerPC funkcji systemu.

Exec library jest przykładem biblioteki, która została całkowicie napisana pod PowerPC, lecz umożliwia przekierowanie tablicy skoków. W związku z tym, program napisany pod 68k, wywołujący np. "AllocVec" ostatecznie trafia do wersji PowerPC tego wywołania, nawet tego nie zauważając. Dla aplikacji napisanej pod 68k, jest to całkowicie przezroczyste.

Wykorzystanie interface'u

Interface posiada strukturę przypominającą język C i nazwę ciągu podaną w ASCII. Struktura określa strukturę interface'u i jest czymś na styl zamiennika starego stylu #pragma znanego z SAS/C czy StormC. Przypomina to bardziej interface z języka Java lub klasy C++. Struktura interface'u określa rozłożenie i dostępne funkcje/metody interface'u - nie ich implementacje. W teorii, ta sama struktura interface'u może być dostarczona do więcej niż jednej biblioteki lub jedna biblioteka może wyeksportować wiele wersji/implementacji interface'u (później przyjrzymy się bardziej szczegółowo temu zagadnieniu, a także w kolejnych artykułach na temat rozszerzeń OS4 i architektury PCI, która w szczególny sposób wykorzystuje tę cechę).

Interface'y zazwyczaj podążają specyficznym schematem. Zwykle posiadają część opisową oraz przyrostek "IFace". Dla przykładu, interface Execa wywoływany jest przez "struct ExecIFace", Intuition - "struct IntuitionIFace" itd. Każda biblioteka rozpoznaje przynajmniej dwa interface'y. Jednym z nich jest wewnętrzny interface kontroli, który wykorzystywany jest tylko przez system (w zależności od tego czy biblioteka jest "prawdziwą" biblioteką czy urządzeniem, wyeksportuje LibraryManagerIFace lub DeviceManagerIFace). Kolejny interface'em zazwyczaj zawiera główną funkcjonalność biblioteki.

Aby uzyskać informacje z interface'u, program musi wywołać funkcję Execa - "GetInterface". Funkcja ta jako argument akceptuje zarówno wskaźnik library base jaki ciąg nazwy ASCII i tworzy i/lub zwraca wskaźnik do interface'u, który go żądał. W tym celu, interface'y są identyfikowane przez nazwę. Większość bibliotek eksportuje główny interface pod nazwą "główny" ("main"), lecz takie nazewnictwo nie jest wymagane. Dla przykładu, Exec library eksportuje "główny" interface jako typ "ExecIFace", a interface "mmu" jako typ "MMUIFace" (drugi interface kontroli wykorzystujący MMU na procesorze PowerPC).

Jak zwykle, Exec library to specjalny przypadek, z racji, że dostarcza wszystkich wymaganych funkcjonalności dla potrzeb operowania interface'em. W tej sytuacji, wskaźnik "ExecIFace" ustawiany jest przez kod startowy C library i jest natychmiast gotowy do użycia.

Wywołanie funkcji z interface'u odbywa się w prostej linii. Tablica skoków zawiera wszystkie funkcje interface'u w formie wskaźników funkcji. Wywołanie niektórych z nich wygląda podobnie do sposobu wywołania w C++ i poniekąd jest również podobne do metody wywołania w języku Java. Dla przykładu, przyjmijmy, że zmienna "IExec" zawiera wskaźnik do głównego interface'u Exec library, który jest typem "struct ExecIFace". Wywołanie funkcji "AllocVec" wyglądało by tak: memory = IExec->AllocVec(size, memory_flags);

Z czasu przed OS4 wyglądało by to w ten sposób: memory = AllocVec(size, memory_flags);

Jak widzimy, jedyna różnica to przekierowanie zmiennej "IExec". W starym systemie, mniej więcej to samo się działo (wywołanie wykonywane było przez przekierowanie wskaźnika SysBase). Różnica ta umożliwia jednak programiście wybrać tablicę skoków do której funkcja AllocVec powinna się odwoływać. Z początku może wydawać się to niepotrzebną stratą czasu, jest jednak kilka zalet tej metody wywołania: wywołanie jest wyraźne i dosłowne; programista posiada pełną kontrolę nad tym co jest wywoływane. Nie ma potrzeby na jakiekolwiek zabiegi zwane "library base swapping", dla przykładu, gdy program chce wywołać pluginy.

Biblioteki stanowią ogromną pomoc. Nie ma powodów dla których dwie biblioteki nie miałyby robić tego samego. Stary system wymagał zestawu nazw funkcji rozdzielających. Wywołanie jest inne niż w starym systemie. Umożliwia implementację natywnych bibliotek pod PowerPC przy zachowaniu starej, znanej z 68k tablicy skoków. W systemie zastosowano podejście modelu składowego zarówno dla systemu jak i aplikacji.

Przy ostatnim punkcie zatrzymamy się na chwilę. Model składowy posiada kilka zalet tradycyjnego programowania. Jedną z nich jest to, że można odwoływać się tylko do jednej metody (lub funkcji wywołania), które części składowe rozumieją. Rozszerzenia AmigaOS4 i architektura PCI polega właśnie na tej cesze. Z powodu wykorzystania interface'ów, możliwa jest transparentna obsługa dowolnego typu amigowego kontrolera PCI, jak również chipsetu Articia na płycie AmigaOne. Każde urządzenie PCI na szynie reprezentowane jest przez interface typu "PCI_Device". Jednakże informacja przekazywana przez interface tworzona jest oddzielnie dla każdego urządzenia, a w związku z tym mogą zostać ukryte wszystkie specyficzne szczegóły implementacji właśnie w interface'ie. Programista po prostu wywołuje pcidev->WriteConfigByte(.);

Interface wie jak sobie z tym radzić i nie ważne czy jest to karta sieciowa wetknięta w Prometheusza, karta dźwiękowa w Mediatorze lub karta AGP za mostkiem PCI->AGP na AmigaOne.

Kompatybilność interface'ów

Aby umożliwić funkcjonalność biblioteki 68k dla natywnych programów pod PPC, Exec implementuje mechanizm tworzący interface bibliotek starego stylu. Dla przykładu załóżmy, że program pod PowerPC chce skorzystać z biblioteki "foo.library", która została napisana pod 68k i nie wie nic na temat koncepcji interface'u. Posiada jednak tablicę skoków 68k, z której program PowerPC może skorzystać poprzez zintegrowany w OS4 emulator. Program może więc wywołać emulator dla wywołania funkcji biblioteki "foo.library", których chce użyć. Jednak gdy autor foo.library zdecyduje, że chce mieć natywną wersję tej biblioteki, program będzie musiał zostać przekompilowany lub uruchomiony w trybie emulacji.

Oczywistym rozwiązaniem jest wygenerowanie "głównego" interface'u dla foo.library (czyli "struct FooIFace), ze wszystkimi funkcjami z tablicy skoków 68k jako interface'u funkcji. Interface po prostu zaemuluje wszystkie punkty wejścia foo.library. Gdy Exec otrzyma wywołanie GetInterface, które w jakiś sposób nie będzie mogło zostać zaakceptowane (czy to z powodu starej biblioteki 68k czy też błędu GetInterface), wywołany zostanie RAMLIB (program odpowiedzialny za wczytywanie urządzeń i bibliotek z dysku), który będzie starał się dostać do interface'u. RAMLIB będzie próbował udostępnić to albo szukając kompilowanego pliku interface'u w LIBS: lub przez generację interface'u w locie z zainstalowanego pliku FD lub SFD.

W dalszym etapie, gdy foo.library jest zastępowana przez natywną wersją PPC, GetInterface uruchomi wywołanie w Execu, a program automatycznie będzie używał wersji PowerPC. Żadne przekompilowanie nie jest wymagane.

Podsumowanie

Obecnie, tylko "liznęliśmy" tematu dotyczącego tego, jakie możliwości daje nowy system interface'u bibliotek. Wspomniano, że interface może zawierać własną część z danymi, nie wiemy jednak jak tego użyć. Możliwym jest sklonowanie interface'u w locie lub zmodyfikowanie go w zależności od jego zawartości. Więcej na ten temat w następnych numerach CAM.



Tłumaczenie na podstawie Club Amiga Monthly - Sebastian Rosa.

Translated and reproduced by kind permission of Amiga Inc. Not for distribution beyond this site.

Przetłumaczone i opracowane za zgodą Amiga.Inc. Dystrybucja wyłącznie na stronach PPA.

Club Amiga logo concept and artwork by Mark Rickan & Mohamed Moujami, winners of the Club Amiga Logo Contest.
Submitted text and images reproduced in Club Amiga Monthly are copyright by their respective authors.
All other text and images reproduced in Club Amiga Monthly are copyright 2003 Amiga Inc.
Content may not be reprinted or reproduced in part or in whole without express written consent of Amiga Inc.

    
dodaj komentarz
Na stronie www.PPA.pl, podobnie jak na wielu innych stronach internetowych, wykorzystywane są tzw. cookies (ciasteczka). Służą ona m.in. do tego, aby zalogować się na swoje konto, czy brać udział w ankietach. Ze względu na nowe regulacje prawne jesteśmy zobowiązani do poinformowania Cię o tym w wyraźniejszy niż dotychczas sposób. Dalsze korzystanie z naszej strony bez zmiany ustawień przeglądarki internetowej będzie oznaczać, że zgadzasz się na ich wykorzystywanie.
OK, rozumiem