ACE
Adaptive Communication Environment
Grzegorz Marczyński
Email: gm153316@zodiac2.mimuw.edu.pl
Co jest w środku
Enkapsulacja funkcji systemowych w klasy C++
Obsługa wielowątkowości, wieloprocesorowości
Mechanizmy synchronizacji
Demultiplexing zdarzeń i ich obsługa
Tworzenie połączeń i serwisów sieciowych
Dynamiczne konfigurowanie rozproszonych serwisów komunikacyjnych
Gotowe do użycia schematy wspomagające tworzenie serwisów wyższego poziomu
Platformy
- Win32 (WinNT 3.5.x i 4.x i Win95 z MSVC++ lub Borland C++)
- UNIX (np. SunOS 4.x, Solaris 2.x, SGI IRIX 5.x i 6.x, HP-UX 9.x i 10.x, OSF/1 aka. DEC UNIX, AIX 4.x, Linux, SCO, UnixWare, NetBSD i FreeBSD),
- systemy czasu rzeczywistego (np. VxWorks, Chorus, LynxOS, and pSoS)
- MVS OpenEdition
Budowa warstwowa (rys 1)
- Warstw
a zależna od systemu
- Właściwie nie należy do ACE - Win32, UNIX
Enkapsulacja funkcji systemowych w C++ (C++ wrappers) / IPC_SAP
Jednolity interfejs bez względu na platformę systemową
Pokrywa następujące usługi systemowe:
- Wielowątkowość i synchronizację
Komunikację międzyprocesową
Demultiplexing zdarzeń
Łączenie dynamiczne
Pamięć dzieloną i pliki-mapy pamięci
Kategorie klas:
- IPC_SAP
(rys 2) - lokalne i zdalne IPC (gniazdka, TLI, łącza FIFO, strumienie, łącza nazwane)
SOCK_SAP (rys 3) - protokoły Internetu ACE_SOCK*) i UNIXa (ACE_LSOCK*)
- *Dgram (datagramy - bez stałego połączenia) vs. *ACE_Stream (strumień - ze stałym połączeniem)
- ACE_*_Acceptor (tworzenie połączeń ) vs. *Stream (dające dwukierunkowy przepływ danych)
- Zalety: wczesne wykrycia błędów "złego typu", niezależność od platformy, mniej kodu
- Przykład użycia: Użycie klasy ACE_SOCK_Dgram_Bcast do rozgłoszenia wiadomości do wszystkich serwerów nasłuchujących na podanym numerze
portu w lokalnej sieci:
int
main (int argc, char *argv[])
{
ACE_SOCK_Dgram_Bcast b_sap (sap_any);
char *msg;
unsigned short b_port;
msg = argc > 1 ? argv[1] : "hello world\n";
b_port = argc > 2 ? atoi (argv[2]) : 12345;
if (b_sap.send (msg, strlen (msg),b_port) == 1)
perror ("can't send broadcast"), exit (1);
exit (0);
}
TLI_SAP - System V Transport Layer Interface (analogicznie do SOCK_SAP, ale nie ma go w Win32)
SPIPE_SAP - Szybkie lokalne IPC (Win32 - na łączach nazwanych, UNIX - łącze - STREAM, serwer connld ??)
FIFO_SAP - UNIXowe łącza nazwane (FIFO)
Pliki z mapą pamięci - Mem_Map
System V IPC - np. semafory są bardziej intuicyjne i łatwiejsze w uzyciu (tylko signal i wait)
Inicjalizacja Serwisów - Acceptor/Connector - tworzenie połączenia i inicjalizacja (strona pasywna i aktywna)
- Rozdziela proces tworzenia połączenia i inicjalizacji od samego korzystania z połączenia sieciowego. Acceptor umożliwia pasywne oczekiwanie na połączenie (strona serwera), Connector zaś implementuje aktywną stronę tworzenia połączenia
Struktura:
- Service Handler -
implementuje obsługę serwisu i udostępnia metodę open aktywującą usługę
Acceptor - pasywne ustanawianie połączenia, tworzy peer_acceptor, który potem służy do transmisji danych, metoda open w Acceptor inicjalizuje peer_acceptor podłączając do odpowiedniego portu TCP pod lokalny adres IP
Connector - inicjuje połączenie do zdalnego akceptora
Dispatcher - przydziela żądania połączenia do Acceptora, pozwala, aby wiele obiektów typu Acceptor nasłuchiwało na połączenia; do Connectora Dispatcher podsyła informacje o zakończeniu procesu tworzenia połączenia, uruchamia metodę complete udostępnianą przez Connector - ma to sens jedynie przy asynchronicznym żądaniu uzyskania połączenia
Mechanizmy współbieżności - wielowątkowość, wieloprocesowość, mechanizmy synchronizacyjne
ACE_Mutex, ACE_Condition, ACE_Semapthore - enkapsulacja POSIX Pthreads
Non-recursive locks - definicja sekcji krytycznej
Recursive locks - możliwość rekurencji jeśli wątek jest właścicielem sekcji krytycznej w momencie ponownego wywołania
Mechanizmy zarządzania pamięcią - dynamiczne przydzielanie i zwalnianie pamięci lokalnej i dzielonej
Możliwa integracja z CORBĄ (np. jedno- i wielowątkowym Orbixem)
Wzorce i schematy
- Reactor
- rozwikływanie zdarzenia (demultiplexing) i wywoływanie obsługi
- Reactor obsługuje żądania usług, które przychodzą współbieżnie to aplikacji od wielu klientów. Każda usługa, którą aplikacja świadczy jest reprezentowana przed odrębny program obsługi zdarzenia (event handler), który jest odpowiedzialny za obsłużenie zdarzenia. Do rozwikłania, który program obsługi odpowiada za
zdarzenie służy synchroniczny demultiplekser, za uruchomienie programu obsługi - initiation dispatcher.
Struktura (rys. 4):
- Identyfikatory zasobów (handle)
- oznaczają zasoby, którymi zarządza system operacyjny - połączenia sieciowe, otwarte pliki, zegary, obiekty synchronizacyjne.
Synchroniczny rozwikływacz zdarzeń (Synchronous Event Demultiplekser) -blokuje wątek czekając na zdarzenia; kończy działanie, gdy jest możliwe wykonanie operacji na identyfikatorze zasobów bez blokowania
Wywoływacz programów obsługi (Initiation Dispatcher) - określa interfejs do rejestracji, usuwania i wywoływania programów obsługi zdarzenia. Synchronous Event Demultiplexer przekazuje do Initiation Dispatcher informacje, kiedy zaszły zdarzenia, czyli: akceptacja, odczytywanie danych, wysyłanie danych, koniec czasu
Program obsługi zdarzenia (event handler) - określa interfejs dla programów obsługi odpowiednich dla specyfikacji
Konkretny program obsługi zdarzenia (concrete event handler) - implementuje konkretną obsługę zdarzenia
Działanie (rys. 5)
Implementacja:
Initiation Dispatcher - tablica konkretnych programów obsługi, pętla główna: UNIX - select(), Win32 - poll(),
Proactor - asynchroniczny demultiplekser i obsługa zdarzeń zakończenia
- Proaktor udostępnia rozwikływanie i wywoływanie wielu programów obsługi zdarzeń uaktywnianych przez zakończenie (completion) wywołań a
synchronicznych
Struktura (rys 6)
Główny wątek (proactive Inititator) - byt w aplikacji, który wywołuje operacje asynchroniczną, rejestruje program obsługi zakończenia operacji (Completion Handler) i uruchamiacz (Completion Dispatcher) wraz z procesorem operacji asynchronicznych (asynchronous Operation Processor), który poinformuje o zakończeniu operacji
Obsługa zakończenia wykonania (completion handler) - interfejs, który wraz z implementacją odrębną dla każdej aplikacji jest uruchamiany po zakończeniu asynchronicznej poeracji
Operacja asynchroniczna (asynchronous operation) - służy do wywołania żądań (wej/wyj, zegar) w imieniu aplikacji; jak aplikacja wywoła operację asynchroniczną jest ona wykonywana bez obciążenia czasowego dla wątku aplikacji; po zakończeniu operacji asynchronicznej procesor operacji asynchronicznych powiadamia o tym Completion Dispatcher
Procesor operacji asynchronicznych (asynchronous operation processor) - jednostka wykonująca operacje asynchroniczne, az do zakończenia; najczęściej implementowana przez system operacyjny
Uruchamiacz programu obsługi zdarzenia zakończenia (Completion Dispatcher) - jest odpowiedzialny za wywołanie programu obsługi zdarzenia, gdy zakończy się wykonywanie operacji asynchronicznej
Działanie (rys. 7)
Wady:
Trudności z odpluskwianiem
Brak kontroli nad kolejnością wykonań zleconych operacji asynchronicznych
Implementacja:
API do wywołań
Procesor Operacji Asynchronicznych - współczesne systemy operacyjne (WinNT, POSIX) wspomagają wywołania asynchroniczne, ale jeśli tak nie jest, to można użyć jednej z technik zastępczych: wątki dedykowane,
Active_Object - asynchroniczne wykonywanie metod (oddzielenie wywołania od wykonania)
- Active Object rozprzęga wykonanie metody od jej wywołania (metoda jest wykonywana w innym wątku niż nastąpiło wywołanie). W ten sposób osiąga się prostszą synchronizację dostępu do zasobów dzielonych z różnych wątków.
Struktura (rys. 8)
- Interfejs klienta (client interface)
- interfejs metod dostępnych dla klienta, wywołanie metody powoduje skonstruowanie i umieszczenie w kolejce obiektu metody do asynchronicznego wywołania
Obiekty metod (method objects) - jest tworzony dla każdego wywołania metody z interfejsu klienta, każdy obiekt zawiera w sobie kontekst wywołania metody
Kolejka aktywacyjna (activation queue) - kolejka priorytetowa zwierająca obiekty metod czekające na wywołanie, jest zarządzana przez Zarządcę/planera
Zarządca/planer (scheduler) - "meta-obiekt", który zarządza kolejką aktywacyjną, wykonanie kolejnych metod zależy od warunkowych synchronizacji wątków wykonujących
Reprezentacja zasobów (resource representation) - dzielony zasób, który jest modelowany przez Active Object; zazwyczaj definiuje właściwe metody, które dostępne są przez interfejs klienta; może również zawierać inne metody pomocnicze dla zarządcy, aby określić kolejnośc wykonania metod z kolejki
Identyfikator wyniku (return handle) - jest zwracany, gdy metoda jest wywoływana z interfejsu klienta; umożliwia aplikacji odzyskanie wyniku funkcji
Współdziałanie elementów składowych (rys. 9)
Wady
potencjalnie zwiększa liczbę przełączeń kontekstu, kopiowania danych i narzutów synchronizacyjnych
trudności z odpluskwianiem z powodu niedeterminizmu zarządcy
Service Configurator - możliwośc dynamicznego łączenia i rekonfiguracji serwisów
- Service Configurator udostępnia możliwość dynamicznego łączenia (explicit dynamic linking z SunOS), rozszerza funkcjonalność konfiguracji "demonów" (inetd na UNIX, Service Control Menager w WinNT) wspomagając statyczną i dynamiczną konfigurację współbieżnego, wielo serwisowego oprogramowania komunikacyjnego, wiadomości przychodzące do zdefiniowanych portów przydziela do wyspecyfikowanych w aplikacji programów obsługi (dispatcher)
Struktura (rys. 10)
- Service_Object - hierarchia dziedziczenia
Shared_Object abstrakcyjna klasa główna - określa interfejs dla dynamicznie łączonych obiektów obsługi serwisu do przestrzeni adresowej aplikacji; udostępnia trzy abstrakcyjne metody: init, fini i info; zaimplementowanie tych metod w konkretnych podklasach Shared_Object zapewnia właściwe łączenie, inicjalizację, identyfikację i odłączanie serwisów w schemacie Service Configurator
Service_Object podklasa abstrakcyjna - podklasa Shared_Object i Event_Handler; w konkretnych podklasach należy zaimplementować metody suspend i resume, które tu są abstrakcyjne; metody te są automatycznie wywoływana przez Sevice Configurator w odpowiedzi na zdarzenia zewnętrzne; dodatkowo konkretna podklasa musi zaimplementować metody init, fini, info i get_handle, co wynika z definicji nadklas (Shared_Object i Event_Handler)
Specyficzne dla aplikacji konkretne podklasy - definiuje sześć ww metod i implementuje funkcje specyficzne dla danej aplikacji
Service Repository - aby zapewnić swobodną możliwość obsługiwania wielu serwisów Service Repository przechowuje opisy wszystkich serwisów (jako znaki ASCII) oraz obiekty instancjonujące konkretne podklasy Service_Object; dla każdego obiektu przechowuje wskaźniki do kodu niezależnego od aplikacji, co umożliwia ładowanie, inicjalizację, zawieszanie, wznawianie i odwoływanie obiektów dynamicznie (bądź statycznie oczywiście); dla łączonych dynamicznie obiektów Service Repository trzyma identyfikator pliku (otwartego) zawierającego obiekt dynamiczny; dostępny jest również iterator umożliwiający swobodny dostęp do wszystkich trzymanych obiektów bez naruszania hermetyzacji
Service Config - umożliwia dynamiczną lub statyczną konfigurację przechowywanych w Service Repository usług; używa pliku konfiguracyjnego (np.: svc.conf); każda aplikacja może być skojarzona z konkretnym plikiem svc.conf , ale jeden plik konfiguracyjny może obsługiwać wiele aplikacji na raz
- Gramatyka opisująca plik svc.conf:
<svcconfigentries> ::=
svcconfigentries svcconfigentry
| NULL
<svcconfigentry> ::= <dynamic> | <static>
| <suspend> | <resume> | <remove>
| <stream> | <remote>
<dynamic> ::= DYNAMIC <svclocation>
[ <parametersopt> ]
<static> ::= STATIC <svcname>
[ <parametersopt> ]
<suspend> ::= SUSPEND <svcname>
<resume> ::= RESUME <svcname>
<remove> ::= REMOVE <svcname>
<stream> ::= STREAM <stream_ops>
'{' <modulelist> '}'
<stream_ops> ::= <dynamic> | <static>
<remote> ::= STRING '{' <svcconfigentry> '}'
<modulelist> ::= <modulelist> <module>
| NULL
<module> ::= <dynamic> | <static>
| <suspend> | <resume> | <remove>
<svclocation> ::= <svcname> <type>
<svcinitializer> <status>
<type> ::= SERVICE_OBJECT '*' | MODULE '*'
STREAM '*' | NULL
<svcinitializer> ::= <objectname>
| <functionname>
<objectname> ::= PATHNAME ':' IDENT
<functionname> ::= PATHNAME ':' IDENT '(' ')'
<status> ::= ACTIVE | INACTIVE | NULL
<parametersopt> ::= STRING | NULL
Działanie (rys. 11)
Adaptive Service Executive (ASX) - upraszcza tworzenie oprogramowania komunikacyjnego przy wykorzystaniu wielu mechanizmów niskiego i wysokiego poziomu dostępnych bibliotece ACE
- Możliwości ASX
- Dowolny typ i liczba serwisów związanych z aplikacją
- Używany jest Service Configurator, więc czas i sposób instalowania i konfiguracji serwisów jest dowolny
- W ASX usługi mogą być udostępniane przez różnorodne typy procesów i wątków; poprzez oddzielenie funkcjonowania usługi od jej wywołania ASX udostępnia cały wachlarz możliwości przebiegu wykonania serwisu
- ... jak całe ACE
Strumienie (Stream Class Category) (rys. 12) - Obiekty umożliwiające aplikacjom współpracę i wykonywanie serwisów wewnątrz ASX. Obiekt Stream zawiera serie wewnętrznie połączonych obiektów Module, które mogą być łączone podczas instalacji lub dynamicznie w czasie wykonania. Modules są obiektami służącymi do dekompozycji architektury rozproszonej aplikacji na warstwy. Każda warstwa implementuje grupę powiązanych ze sobą (składających się na jakąś usługę) funkcji. Warstwa przeprowadzająca multiplexing i demultiplexing obiektów z wiadomościami między dwoma powiązanymi Streams jest implementowana przez obiekt Multiplexor - pojemnik C++ udostępniający mechanizmy do "routowania" wiadomości między modułami w zbiorze powiązanych strumieni. Każdy obiekt Module zawiera parę obiektów Task, które dzilą warstwę na stronę czytającą i piszącą (read-side & write-side). Task jest abstrakcyjną klasą, którą można ukonkretnić dla osiągnięcia celów specyficznych dla aplikacji.
Stream Class - Definiuje interfejs do strumienia. Obiekt Stream zawiera dwustronny interfejs typu get/put, który umożliwia aplikacjom dostać się do stosu jednego lub wielu powiązanych hierarchicznie modułów (Module). Aplikacja może przesyłać i odbierać wiadomości poprzez wewnętrznie połączone moduły. Stream implementuje również interfejs typu push/pop umożliwiający dynamiczne dodawanie i usuwanie obiektów klasy Module.
Module Class - Definiuje właściwą warstwę serwisów. Zawsze istnieją przynajmniej dwa podstawowe moduły Stream_Head i Stream_Tail.
Stream_Head - klasa buforująca wiadomości między aplikacją a strumieniem
Stream_Tail - klasa transformująca przychodzące siecią wiadomości na wyższy poziom, czytelny dla elementów wyższego rzędu w Stream oraz analogicznie zamieniająca wewnątrzstrumieniowy format wiadomości na pakiety sieciowe przy pisaniu
Task Abstract Class - definiuje stronę piszącą i czytającą poprzez implementacje abstrakcyjnych funkcji (open, close, put i svc)
Multiplexor Class - obsługuje połączenia i rozgałęzienie strumieni
Przykład zastosowania
- Serwer logujący
The ACE Orb - TAO
Dostępność:
ftp://sunsite.icm.edu.pl/pub/programming/ace/
http://www.cs.wustl.edu/~schmidt/ACE.html
Bibliografia
The ADAPTIVE Communication Environment: Object-Oriented Network Programming Components for Developing Client/Server Applications -- This gives an overview of the entire toolkit.
IPC_SAP: A Family of Object-Oriented Interfaces for Local and Remote Interprocess Communication -- This describes the ACE socket wrappers.
An OO Encapsulation of Lightweight OS Concurrency Mechanisms in the ACE Toolkit. -- This describes the ACE concurrency components.
Systems Programming with C++ Wrappers: Encapsulating Interprocess Communication Services with Object-Oriented Interfaces, C++ Report, September/October 1992.
Reactor: An Object Behavioral Pattern for Concurrent Event Demultiplexing and Event Handler Dispatching, Proceedings of the 1st Pattern Languages of Programs Conference, August 1994.
Proactor -- An Object Behavioral Pattern for Demultiplexing and Dispatching Handlers for Asynchronous Events. 4th Pattern Languages of Programming conference in Allerton Park, Illinois, September 2-5, 1997. (with Tim Harrison, Irfan Pyarali), and Thomas Jordan
Acceptor and Connector -- A Family of Object Creational Patterns for Initializing Communication Services. Presented at the European Pattern Language of Programs conference, July 10-14, 1996, Kloster Irsee, Germany, as well as appeared as a chapter in the book Pattern Languages of Program Design 3.
Active Object: an Object Behavioral Pattern for Concurrent Programming, Proceedings of the 2nd Pattern Languages of Programs Conference, September 1995
Service Configurator, C++ Report, SIGS, Vol. 9, No. 6, June, 1997.
ASX: an Object-Oriented Framework for Developing Distributed Applications, 6th USENIX C++ Conference, April 1994.