Subsections

3. Dokumentacja techniczna programu MONITOR

Rozdział ten zawiera opis algorytmów i struktury programu MONITOR. Oprócz tego została przedstawiona krótka charakterystyka niektórych mechanizmów komunikacji procesów w systemie UNIX.

3.1 Synchronizacja procesów poprzez sygnały

Sygnał [9] jest zdarzeniem asynchronicznym zewnętrznym w stosunku do procesu, który go otrzymuje. W systemie SunOS istnieje 31 typów sygnałów, które są zdefiniowane w pliku signal.h. Przykładowym sygnałem jest SIGKILL (bezwarunkowe zabicie procesu), czy też SIGINT (przerwanie z klawiatury - na terminalu ANSI jest to naciśnięcie klawisza DEL), lub SIGUSR1 i SIGUSR2 (sygnały definiowane przez użytkownika).

W procesie, który otrzyma, lub raczej wykryje sygnał, sterowanie jest przekazywane do procedury obsługi. Jest to albo procedura standardowa (oznaczona symbolicznie przez SIG_DFL) albo procedura użytkownika. Proces może także zażądać zignorowania sygnału (symbol SIG_IGN). Do wykonania tych operacji służy funkcja:

Wysłanie sygnału do procesu można natomiast zrealizować instrukcją: Przerwania mogą być wykorzystywane do odtwarzania wcześniejszego stanu procesu (cofania historii). Służą do tego następujące instrukcje: Do grupy funkcji, związanych z obsługą sygnałów należą także alarm() - ustawienie budzika, powodujące wysłanie sygnału SIGALRM po upływie określonego czasu oraz pause() - zawieszenie procesu aż do otrzymania sygnału.

3.2 Mechanizmy komunikacji procesów w systemie Unix

System Unix oferuje bogaty mechanizm komunikacji procesów. Ważniejszymi są: W pracy opisane zostaną trzy ostatnie mechanizmy. Opis pozostałych mechanizmów można znależć w pracy [16].

3.2.1 Potoki

Potok (pipeline) jest strukturą łączącą dwa procesy poprzez łącze (pipe), będące kolejką typu FIFO, do której można zapisywać dane i z której można dane pobierać. Łącze to można przedstawić jako plik o dwóch deskryptorach, z których jeden opisuje ``stronę'' służącą do pisania, a drugi do czytania. Podobnie jak w przypadku zwykłych plików istnieją tu dwa mechanizmy dostępu.

Mechanizm pierwszy oparty jest o poniższą funkcję:

Mechanizm drugi opiera się na następujących funkcjach: Mechanizm ten jest przydatny wówczas, gdy dane mają sobie przesyłać różne programy (jeden z nich jest wówczas żródłem danych lub programem do obróbki danych).

3.2.2 Pliki FIFO - named pipes

Mianem tym określa się mechanizm podobny do opisanego powyżej, lecz służący do porozumiewania się procesów niespokrewnionych. Łącze musi zostać jawnie utworzone jako plik specjalny poprzez funkcję mknod(). Dalsze mechanizmy porozumiewania się są analogiczne jak w przypadku plików. Usunięcie łącza następuje poprzez funkcję unlink().

3.2.3 Mechanizmy IPC

Mechanizmy IPC (Inter-Proces Communication) obejmują semafory ( semaphores), kolejki komunikatów (messages) oraz wspólną pamięć (shared memory). Semafory są zrealizowane jako wielowartościowe.

Wspólna pamięć jest bardzo wygodnym mechanizmem do przekazywania danych w środowisku niesieciowym. Każdy proces, dołączony do obszaru wspólnej pamięci widzi ją w swojej przestrzeni adresowej jako tablicę typu char[] i korzysta z danych, jak ze zwykłych zmiennych.

3.3 Zastosowanie mechanizmu kolejki komunikatów w programie MONITOR

Rozdział ten zawiera informacje o mechanizmie - kolejka komunikatów (messages), wykorzystanym przeze mnie w programie MONITOR.

3.3.1 Schemat komunikacji. Opis mechanizmu wymiany danych

Program MONITOR wykorzystuje mechanizm kolejek komunikatów, w celu przesłania danych do programu przeprowadzającego symulację. Program wyświetlający wyniki symulacji wysyła informację sterującą do programu symulacji. Ten ostatni na tej podstawie, zmienia wartość parametru (w przykładzie - temperatury) układu symulowanego (rysunek 3.1).

Aby rozpocząć wymianę danych należy najpierw utworzyć kolejkę komunikatów. Dokonujemy tego funkcją init_queue() , która tworzy nową kolejkę, ale tylko wtedy, gdy taka kolejka nie istniała. Jeżeli kolejka istniała już wcześniej, to nowa nie jest tworzona. Dane są wtedy wymieniane przez ``starą'' kolejkę. Jeżeli nie chcemy korzystać ze ``starej'' kolejki, przed uruchmieniem programu należy wydać polecenie rmq, usuwające kolejkę. Program, który jest dostarczycielem danych, musi wywołać funkcję init_queue() z argumentem Q_SERVER. (tu program MONITOR). Program odbierający dane (program symulacyjny) wywołuje wymienioną funkcję z argumentem Q_CLIENT.

Po uworzeniu kolejki można już wymieniać dane pomiędzy oboma procesami. Wymiana danych odbywa się w trybie asynchronicznym. Jeżeli program wyświetlający wyniki chce przesłać dane do programu symulacyjnego, wykonuje funkcję snd_data(), która umieszcza dane w kolejce i powiadamia proces odbierający dane o dostarczeniu danych, wysyłajç do niego sygnał SIGUSR1.

Jeżeli program dokonujący symulacji odbierze sygnał (SIGUSR1), że przybyły nowe dane, wtedy wywołuje funkcję rcv_data(), która odczytauje dane z kolejki i wpisuje je do zmiennej, której adres znajduje się w zmiennej New_data.

Przed zakończeniem pracy programu symulacyjnego, wywoływana jest funkcja leave_queue(), która informuje program prezentujący wyniki symulacji, że nie będziemy już odbierać danych z kolejki. W takim przypadku nie będzie więc możliwe nadawanie danych.

Więcej informacji na temat powyższych funkcji można znależć w podrozdziale 3.3.4.

Rysunek: Ogólny schemat komunikacji
\begin{figure}\unitlength=0.7mm\linethickness{0.4pt}
\begin{picture}(200,120)...
...,0){100}}
\put(110,98){\makebox(0,0){sygnał SIGUSR1}}
\end{picture}
\end{figure}

3.3.2 Pliki nagłówkowe

Aby korzystać z poniższych funkcji trzeba dołączyć następujące pliki nagłówkowe:

3.3.3 Opis funkcji systemowych mechanizmu - kolejka komunikatów

Do wymiany danych i przygotowania komunikacji służą następujące cztery funkcje [14] :

  1. int msgsnd( int msgid , struct msgbuf *msgp , int msgsz , int msgflg )
    Funkcja ta wysyła wiadomość do kolejki o numerze msgid, gdzie msgp jest wskażnikiem na strukturę zawierającą wiadomość, struktura ta ma następujące pola:

    1. long mtype - typ wiadomości.

    2. char mtext[1] - tekst wiadomości.

    mtype jest liczbą dodatnią, która może być używana przez proces odbierający wiadomości, w celu ich selekcji.

    mtext jest dowolnym tekstem o długości msgsz bajtów. msgflg specyfikuje akcje, wykonywane w sytuacjach wyjątkowych ( np. przekroczenie limitu ilości wiadomości).

    Jeżeli msgflg && IPC_NOWAIT ma wartość PRAWDA, wiadomość, nie będzie wysłana, a proces wywołujący tę funkcję, wykonuje następną instukcję po msgsnd.

    Jeżeli msgflg && IPC_NOWAIT ma wartość FAŁSZ, proces wywołujący zawisa, aż wiadomość będzie odebrana, lub np. kolejka zostanie usunięta z systemu. W takim przypadku, funkcja zwraca wartość -1 i ustawia zmienną errno na wartość EIDRM.

    Funkcja ta zwraca wartość 0, jeżeli wszystko przebiegło pomyślnie i -1 (ustawia dodatkowo zmienną errno na kod błędu ) w przeciwnym przypadku.

  2. int msgrcv( int msgid , struct msgbuf *msgp , int msgsz , long msgtyp , int msgflg )
    funkcja ta czyta wiadomość z kolejki o numerze msgid i zapisuje ją do struktury wskazywanej przez msgp (opis struktury j. w. ). Jeżeli długość wiadomości jest większa niż msgsz bajtów i msgflg && MSG_NOERROR ma wartość PRAWDA, wtedy wiadomość jest obcinana do wartości msgsz. Część obcinana jest usuwana, ale żadna informacja o obcięciu nie jest zwracana do procesu wykonującego tą funkcję.

    Zmienna msgtyp specyfikuje typ wiadomości do odebrania, według następującego schematu:

    msgflg określa akcje wykonywane, gdy w kolejce nie ma wiadomości o żądanym przez nas typie.

    Jeżeli msgflg && IPC_NOWAIT ma wartość PRAWDA, wtedy funkcja zwraca wartość -1 i podstawia pod zmienną errno wartość ENOMSG.

    Jeżeli msgflg && IPC_NOWAIT ma wartość FAŁSZ, wtedy proces wywołujący funkcję zawiesza się, aż:

    Funkcja ta zwraca liczbę bajtów wpisanych do mtext, gdy wszystko przebiegło pomyślnie i wartość -1 w przeciwnym przypadku i dodatkowo wpisuje do zmiennej errno kod błędu.

    Do utworzenia kolejki są służą poniższe funkcje:

  3. int msgget( key_t key , int msgflg )
    Funkcja ta tworzy nową kolejkę i zwraca identyfikator kolejki związanej z kluczem key.

    Kolejka i związane z nią struktury danych są tworzona jeżeli:

    W przeciwnym razie funkcja ta nie tworzy żadnej kolejki, a tylko zwraca identyfikator kolejki związanej z kluczem key.

    Identyfikator kolejki jest unikalną dodatnią liczbą całkowitą. Jest on zwracany jeżeli wszystko przebiegło pomyślnie, w przeciwnym przypadku funkcja zwraca wartość -1 i wpisuje do zmiennej errno kod błędu.

  4. key_t ftok( char *path , char id )
    Funkcja ta tworzy klucz, używany przez funkcję msgget.

    Zmienna path musi zawierać nazwę ścieżki do pliku, który jest w pełni dostępny dla procesu.

    Zmienna id - znak, który charakteryzującym projekt (unikalnie).

    Funkcja ta zwraca wartość key_t, jeśli wszytko przebiegło pomyślnie lub -1, jeśli ścieżka nie istnieje lub nie jest dostępna dla procesu.


3.3.4 Opis funkcji komunikacyjnych programu MONITOR

W tym rozdziale można znależć informacje o funkcjach zawartych w module com.c, które są odpowiedzialne za komunikację pomiędzy programem wyświetlającym wyniki symulacji molekularnej, a programem dokonującym symulacji.

Moduł com.c zawiera definicje następujących funkcji:

3.4 Moduł prezentacji wyników

W rozdziale tym są zawarte szczegółowe informacje o programie MONITOR takie jak opis konstrukcji programu, wykaz funkcji itp.

3.4.1 Konstrukcja programu

Program został napisany w języku C i skompilowany przy pomocy standardowego kompilalatora cc systemu SunOS. Obecna wersja została uruchomiona i przetestowana na stacji roboczej Sun SPARC SLC firmy Sun Microsystems. Procedury graficzne zostały zaczerpnięte z biblioteki Xview, pozostałe procedury korzystają ze standardowych bibliotek ANSI C.

Cały program główny znajduje się w pliku monitor.c. Funkcje komunikacyjne znajdują się w pliku com.c. Plik init.h zawiera deklaracje zmiennych i symboli dla procedur komunikacyjnych.

W programie przyjęto następującą konwencję dotyczącą nazw zmiennych i symboli:

  1. Zmienne zewnętrzne zaczynają się od dużej litery np. Bufor.

  2. Symbole (definiowane przez #define) są pisane wielkimi literami np. MARGIN.

  3. pozostałe zmienne i nazwy funkcji są pisane małymi literami.

Na początku programu znajdują się (w kolejności ich występowania): definicje symboli, deklaracje zapowiadające funkcji, definicje zmiennych zewnętrznych oraz deklaracje właściwe funkcji.

Funkcja main(), stanowi główny trzon programu. Z niej są wywoływane pozostałe funkcje. Na początku tej funkcji zostaje wykonana procedura xv_init() w celu zainicjowania programu w środowisku WINDOWS. Następnie za pomocą procedury xv_create() są tworzone:

Z kolei przydzielana jest pamięć dla bufora danych i tworzona kolejka komunikatów. W celu uruchomienia całego programu wykonujemy procedurę xv_main_loop().

3.4.2 Opis funkcji

Jak wspomniałem wcześniej z poziomu funkcji main() są wywoływane pozostałe funkcje. Rozdział ten zawiera ich opis.

3.5 Komunikaty błędów

W przypadku niepoprawnej obsługi programu wyświetlającego wyniki symulacji, w obszarze stopek i na standardowym wyjściu błędów są wyświetlane wszelkie komunikaty informujące o błędach zaistniałych podczas działania programu; Są one dodatkowo poprzedzane sygnałem dżwiękowym.
  1. ``File non available'' (Plik nie dostępny).
    Należy jeszcze raz załadować podany plik.

  2. ``Not enough memory to create a bufor'' (Brak pamięci na utworzenie bufora danych).
    Ponieważ bufor nie został utworzony, dlatego nie można załadować do niego żadnych danych z pliku, ani ich wyświetlać. W przypadku wystąpinia tego błędu (choć jest on mało prawdopodobny) należy zmienić długość bufora (symbol BUFOR_LENGTH) w programie żródłowym i skompilować cały program jeszcze raz.

  3. ``File empty'' (plik z danymi jest pusty).
    Należy poczekać, aż plik będzie zawierał jakieś dane i załadować go jeszcze raz.

  4. ``Queue not init'' (Brak kolejki komunikatów).
    W przypadku wystąpienia tego błędu, nie jest możliwa wymiana danych między procesami.