|
Obraz jest skompilowanym kodem. Są trzy typy obrazu:
- Obraz apklikacji jest aplikacją. Każda aplikacja ma pojedynczy obraz aplikacji.
- Obraz biblioteki jest biblioteką dołączaną dynamicznie ("biblioteka współdzielona"). Większość aplikacji dołącza biblioteki systemowe (libroot.so, libbe.so, i tak dalej), które dostarcza Be.
- Obraz wtyczki jest obrazem, który ładuje Twoja aplikacja gdzy jest ona uruchomiona. Symbole z obrazu wtyczki są dołączane a odniesienia są ustalane gdy obraz jest ładowany. Obraz wtyczki dostarcza "rozszerzonego łączenia dynamicznego" poza tymi z DLL (biblioteki dołączanej dynamicznie - przyp. tłum.).
Poniższe sekcje wyjaśniają jak ładować i uruchamiać obraz aplikacji, jak tworzyć biblioteki współdzielone i jak tworzyć i łądować obrazy wtyczek.
Ładowanie obrazu aplikacji jest jak uruchamianie "podprogramu". Obraz, który ładujesz jest odpalany w bardzo podobny sposób jak jest odpalany przez podwójne kliknięcie w Tracker'ze lub jak jest odpalany z wiersza poleceń. Działa on w swoim własnym zespole - nie współdzieli on przestrzeni adresowej z aplikacją z której był odpalony - i ogólnie, prowadzi własne życie.Aplikacja może być ładowana jako obraz aplikacji; nie potrzebujesz specjalnuych instrukcji komplikacji lub inaczej manipulować binariami. Jedynym żądaniem obrazu aplikacji jest to, że musisz mieć funkcję main().
Aby załadować obraz aplikacji, wywołaj funkcję load_image():
thread_id load_image(int32 argc,
const char **argv,
const char **env)Dwa pierwsze argumenty funkcji identyfikują obraz aplikacji (plik), który chcesz odpalić - wrócimy do tego za chwilę. Mając zlokalizowany plik, funkcja tworzy nowy zespó, zapoczątkowuje główny wątek i zwraca Ci identyfikator thread_id wątku. Wątek nie działą: aby dokonać jego uruchomienia przekazujesz thread_id do resume_thread() lub wait_for_thread() (jak wyjaśniono w rozdziale "Wątki i zespoły").
Para argumentów argc/argv jest kopiowana i posyłana do funkcji main() nowego wątku:
- Pierwszy łańcuch znaków w tablicyargv musi być nazwą pliku obrazu, który chcesz odpalić; load_image() używa tego łańcucha znaków do odnalezienia pliku. Instalujesz (wprowadzasz - przyp. tłum.) inne argumenty, które chcesz mieć w tablicy i zakańczasz tablicę wprowadzając znak NULL. argc jest ustawiany na liczbę wejść w tablicy argv (nie uzwględniając znaku NULL). Wywołujący jest odpowiedzialny na zwolnienie tablicy argv po zwróceniu przez funkcję load_image() wartości (pamiętaj - tablica jest kopiowana przed przekazaniem jej do nowego wątku).
- envp jest tablicą zmiennychśrdowiskowych,które są również przekazywane do funkcji main(). Zwykle używasz globalnego wskaźnika environ (który musi być zadeklarowany jako extern - patrz na przyklad podany poniżej). Możesz, oczywiście, utworzyć swoją własną tablicę zmiennych środowiskowych: jak z tablicą argv, tablica envp powinna być zakończona wprowadzeniem znaku NULL a Ty musisz zwolnić tablicę gdy funkcja load_image() zwróci wartość (jest tak, jeśli przydzieliłeś ją sobie sam - nie próbuj zwalniać environ).
Poniższy przykład demonstruje typowe użycie funkcji load_image(). Najpierw dołączamy odpowiednie pliki i deklarujemy konieczne zmienne:
#include <image.h> /* load_executable() */
#include <OS.h> /* wait_for_thread() */
#include <stdlib.h> /* malloc() */
char **arg_v; /* wybierz nazwę która nie koliduje z argv */
int32 arg_c; /* taki sam jak argc */
thread_id exec_thread;
int32 return_value;Zainstaluj (wprowadź - przyp. tłum.) w tablicy arg_v argumenty "wiersza poleceń". Symulejemy, że odpalamy program znaleziony w katalogu /boot/home/apps/adder, który pobiera dwie liczby całkowite, dodaje je do siebie i zwraca wynik jako kod wyjścia funkcji main(). Zatem są tutaj dwa argumenty: nazwa programu i wartości dwóch dodawanych liczb konwertowanych na łańcuchy znaków. Ponieważ są trzy argumenty, przyfdzielamy arg_v do przechowywania czterech wskaźników (aby pomieścić końcowy znak NULL). Następnie przydzielamy i kopiujemy argumenty.
arg_c = 3;
arg_v = (char **)malloc(sizeof(char *) * (arg_c + 1));
arg_v[0] = strdup("/boot/home/apps/adder");
arg_v[1] = strdup("5");
arg_v[2] = strdup("3");
arg_v[3] = NULL;Teraz, gby wszystko jest właściwie ustawione, wywołujemy load_image(). Po zwróceniu wartości przez tą funkcję, bezpiecznie jest już zwalniana przydzielona tablica arg_v:
exec_thread = load_image(arg_c, arg_v, environ);
while (--arg_c >= 0)
free(arg_v[arg_c]);
free(arg_v);W tym miejscu, exec_thread jest zawieszany (naturalny stan nowo zapoczątkowanego wątku). Żeby uzyskać jego zwróconą wartość, używamy funkcji wait_for_thread() do powiadomienia wątku, żeby rozpoczął działanie:
wait_for_thread(exec_thread, &return_value); Po zwróceniu wartości przez funkcję wait_for_thread(), wartość argumentu return_value powinna być równa 8 (t.j. 5 + 3).
Pierwotna dokumentacja o tworzeniu bibliotek współdzielonych jest dostarczana przez MetroWerks w ich podręczniku do programu CodeWarrior. Poza informacją, które tam znajdujesz, powinieneś być świadomym poprawek i zmian jakie zaszły:
- Nie możesz eksportować symbole swojej biblioteki poprzez znacznik -export all linker'a (konsolidatora). Zamiast tego, użyj dyrektywy __declspec() do eksportowania każdego symbolu. Makro jest opisane poniżej. Jeśli kompilujesz dla PPC (procesora PowerPC), musisz również eksportować symbole #pragma; aby to wykonać w BeIDE, przejdź do części Linker/PEF w oknie Settings i ustaw "Export Symbols" na "Use #pragma".
- Loader (program ładujący) szuka bibliotek poprzez zmienną środowiskową LIBRARY_PATH. Domyślna ścieżka wygląda jak ta poniżej:
$ echo $LIBRARY_PATH
%A/lib:/boot/home/config/lib:/boot/beos/system/libgdzie "%A" oznacza katalog, który zawiera aplikację, jaką odpala użytkownik.
Jeśli programujesz bibliotekę współdzieloną powinieneś jawnie wyeksportować każdy globalny symbol w Twojej bibliotece przez użycie makra __declspec(). Aby wyeksportować symbol, deklarujesz go w ten sposób...
__declspec(dllexport) typename ...gdzie "_declspec(dllexport)" jest literałem a type i name deklarują symbol. Kilka przykłladów:
__declspec(dllexport) char *some_name;
__declspec(dllexport) void some_func() {...}
class __declspec(dllexport) MyView {...}Aby zaimportować te symbole, aplikacja, która chce użyć Twoją bibliotekę musi "odwrócić" deklarację przez zamianę dllexport na dllimport:
__declspec(dllimport) char *some_name;
__declspec(dllimport) void some_func();
class __declspec(dllimport) MyView;Problem z tym systemem jest taki, że oznacza on dwa zestawy nagłówków, jeden do eksportowania (do budowania Twojej biblioteki) i kolejny do importowania (żeby klient Twojej biblioteki mógł ją użyć). Biblioteki Be używaja makr, zdefiniowanych w katalogu be/BeBuild.h, który przestawia przełącznik import/export więc pliki nagówkowe mogą być zunifikowane. Dla przykładu, podano tutaj takie makro dla libbe:
#if _BUILDING_be
#define _IMPEXP_BE __declspec(dllexport)
#else
#define _IMPEXP_BE __declspec(dllimport)
#endifKiedy budowana jest biblioteka libbe, prywatna dyrektywa kompilatora definiuje _BUILDING_be aby była ona różna od zera a _IMPEXP_BE eksportuje symbole. Gdy programista dołącza BeBuild.h, zmienna _BUILDING_be jest ustawiona na zero, więc _IMPEXP_BE jest ustawiona na import symboli.
Możesz chcieć naśladować ten system przez zdefiniowanie makr dla Twoich własnych bibliotek. Powoduje to, że masz do zdefiniowania swój przełącznik kompilatora (analoggiczny do _BUILDING_be). Ustaw przełącznik na wartość różną od zera gdy bufdujesz swoją bibliotekę a następnie przestaw go na zero, kiedy publikujesz nagłówki do użycia przez klientów biblioteki.
Obraz wtyczki jest nie do odróżnienia od obrazu biblioteki współdzielonej. Tworzenie wtyczek jest dokładnie takie jak tworzenie biblioteki współdzielonej, temat który wieje powyższym ale z dołaczeniem kilku mniejszych zmian:
- Loader (program ładujący) szuka wtyczek przez śledzenie ścieżek w zmiennej środowiskowej ADDON_PATH . Domyślnie ADDON_PATH wygląda podobnie jak poniżej :
$ echo $ADDON_PATH
%A/add-ons:/boot/home/config/add-ons:/boot/beos/system/add-ons
- Musisz wyeksportować Twoje symbole wtyczki i musisz je również oznaczyć jako extern "C". Zapewni to, że nazwy symboli nie będa zniekształcone przez kompilator.
Aby wyeksportować symbole wtyczki, zadeklaruj ich w ten sposób:
extern "C" __declspec(dllexport) void some_func();
extern "C" __declspec(dllexport) int32 some_global_data;Oznaczania klasy C++ jako extern wymaga więcej pracy. Nie możesz bezpośrednio oznaczyć klasy jako extern; zwykle tym co robisz jest utworzenie (oznaczenie jako extern) funkcji C, która opakowuje konstruktor klasy:
extern "C" __declspec(dllexport) MyClass *instantiate_my_class(); instantiate_my_class() jest implementowane aby wywołać konstruktor MyClass:
MyClass *instantiate_my_class()
{
return new MyClass();
}
Aby załądować wtyczkę do Twojej aplikacji, wywołaj funkcję load_add_on(). Funkcja pobiera ścieżkę dostępu (absolutną lub względną w bieżącym katalogu roboczym) do pliku wtyczki i zwraca liczbę image_id, która jest unikalnym identyfikatorem obrazu w całym systemie.Na przykład, powiedzmy, że utworzyłeś dodatkowy obraz, którym jest wprowadzony do pamięci plik /boot/home/add-ons/adder. Kod, który ładuje wtyczkę wygląda tak, jak ten:
/* Dla zwięzłości, nie sprawdzamy błędów. */
image_id addon_image;
/* Ładowanie wtyczki. */
addon_image = load_add_on("/boot/home/add-ons/adder");W przeciwieństwie do ładowania pliku wykonywalnego, ładowanie wtyczki nie powoduje utworzenia oddzielnego zespołu ani nie powoduje zapoczątkowania kolejnego wątku. Całym sednem ładowania wtyczki jest wprowadzenie obrazu do przestrzeni adresowej Twojej aplikacji, więc możesz wywoływać funkcje i bawić się zmiennymi, które definiuje wtyczka.
Po załadowaniu wtyczki do Twojej aplikacji, będziesz chciał sprawdzić symbole (zmienne i funkcje), które ona przynosi ze sobą. Aby uzyskać informacje o symbolu, wywołaj funkcję get_image_symbol():
status_t get_image_symbol(image_id image,
char *symbol_name,
int32 symbol_type,
void **location)Pierwsze trzy argumenty funkcji określają symbol, który chcesz uzyskać:
- Pierwszy argument jest identyfikatorem image_id z tej wtyczki, która posiada symbol.
- Argument drugi jest nazwą symbolu. Załkada to oczywiście, że znasz nazwę oraz to, że wtyczka ma zadeklarowaną tą nazwę jako extern. Ogólnie, używanie wtyczek oznacza tylko ten rodzaj współpracy.
- Trzeci argument jest stałą, która podaje typ symbol type symbolu. Są trzy typy, takie jak podano poniżej. Jeśli format wykonywalny nie jest rozróżniany pomiędzy tekstem i symbolami danych, wtedy możesz użyć jakiegokolwiek z tych trzech typów - wszystkie one będą takie same. Jeśli format powoduje rozróżnienie pomiędzy tekstem a danymi, wtedy możesz zapytać o określony typ lub możesz zapytać o B_SYMBOL_TYPE_ANY.
Stała Znaczenie B_SYMBOL_TYPE_DATA Dane globalne (zmienne) B_SYMBOL_TYPE_TEXT Funkcje B_SYMBOL_TYPE_ANY Symbol istniejący gdziekolwiek Te funkcje zwracają, przez referencję w końcowym argumencie, wskaźnik do adresu symbolu. Na przykład, powiedzmy, że kod wtyczki adder wygląda podobnie do tego:
extern "C" int32 a1 = 0;
extern "C" int32 a2 = 0;
extern "C" int32 adder(void);
int32 adder(void)
{
return (a1 + a2);
}Aby sprawdzić zmienne (a1 i a2), powinieneś wywołać get_image_symbol() w ten sposób:
int32 *var_a1, *var_a2;
get_image_symbol(addon_image, "a1", B_SYMBOL_TYPE_DATA, &var_a1);
get_image_symbol(addon_image, "a2", B_SYMBOL_TYPE_DATA, &var_a2);Uzyskujemy tutaj symbol dla funkcji adder():
int32 (*func_add)();
get_image_symbol(addon_image, "adder", B_SYMBOL_TYPE_TEXT, &func_add);Teraz, że uzyskaliśmy wszystkie symbole, możemy ustawić wartości dwóch dodawanych liczb i wywołać funkcję::
*var_a1 = 5;
*var_a2 = 3; int32 return_value = (*func_add)();
|
Be
Book,
...w ślicznym HTML...
dla BeOS wydanie 5
Copyright © 2000 Be, Inc. Wszelkie prawa zastrzeżone.