Tutaj trochę szerzej opisałam mechanizm gniazdek. 3 tematy wymienione poniŝej przeplatają się ze sobą więc wrzuciłam je do jednego worka i przedstawiłam w takim porządku, który wydawał mi się najbardziej logiczny W wersji skróconej te pytania są juŝ zebrane po kolei tak jak G przykazał w tematach;) Miłej lektury *41. Schemat funkcjonowania mechanizmu gniazdek. + *42. Elementy i ograniczenia przesyłania bezpołączeniowego za pomocą gniazdek. + *43. Elementy i właściwości przesyłania połączeniowego za pomocą gniazdek. I.Cel Komunikacja pomiędzy procesami działającymi w róŝnych węzłach sieci wymaga, aby jeden z tych procesów czekał w gotowości do odebrania danych (nasłuchiwał), wówczas drugi proces moŝe skomunikować się z nim w dowolnym, wybranym przez siebie w momencie. W związku z takim modelem komunikacji proces nasłuchujący nazywany jest serwerem, a proces komunikujący się z nim i korzystający z jego usług nazywany jest klientem. Klient musi zatem znać adres serwera przed nawiązaniem komunikacji, serwer natomiast nie wie jacy klienci się do niego zgłoszą, ich adresy moŝe więc poznać dopiero po nawiązaniu komunikacji. II.Wstęp Gniazda BSD umoŝliwiają komunikację sieciową (między procesami działającymi na róŝnych komputerkach )z wykorzystaniem róŝnych rodzin protokołów np. TCP/IP, IPX, AppleTalk i wielu innych. Zdecydowanie najpopularniejsza jest obecnie komunikacja w dziedzinie Internetu, wykorzystująca protokoły TCP/IP. Gniazda dają równieŝ moŝliwość komunikacji międzyprocesowej w dziedzinie UNIX-a, czyli wewnątrz jednego systemu operacyjnego z wykorzystaniem jego wewnętrznych protokołów. III.Rodzaje transmisji Funkcje systemowe interfejsu gniazd pozwalają zrealizować transmisję danych w protokole połączeniowym lub bezpołączeniowym (rozstrzygniecie w momencie tworzenia gniazdka.). PoniŜej przedstawiłam typowe scenariusze takich transmisji.
Tryb bezpołączeniowy (datagramowy) Tryb połączeniowy tryb połączeniowy, obsługiwany przez protokół TCP/IP, gwarantujący przesyłanie danych w postaci strumienia, w sposób niezawodny (bez utraty danych), bez duplikowania danych i bez zmiany ich kolejności, w obu kierunkach (od serwera do klienta i od klienta do serwera), Główne właściwości: nawiązanie połączenia asymetryczne, później wymiana danych symetryczna, do funkcji nie trzeba przekazywać adresów(są one ustalane podczas nawiązywania połączenia) tryb bezpołączeniowy, obsługiwany przez protokół UDP/IP, umoŝliwiający przesyłanie datagramów, czyli określonych porcji danych, nie gwarantujący niezawodnego doręczenia, dopuszczający duplikację całego datagramu lub zmianę kolejności doręczenia dwóch kolejno wysłanych datagramów. Główne ograniczenia: tego trybu: wykorzystywany protokół UDP, kontrola poprawności naleŝy do aplikacji, ograniczony rozmiar porcji danych limitowany własnościami protokołu i sieci. IV. Operacje na gniazdkach /* W tym dziale opiszę wszystkie funkcje wymienione na rysunkach, zarówno dla trybu połączeniowego jak i bezpołączeniowego poniewaŝ imho taki ciąg będzie bardziej logiczny (omówienie funkcji send() oraz sentdo() jednocześnie), w części skróconej zmienię trochę porządek tak aby odpowiadał ściśle na pytania z zestawu, które odnoszą się do dwóch trybów oddzielnie. */ 0.Reprezentacja w systemie (To juŝ zamieściłam w ramach ciekawostki dla wnikliwych )
Dla procesu w systemie Unix gniazdo jest rodzajem pliku specjalnego, w związku z czym moŝna wykonywać na jego deskryptorze podobne operacje, jak na pliku (read(), write(), close()). Zapis do gniazda lub odczyt z niego oznacza odpowiednio wysłanie lub odbiór danych przez sieć. 1. Utworzenie i otworzenie gniazda oraz otrzymanie jego deskryptora umoŝliwia funkcja systemowa socket(). KaŜdy z dwóch komunikujących się ze sobą procesów musi mieć własne gniazdo, które moŝe słuŝyć zarówno do wysyłania jak i odbioru danych. Tworząc gniazdo funkcją socket(), naleŝy wyspecyfikować tryb komunikacji (połączeniowy lub bezpołączeniowy), z którego wynika protokół transmisji danych przez to gniazdo (TCP lub UDP). Protokół jest równieŝ elementem wyróŝniającym gniazdo, czyli dwa gniazda o tym samym adresie ale róŝnych protokołach są róŝnymi gniazdami. int socket(int addrfamily, int sockettype, int protocol); - addrfamily - nazwa dziedziny (rodziny protokołów), forma adresowa + AF_INET, AF_UNIX - sockettype - sposób przesyłania danych, typ komunikacji np. + SOCK_STREAM - połączeniowy, + SOCK_DGRAM - bezpołączeniowy, - protocol protokół uŝywany przez gniazdko + IPPROTO_TCP, + IPPROTO_UDP + jesli 0 domyslny dla formy adresowej i typukomunikacji - return :int Funkcja zwraca deskryptor utworzonego gniazda (pliku) lub -1 w wypadku błędu. 2. Przydzielanie adresu W celu wysłania danych przez gniazdo musi zostać określony adresat, czyli gniazdo, do którego dane zostaną wysłane. Właściwy adres gniazda składa się z dwóch elementów: adresu węzła i numeru portu. KaŜde gniazdo musi mieć przydzielony lokalny adres zanim będzie mogło być uŝyte do komunikacji. Operację tę określa się równieŝ jako związywanie adresu z gniazdem lub nazywanie gniazda. Ostatnie określenie ma szczególne uzasadnienie w przypadku gniazd w dziedzinie UNIX-a, dla których adresami są nazwy ścieŝkowe plików. Pliki te są tworzone przez jądro podczas nazywania gniazda. Adres moŝe być przydzielony jawnie za pomocą funkcji bind() lub automatycznie przez jądro systemu. Funkcja ta musi być wywołana przez serwer zarówno w trybie połączeniowym jak i bezpołączeniowym int bind(socket socket, const struct sockaddr *myaddr,int addrlen); - socket deskryptor gniazda, utworzony przez funkcję socket(), - myaddr wskaźnik do struktury zawierającej adres własny - addrlen rozmiar struktury zawierającej adres Zamiast wskaźnika na strukturę sockaddr przekazuje się najczęściej wskaźnik na strukturę odpowiednią do rodziny aktualnie uŝywanych protokołów, wykonując przy przekazaniu wskaźnika rzutowanie typu na (struct sockaddr*). W dziedzinie Internetu uŝywa się struktury sockaddr_in o następującej definicji: struct sockaddr_in { sa_family_t sin_family; /* AF_INET */ u_init_16 sin_port; /* numer portu */ struct in_addr sin_addr; /* 32-bitowy adres węzła */
} char sin_zero[8]; /* nie uŝywane */ Struktura in_addr zawiera jedno pole typu long: struct in_addr{ u_int32_t s_addr; /* 32-bitowy adres właściwy węzła lub INADDR_ANY */ } 3.Ustanawianie połączenia : connect(), listen() akcept() a) Funkcja connect() uŝywana jest najczęściej przez klienta w trybie połączeniowym i słuŝy do zgłaszania Ŝądania nawiązania połączenia z serwerem. Przez wywołanie funkcji connect() gniazdu klienta przypisywany jest adres wybrany przez system. Proces klienta jest blokowany do momentu ustanowienia połączenia(funkcja czeka na potwierdzenie). Klient połączeniowy moŝe zrealizować tylko jedno pomyślne połączenie. int connect(socket socket, const struct sockaddr *servaddr, int addrlen); - socket - deskryptor gniazda, utworzony przez funkcję socket(), - servaddr - wskaźnik do struktury zawierającej adres serwera (struktura ta moŝe być róŝna dla róŝnych rodzin protokołów), - addrlen - rozmiar struktury zawierającej adres. b) Funkcja systemowa listen() Serwer protokołu połączeniowego wywołuje funkcję listen(), aby zgłosić w systemie gotowość przyjmowania połączeń i ustalić jednocześnie maksymalną liczbę połączeń oczekujących na obsłuŝenie. int listen(socket socket, int backlog); - socket - deskryptor gniazda, utworzony przez funkcję socket(), - backlog - parametr określający maksymalną liczbę Ŝądań oczekujących w kolejce na wykonanie funkcji accept(). c) Funkcja systemowa accept() Funkcja accept() wywoływana jest przez serwer w celu przyjęcia Ŝądania nawiązania połączenia, zgłoszonego wcześniej i oczekującego w kolejce. JeŜeli Ŝadne Ŝądanie nie dotarło, serwer jest blokowany do momentu otrzymania Ŝądania (funkcja czeka na Ŝądanie ). Po przyjęciu Ŝądania funkcja pod adres addr. wpisuje adres klienta i informuje aplikację, który klient został podłączony. Tworzy nowy deskryptor dla danego gniazda socket, który moŝe być następnie wykorzystywany przez proces obsługi zgłoszenia. Stare gniazdko nadal czeka na kolejne Ŝądania. (w zaleŝności od parametru funkcji listen()) SOCKET accept(socket socket, struct sockaddr * addr, int *addrlen); - socket - deskryptor gniazda, utworzony przez funkcję socket(), - addr - wskaźnik do struktury zawierającej adres klienta (struktura ta moŝe być róŝna dla róŝnych rodzin protokołów), - addrlen - rozmiar struktury zawierającej adres. 4.Przesyłanie danych
Sposób przesyłania danych jest uzaleŝniony od typu gniazd. Gniazda strumieniowe, wykorzystywane w transmisji połączeniowej, mogą być dostępne za pomocą typowych funkcji systemowych interfejsu plików. Po ustanowieniu połączenia, obydwa procesy uczestniczące w komunikacji znają juŝ swoje adresy i mogą korzystać z funkcji read() i write() lub recv() i send() do odbierania i wysyłania danych przez gniazdo. W pozostałych przypadkach, przesłanie i odbieranie datagramu wymaga podania adresu odbiorcy, co umoŝliwiają funkcje sendto() recvfrom(). a)funkcje systemowe send() i sendto() Działanie tych funkcji jest podobne do działania funkcji write() moŝna jednak w dodatkowym polu flags przekazać pewne opcje związane z wysłaniem komunikatu. Ponadto funkcja sendto() umoŝliwia podanie adresu docelowego w przypadku komunikacji bezpołączeniowej. int send(socket socket, const char *buf, int nbytes, int flags); int sendto(socket socket, const char *buf, int nbytes, int flags, const struct sockaddr *toaddr, int addrlen); - socket - deskryptor gniazda, utworzony przez funkcję socket() lub accept(), - buf - adres (wskaźnik) bufora, zawierającego dane do wysłania, - nbytes - liczba bajtów do wysłania, znajdująca się w buforze, - flags - znaczniki: - toaddr - wskaznik na strukture odpowiadajaca własciwej formie adresowej(adres odbiorcy), - addrlen - rozmiar struktury zawierającej adres. b) Funkcje systemowe recv() i recvfrom() Działanie tych funkcji jest podobne do działania funkcji read() moŝna jednak w dodatkowym polu flags przekazać pewne opcje związane z dobieraniem komunikatu. Ponadto funkcja recvfrom() umoŝliwia uzyskanie adresu nadawcy w przypadku komunikacji bezpołączeniowej. int recv(socket socket, char *buf, int nbytes, int flags); int recvfrom(socket socket, char *buf, int nbytes, int flags, struct sockaddr *fromaddr, int *addrlen); - socket - deskryptor gniazda, utworzony przez funkcję socket() lub accept(), - buf - adres (wskaźnik) bufora, który będzie zawierał dane po ich odebraniu,(pierwszy pakiet z kolejki) - nbytes - rozmiar bufora (maksymalna liczba bajtów, którą moŝna jednorazowo odebrać pozostałe dane >nbytes są tracone.) - flags - znaczniki: - fromaddr - wskaźnik do struktury, w której zapisany zostanie adres nadawcy danych. - addrlen - wskaźnik do zmiennej, przez którą zwrócony zostanie rozmiar struktury zawierającej adres. V LINKI Materiały z których korzystałam tworząc ten opis: (UWAGA deklaracje funkcji mogą się nieco róŝnić od tych podanych na wykładach np. zamiast argumentu : const struct blabla występuje struct blabla brakuje klucza : const) http://www.cs.put.poznan.pl/mszychowiak/dydaktyka/socketapi.html
http://www.imio.pw.edu.pl/vlsi/teaching/soe/materialy/lekcja10/ *44. Elementy i implementacje standardu MPI. 1 Interfejs MPI - Message Passing Interface (Interfejs Transmisji Wiadomości) standard interfejsu do przesyłania komunikatów w rzeczywistych i wirtualnych maszynach równoległych z pamiecia rozproszona (DM). - Biblioteka procedur i funkcji wywoływanych z programów napisanych w jezykach Fortran, C/C++ słuŝących do obsługi komunikatów i synchronizacji zadan wykonujacych najczesciej ten sam program. - Transfer danych pomiędzy poszczególnymi procesami programu wykonywanymi na procesorach maszyn będących węzłami klastra odbywa się za pośrednictwem sieci. - Program w MPI składa się z niezaleŝnych procesów operujących na róŝnych danych (MIMD). KaŜdy proces wykonuje się we własnej przestrzeni adresowej, aczkolwiek wykorzystanie pamięci współdzielonej teŝ jest moŝliwe. - Procesy są identyfikowane poprzez ich numer w grupie w zakresie 0.. groupsize - 1. 2 Implementacje MPI - Pierwszy standard MPI nazwany później MPI-1 był gotowy w maju 1994 roku - Drugi standard zwany MPI-2 ukończono w 1998 roku. Nie cieszył się on duŝą popularnością, poniewaŝ rok wcześniej opracowano MPICH, w którym zaimplementowano część poprawek wprowadzanych w MPI-2. - MPICH i LAM MPI to najczęściej stosowane implementacje standardu MPI. - W standardzie MPI-2 zdefiniowano równoległe operacje wejścia/wyjścia, które pierwotnie zostały zawarte w pakiecie MPI-IO rozwijanym specjalnie na potrzeby NASA, a następnie zmodyfikowane i przeniesione do nowego MPI-2. - Implementacje producentów sprzętu SA optymalizowane pod katem sprzętowych rozwiązań (sieci) komunikacyjnych i architektury (topologii) danego systemu. *45. Sposoby wywołania funkcji MPI. Prawie wszystkie funkcje MPI zwracają kod błędu: w C jako wartość funkcji, w Fortranie jako wartość ostatniego argumentu. Przed zwróceniem wartości wywoływana jest bieŝąca procedura obsługi błędu: standardowo powoduje przerwanie zadania, ustawienie uchwytu MPI_ERRORS_RETURN spowoduje zwracanie kodu błędu. MPI nie gwarantuje dalszego działania. W razie powodzenia zwracany jest MPI_SUCCESS Include Postac bibliotek załączanych dla języka: C : #include ``mpi.h`` Fortran : include `mpif.h` Format funkcji MPI C: int error; error = MPI_Xxxxx(parametr,...); MPI_Xxxxx(parametr,...); Fortran: INTEGER IERROR CALL MPI_XXXXX(parametr,..., IERROR) Przykład : Inicjacja biblioteki MPI Funkcja MPI_Init: ////////////////////////// C //////////////////////
int error = MPI_Init(int *argc, char **argv[]) c /////////Fortran INTEGER ierror CALL MPI_INIT(ierror) jak większość funkcji MPI zwraca stała int o wartości MPI_SUCCESS w przypadku pomyślnego wykonania. Wersja Fortran zwraca tylko argument ierror.