===== Studium podyplomowe Sieci komputerowe ===== ==== Konfiguracja PVM ==== 1. Dodanie w pliku ~/.rhosts nazw komputerów w celu umożliwienia zdalnego logowania bez podawania hasła. Postać pliku: nazwa_hosta nazwa_użytkownika inna_nazwa_hosta nazwa_użytkownika ... Nazwa użytkownika może być pominięta, jeżeli na komputerze zdalnym i lokalnym ta nazwa jest taka sama 2. Przetestowanie działania rsh: rsh nazwa_zdalnego_hosta W rezultacie wykonania polecenia powinniśmy zalogować się na wskazany komputer bez podawania hasła 3. Ustawienie zmiennych środowiskowych: W pliku ~/.bashrc dopisujemy: export PVM_ROOT=/usr/lib/pvm3 export PVM_ARCH=`$PVM_ROOT/lib/pvmgetarch` export PVM_HOME=$HOME/pvm3/bin/$PVM_ARCH/ 4. Stworzenie katalogu na pliki wykonywalne: mkdir -p $PVM_HOME 5. Przygotowanie pliku konfiguracyjnego dla PVM. Przykładowa postać pliku: lab-sec-1 lab-sec-2 lab-sec-3 6. Uruchomienie pvm: pvm nazwa_pliku_konfiguracyjnego 7. Odczytanie informacji o dostępnych hostach poleceniem conf 8. Dodanie nowego hosta poleceniem: add nazwa_hosta 9. Usunięcie jednego z hostów poleceniem: delete nazwa_hosta ==== Zadanie ==== Napisać aplikację równlogłą do wyznaczania przybliżonej wartości liczby π metodą Monte Carlo. Realizację można oprzeć na poniżej wersji sekewncyjnej: #include #include main(){ unsigned long n = 0, N = 0, i; double x, y; for( i = 0; i < 100000; i++ ){ x = drand48(); y = drand48(); N++; if ( x*x + y*y <= 1 ) n++; } // for printf( "%lf\n", 4.0*n/N ); } 1. Zrealizować aplikację zgodnie ze schematem master-slave: master uruchamia procesy slave, które odsyłają do niego wyniki obliczeń. Program mastera #include #include #include #define MAX_SLAVE 5 main() { int tids[MAX_SLAVE]; int num_tids; unsigned long n, N, suma_n = 0, suma_N = 0; num_tids = pvm_spawn("pi_slave", NULL, PvmTaskDefault, "", MAX_SLAVE, tids); if (num_tids <= 0){ fprintf(stderr, "Blad uruchomienia slave'ow\n"); pvm_exit(); exit(1); } for(;;){ pvm_recv(-1, -1); pvm_upkulong(&n, 1, 1); suma_n += n; pvm_upkulong(&N, 1, 1); suma_N += N; printf( "%lf\n", 4.0*n/N ); } } Program slave'a #include #include #include main(){ unsigned long n = 0, N = 0, i; double x, y; int master_tid, my_tid; my_tid = pvm_mytid(); srand48(my_tid); master_tid = pvm_parent(); for(;;){ for( i = 0; i < 100000; i++ ){ x = drand48(); y = drand48(); N++; if ( x*x + y*y <= 1 ) n++; } // for //printf( "%lf\n", 4.0*n/N ); pvm_initsend(PvmDataRaw); pvm_pkulong(&n, 1, 1); pvm_pkulong(&N, 1, 1); pvm_send(master_tid, 1); } } 2. Zastosować mechanizm dynamicznych grup procesów w celu udostępnienia możliwości dodawania nowych zadań w trakcie obliczeń. Program mastera #include #include #include #define MAX_SLAVE 5 main() { int tids[MAX_SLAVE]; int num_tids; unsigned long n, N, suma_n = 0, suma_N = 0; pvm_mytid(); pvm_joingroup("pi"); /* num_tids = pvm_spawn("pi_slave", NULL, PvmTaskDefault, "", MAX_SLAVE, tids); if (num_tids <= 0){ fprintf(stderr, "Blad uruchomienia slave'ow\n"); pvm_exit(); exit(1); } */ for(;;){ pvm_recv(-1, -1); pvm_upkulong(&n, 1, 1); suma_n += n; pvm_upkulong(&N, 1, 1); suma_N += N; printf( "%lf\n", 4.0*n/N ); } } Program slave'a #include #include #include main(){ unsigned long n = 0, N = 0, i; double x, y; int master_tid, my_tid; my_tid = pvm_mytid(); srand48(my_tid); //master_tid = pvm_parent(); master_tid = pvm_gettid("pi", 0); for(;;){ for( i = 0; i < 100000; i++ ){ x = drand48(); y = drand48(); N++; if ( x*x + y*y <= 1 ) n++; } // for //printf( "%lf\n", 4.0*n/N ); pvm_initsend(PvmDataRaw); pvm_pkulong(&n, 1, 1); pvm_pkulong(&N, 1, 1); pvm_send(master_tid, 1); } } Sposób kompilacji cc pi_master.c -o pi_master -L$PVM_ROOT/lib/LINUX64 -lpvm3 -lgpvm3 cc pi_slave.c -o pi_slave -L$PVM_ROOT/lib/LINUX64 -lpvm3 -lgpvm3 3. Zmodyfikować aplikację w taki sposób, żeby obliczenia rozpoczynały się dopiero po uzyskaniu gotowości do działania przez co najmniej 4 procesy typu slave. ===== Oprogramowanie middleware ===== ==== Slajdy do wykładów ==== - Wprowadzenie {{slides:wstep_2s.pdf|2 slajdy/stronę}}, {{slides:wstep_3s.pdf|3 slajdy/stronę}}, {{slides:wstep_4s.pdf|4 slajdy/stronę}}, {{slides:wstep_6s.pdf|6 slajdów/stronę}} - Wywoływanie zdalnych procedur (RPC) {{slides:rpc_2s.pdf|2 slajdy/stronę}}, {{slides:rpc_3s.pdf|3 slajdy/stronę}}, {{slides:rpc_4s.pdf|4 slajdy/stronę}}, {{slides:rpc_6s.pdf|6 slajdów/stronę}} - Dostęp do zdalnych obiektów w środowisku Java (Java RMI) {{slides:rmi_2s.pdf|2 slajdy/stronę}}, {{slides:rmi_3s.pdf|3 slajdy/stronę}}, {{slides:rmi_4s.pdf|4 slajdy/stronę}}, {{slides:rmi_6s.pdf|6 slajdów/stronę}} - Współbieżność w środowisku Java {{slides:java-threads_2s.pdf|2 slajdy/stronę}}, {{slides:java-threads_3s.pdf|3 slajdy/stronę}}, {{slides:java-threads_4s.pdf|4 slajdy/stronę}}, {{slides:java-threads_6s.pdf|6 slajdów/stronę}} - Idempotentność i bezstanowość (NFS) {{slides:idemp-stateless_2s.pdf|2 slajdy/stronę}}, {{slides:idemp-stateless_3s.pdf|3 slajdy/stronę}}, {{slides:idemp-stateless_4s.pdf|4 slajdy/stronę}}, {{slides:idemp-stateless_6s.pdf|6 slajdów/stronę}} - Message Oriented Middleware (JMS) {{slides:mom_jms_2s.pdf|2 slajdy/stronę}}, {{slides:mom_jms_3s.pdf|3 slajdy/stronę}}, {{slides:mom_jms_4s.pdf|4 slajdy/stronę}}, {{slides:mom_jms_6s.pdf|6 slajdów/stronę}} - Przestrzeń krotek (JavaSpaces) {{slides:ts_2s.pdf|2 slajdy/stronę}}, {{slides:ts_3s.pdf|3 slajdy/stronę}}, {{slides:ts_4s.pdf|4 slajdy/stronę}}, {{slides:ts_6s.pdf|6 slajdów/stronę}} ==== Zagadnienia laboratoryjne ==== - Sun RPC/XDR {{slides:sun-rpc-xdr_2s.pdf|2 slajdy/stronę}}, {{slides:sun-rpc-xdr_3s.pdf|3 slajdy/stronę}}, {{slides:sun-rpc-xdr_4s.pdf|4 slajdy/stronę}}, {{slides:sun-rpc-xdr_6s.pdf|6 slajdów/stronę}}\\ [[RPC:licznik|Zdalny licznik w Sun RPC]] - Wątki w Javie\\ [[Java:prod-kons|Synchronizacja producenta i konsumenta w Javie]] - Java RMI\\ [[RMI:licznik|Zdalny licznik w Java RMI]]\\ [[RMI:bufor|Bufor 1-elementowy w Java RMI]]\\ [[RMI:bufor_juc|Bufor 1-elementowy w Java RMI z użyciem java.util.concurrent do synchronizacji]] - JMS ===== Narzędzia przetwarzania rozproszonego ===== ==== Slajdy do wykładów ==== - Wprowadzenie {{slides:wstep_2s.pdf|2 slajdy/stronę}}, {{slides:wstep_3s.pdf|3 slajdy/stronę}}, {{slides:wstep_4s.pdf|4 slajdy/stronę}}, {{slides:wstep_6s.pdf|6 slajdów/stronę}} - Message Passing Interface (MPI) {{slides:mpi-tutorial.pdf|Tutorial Williama Groppa (1 slajd/stronę)}} - Wywoływanie zdalnych procedur (RPC) {{slides:rpc_2s.pdf|2 slajdy/stronę}}, {{slides:rpc_3s.pdf|3 slajdy/stronę}}, {{slides:rpc_4s.pdf|4 slajdy/stronę}}, {{slides:rpc_6s.pdf|6 slajdów/stronę}} - Współbieżność w środowisku Java {{slides:java-threads_2s.pdf|2 slajdy/stronę}}, {{slides:java-threads_3s.pdf|3 slajdy/stronę}}, {{slides:java-threads_4s.pdf|4 slajdy/stronę}}, {{slides:java-threads_6s.pdf|6 slajdów/stronę}} - Wywoływanie zdalnych metod * dostęp do zdalnych obiektów w środowisku Java (Java RMI) {{slides:rmi_2s.pdf|2 slajdy/stronę}}, {{slides:rmi_3s.pdf|3 slajdy/stronę}}, {{slides:rmi_4s.pdf|4 slajdy/stronę}}, {{slides:rmi_6s.pdf|6 slajdów/stronę}} * integracja oparta na zdalnych obiektach (CORBA) - Message Oriented Middleware (JMS) {{slides:mom_jms_2s.pdf|2 slajdy/stronę}}, {{slides:mom_jms_3s.pdf|3 slajdy/stronę}}, {{slides:mom_jms_4s.pdf|4 slajdy/stronę}}, {{slides:mom_jms_6s.pdf|6 slajdów/stronę}} - Przestrzeń krotek (JavaSpaces) {{slides:ts_2s.pdf|2 slajdy/stronę}}, {{slides:ts_3s.pdf|3 slajdy/stronę}}, {{slides:ts_4s.pdf|4 slajdy/stronę}}, {{slides:ts_6s.pdf|6 slajdów/stronę}} - Ada * Wprowadzenie do języka Ada na podstawie opracowania Simona Johnstona "Ada dla programistów C/C++" {{slides:ada95-4-cpp.pdf|Podstawy}}, {{slides:ada95-4-cpp-packages.pdf|Pakiety}} * {{slides:ada95-pointers.pdf|Wskaźniki}} * {{slides:ada95-wsplobieznosc.pdf|Współbieżność}} * {{slides:ada95-distcomp.pdf|Przetwarzanie rozproszone (Aneks E)}} [[npr:synchronizacja#zadania|Zadania z synchronizacji]] ===== Środowiska przetwarzania rozproszonego (SUM SK) ===== ==== Slajdy do wykładów ==== do uzupłenienia ==== Zagadnienia laboratoryjne ==== === PVM (05.11.2016) === Przykładowy program korzystający z PVM: {{:example.tar.gz|}} == Konfiguracja PVM == 1. Dodanie w pliku ~/.rhosts nazw komputerów w celu umożliwienia zdalnego logowania bez podawania hasła. Postać pliku: nazwa_hosta nazwa_użytkownika inna_nazwa_hosta nazwa_użytkownika ... Nazwa użytkownika może być pominięta, jeżeli na komputerze zdalnym i lokalnym ta nazwa jest taka sama 2. Przetestowanie działania rsh: rsh nazwa_zdalnego_hosta W rezultacie wykonania polecenia powinniśmy zalogować się na wskazany komputer bez podawania hasła 3. Ustawienie zmiennych środowiskowych: W pliku ~/.bashrc dopisujemy: export PVM_ROOT=/usr/lib/pvm3 export PVM_ARCH=`$PVM_ROOT/lib/pvmgetarch` export PVM_HOME=$HOME/pvm3/bin/$PVM_ARCH/ 4. Stworzenie katalogu na pliki wykonywalne: mkdir -p $PVM_HOME 5. Przygotowanie pliku konfiguracyjnego dla PVM. Przykładowa postać pliku: lab-sec-1 lab-sec-2 lab-sec-3 6. Pobranie i rozpakowanie pliku {{:example.tar.gz|}} 7. Zbudowanie programu master i salve poleceniem make 8. Uruchomienie pvm: pvm nazwa_pliku_konfiguracyjnego 9. Odczytanie informacji o dostępnych hostach poleceniem conf 10. Dodanie nowego hosta poleceniem: add nazwa_hosta 11. Usunięcie jednego z hostów poleceniem: delete nazwa_hosta 12. Uruchomienie przykładowego programu: spawn -> master == Zadanie == Napisać aplikację równlogłą do wyznaczania przybliżonej wartości liczby π metodą Monte Carlo. Realizację można oprzeć na poniżej wersji sekewncyjnej: #include #include main(){ unsigned long n = 0, N = 0, i; double x, y; for( i = 0; i < 100000; i++ ){ x = drand48(); y = drand48(); N++; if ( x*x + y*y <= 1 ) n++; } // for printf( "%lf\n", 4.0*n/N ); } - Zrealizować aplikację zgodnie ze schematem master-slave: master uruchamia procesy slave, które odsyłają do niego wyniki obliczeń. {{:pi.tar.gz|}} - Zastosować mechanizm dynamicznych grup procesów w celu udostępnienia możliwości dodawania nowych zadań w trakcie obliczeń. {{:reduce.tar.gz|}} - Zmodyfikować aplikację w taki sposób, żeby obliczenia rozpoczynały się dopiero po uzyskaniu gotowości do działania przez co najmniej 4 procesy typu slave. === RPC === == Prosta aplikacja klienta dla rusers == 1. Pobranie implementacji serwera: {{:rtime.tar|}} 2. Skompilowanie i uruchomienie serwera. 3. Sprawdzenie listy zarejestrowanych programów RPC: /usr/sbin/rpcinfo -p 4. Implementacja klienta pobierającego aktualny czas (po stronie serwera). Wykorzystanie funkcji: int callrpc(char *host, unsigned long prognum, unsigned long versnum, unsigned long procnum, xdrproc_t inproc, char *in, xdrproc_t outproc, char *out); zadeklarowanej w pliku nagłówkowym: rpc/rpc.h Numery programu i procedury pobrać można z pliku nagłówkowego: rtime.h gdzie zdefiniowane są stałe: TIMEPROG TIMEVER_1 get_time Procedura get_time nie wymaga parametrów wejściowych (xdr_void), a jako wynik (parametr out) zwraca zmienną int (xdr_int) oznaczającą liczbę zalogowanych użytkowników. 5. Kompilacja programu klienta: gcc -o rtime_client rtime_client.c == Usługa zdalnego zabijania procesów == Zadanie: napisać aplikację (serwer+klient) umożliwiającą zdalne zabijanie procesów. Klient ma uruchamiać zdalną procedurę, która jako parametr przyjmuje PID procesu do zabicia. Procedura zdalna jako wynik zwraca int określający status zakończenia operacji (0 - proces został unicestwiony, -1 - wystąpił błąd) 1. Utworzenie pliku rkill.x z opisem usługi: program RKILL_PROG { version RKILL_VERS_1 { int rkill(int pid) = 1; } = 1; } = 0x22000000; 2. Wygenerowanie części kodu programem: rpcgen -a -N rkill.x Program wygeneruje następujące pliki: rkill.h - plik nagłówkowy z definicjami stałych rkill_client.c - szkielet aplikacji klienta rkill_server.c - szkielet aplikacji serwera rkill_clnt.c - stub klienta rkill_svc.c - stub serwera Makefile.rkill 3. Kompilacja z wykorzystaniem Makefile: make -f Makefile.rkill 4. Implementajca zabijania procesu w pliku rkill_server.c z wykorzystaniem funkcji: int kill(pid_t pid, int sig) (man 2 kill) 5. Dodanie wyświetlania statusu wykonania po stronie klienta (plik rkill_clietn.c) 6. Dodanie drugiego argumentu dla zdalnej procedury: numer wysyłanego sygnału a) zmiana definicji w pliku rkill.x: int rkill(int pid, int signum) = 1; b) ponowne wygenerowanie kodu programem rpcgen c) implementajca funkcjonalności serwera i klienta 7. Dodanie procedury echo, która pozwala na wypisanie po stronie serwera tekstu przesłanego przez klienta 8. Przetestowanie działania mechanizmu timeout poprzez: a) zabicie serwera b) opóźnienie wysłania odpowiedzi przez serwer przy użyciu funkcji: unsigned int sleep(unsigned int seconds); test dla komunikacji po udp oraz po tcp 9. Ustawianie timeout dla klienta: a) modyfikacja stałej: TIMEOUT zdefiniowanej w pliku: rkill_clnt.c b) wykorzystanie funkcji: bool_t clnt_control(CLIENT *cl, int req, char *info); req = CLSET_TIMEOUT - ustawianie wartości timeout req = CLSET_RETRY_TIMEOUT - ustawianie czasu dla ponawiania żądania (tylko UDP) Przykład: struct timeval tm = { 60, 0 }; clnt = clnt_create (host, RKILL_PROG, RKILL_VERS_1, "udp"); clnt_control(clnt, CLSET_TIMEOUT, &tm); == Wywołanie asynchroniczne == Zadanie: Zmodyfikować rkill tak, aby klient mógł kontynuować przetwarzanie bez oczekiwania na zakończenie obliczeń przez serwer (zakładamy, że klienta nie interesuje odpowiedź od serwera) === RMI === == HelloWorld == 1. Utworzyć plik z interfejsem programu HelloWorld import java.rmi.Remote; import java.rmi.RemoteException; public interface HelloWorld extends Remote { String getString(String name) throws RemoteException; } 2. Napisać aplikację klienta odwołującego się do zdalnego obiektu implementującego interfejs HelloWorld: a) rejestr, w którym zarejestrowany został zdalny obiekt znajduje się pod adresem wskazanym przez prowadzącego b) program klienta pobiera z rejestru referencję na zdalny obiekt korzystając z metody Naming.lookup(String name); c) program klienta wywołuje zdalną metodę //getString// jako argument podając tekst pobrany z linii komend przy uruchamianiu programu d) napis zwrócony przez metodę //getString// należy wypisać na standardowe wyjście e) skompilować program klienta javac *.java f) utworzyć plik client.policy z definicją polityk bezpieczeństwa: grant { permission java.security.AllPermission; }; g) uruchomić program klienta java -Djava.security.policy=client.policy HelloWorldClient 3. Napisać własną implementację serwera HelloWorld: a) przygotować klasę HelloWorldObj rozszerzający klasę UnicastRemoteObject oraz implementujący interfejs HelloWorld b) przygotować klasę HelloWorldServer, która w metodzie main utworzy instancję klasy HelloWorldObj, a następnie zarejestruje ten obiekt w rejestrze przy użyciu metody: Naming.rebind("//127.0.0.1:2100/HelloWorld") c) skompilować program d) uruchomić rejestr RMI rmiregistry nr_portu e) uruchomić i przetestować serwer java -Djava.rmi.server.hostname=127.0.0.1 -Djava.security.policy=server.policy HelloWorldServer 4. Przerobić program serwera tak, aby uruchamiał swój własny rejestr Registry registry = LocateRegistry.createRegistry(2100); registry.rebind("HelloWorld", obj); == Przekazywanie obiektów przez wartość i przez referencję == 1. Zaimplementować klasę ArgumentObject zawierającą prywatne pole //value// typu int oraz metody umożliwiające odczyt i nadpisanie wartości tego pola. Klasa powinna implementować interfejs java.io.Serializable 2. Umieścić implementację ArgumentObject w classpath klienta i serwera 3. Przerobić implementację //HelloWorldObj//, tak aby metoda getString przyjmowała jako argument obiekt klasy //ArgumentObject//, odczytywała wartość zdefiniowanego w tej klasie pola, nadpisywała wartość tego pola i zwracała odczytaną wcześniej wartość w postaci obiektu typu String. 4. Przerobić program klienta, tak aby tworzył obiekt //ArgumentObject// i podawał go jako argument wywołania metody //getString// ze zdalnego obiektu //HelloWorldObj//; następnie klient powinien wypisać odczytany String oraz aktualną wartość pola //value// z klasy //ArgumentObject// 5. Sprawdzić rezultat działania programu 6. Przerobić program, tak aby //ArgumentObject// przekazywany był przez referencję; w tym celu należy: a) zdefiniować interfejs //Argument// rozszerzający interfejs //Remote// i umieścić go w classpath serwera i klienta, b) przerobić implementację //ArguentObject//, tak aby rozszerzał //UnicastRemoteObjet// oraz implementował interfejs //Argument//, c) usunąć klasę //ArgumentObject// z classpath serwera 7. Uruchomić program i zaobserwować zmiany w działaniu == RemoteComputation == Zadanie: przygotować program umożliwiający wykonywanie po stronie serwera dowolnych obliczeń zleconych przez klienta: 1. Serwera udostępnia zdalny obiekt implementujący interfejs Compute public interface Compute { Object runTask(Task task, Args args); } 2. Task jest interfejsem reprezentującym zadanie obliczeniowe, posiada metodę //run(Args args)//, która jest wywoływana wewnątrz metody runTask 3. Klient dostarcza co najmniej dwie implementacje interfejsu Task, np. wykonujące dodawanie i mnożenie; obie klasy muszą implementować interfejs Serializable 4. Klient korzystając z rejestru pobiera referencję na zdalny obiekt Compute i wywołuje metodę //runTask// podając jako argument jedną z implementacji interfejsu Task; wynik zwrócony przez //runTask// jest wypisywany na standardowe wyjście 5. Klient dostarcza serwerowi klasy z implementacją Task poprzez http lub system plików, przykładowe wywołanie programu klienta wygląda wówczas następująco: java -Djava.rmi.server.codebase=file:///path/to/Task/implementation/ -Djava.security.policy=client.policy ComputeClient == Accounts == Zadanie: zaimplementować program do zdalnego wykonywania operacji na kontach bankowych 1. Serwer udostępnia zdalny obiekt umożliwiający pobranie listy kont bankowych public interface AccountList extends Remote { List getList() throws RemoteException; } 2. Interfejs //Account// rozszerza interfejs //Remote// i umożliwia przeprowadzenie podstawowych operacji na koncie public interface Account extends Remote { public Integer getBalance() throws RemoteException; //pobranie stanu konta public Integer add(Integer amount) throws RemoteException; //dodanie określonej kwoty do stanu konta public Integer withdraw(Integer amount) throws RemoteException; //pobranie określonej kwoty z konta } 3. Klient korzystając ze zdalnego obiektu implementującego //AccountList// pobiera listę zdalnych referencji na obiekty typu //Account// 4. Do wybranego konta klient dopisuje określoną kwotę i sprawdza aktualny stan tego konta 5. Sprawdzenie, czy modyfikacja stanu konta zapamiętywana jest między kolejnymi uruchomieniami programu klienta == Rozwiązania == === JMS === 1. Pobierz ze strony mq.java.net Open MQ 4.5 (zip archive, no installer) 2. Uruchom program brokera: bin/imqbrokerd 3. Zaimplementuj: a) program HelloProvider wysyłający wiadomość tekstową do kolejki (Queue) b) program HelloConsumer pobierający synchronicznie wiadomość z kolejki i wypisujący jej treść na ekranie 4. W aplikacji z punktu 3. zmień Queue na Topic i porównaj sposób działania. 5. Zaimplementuj program konsumenta, który odbiera wiadomości z kolejki asynchronicznie. 6. Zaimplementuj konsumenta korzystającego z mechanizmu trwałej subskrypcji. 7. Przetestuj mechanizm filtrowania wiadomości.