1 / 15

Systemy operacyjne (wiosna 2014)

Systemy operacyjne (wiosna 2014). Laboratorium 11 – tutorial Komunikacja procesów. dr inż. Wojciech Bieniecki Instytut Nauk Ekonomicznych i Informatyki http://wbieniec.kis.p.lodz.pl/pwsz. Metody komunikacji międzyprocesowej.

Download Presentation

Systemy operacyjne (wiosna 2014)

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Systemy operacyjne(wiosna 2014) Laboratorium 11 – tutorial Komunikacja procesów dr inż. Wojciech Bieniecki Instytut Nauk Ekonomicznych i Informatyki http://wbieniec.kis.p.lodz.pl/pwsz

  2. Metody komunikacji międzyprocesowej Komunikacja międzyprocesowa (ang. Inter-ProcessCommunication — IPC) – sposoby komunikacji pomiędzy procesami systemu operacyjnego. Pojęcie IPC może odnosić się do wymiany informacji w systemach rozproszonych (klastrów, systemów odległych połączonych siecią). Mechanizmy IPC opierają się na budowaniu w pamięci lub na dysku dynamicznych struktur, używanych do transmisji komunikatów pomiędzy procesami Lista metod IPC ta obejmuje: pliki i blokady – najprostsza i najstarsza forma IPC sygnały (ang. signals) – czasami znane jako przerwania programowe semafory (ang. semaphores) łącza nienazwane (ang. pipes) – znane też jako łącza komunikacyjne łącza nazwane (ang. namedpipes) – znane też jako nazwane łącza komunikacyjne kolejki komunikatów (ang. messagequeues) pamięć dzielona (ang. sharedmemory) gniazda Uniksa (ang. Unix domainsockets) gniazda (ang. sockets) RPC (ang. RemoteProcedureCall) – zdalne wywoływanie procedur.

  3. Łącza komunikacyjne Łącza w UNIX są plikami specjalnymi. Są podobne do plików zwykłych – posiadają swój i-węzeł, posiadają bloki z danymi, na otwartych łączach można wykonywać operacje zapisu i odczytu. Czym różnią się od plików zwykłych • • ograniczona liczba bloków – łącza mają rozmiar 4KB – 8KB w zależności od konkretnego systemu, • • dostęp sekwencyjny – na łączach można wykonywać tylko operacje zapisu i odczytu, ale nie można wykonywać funkcji lseek) • • sposób wykonywania operacji zapisu i odczytu – dane odczytywane z łącza są zarazem usuwane (nie można ich odczytać ponownie) • • proces jest blokowany w funkcji read na pustym łączu i w funkcji write, jeśli w łączu nie ma wystarczającej ilości wolnego miejsca, żeby zmieścić zapisywany blok

  4. Łącza komunikacyjne – cechy wspólne i różnice Łącze nazwane (tzw kolejki FIFO). Łącze nienazwane (tzw. potok) Posiada dowiązanie w systemie plików (istnieje jako plik w jakimś katalogu) nie ma dowiązania w systemie plików – istnieje tylko tak długo, jak jest otwarte Może być identyfikowane przez nazwę Jest identyfikowane tylko przez deskryptory Po zamknięciu pozostaje przydzielony i-węzeł, ale wszystkie jego bloki na dysku są zwalniane. Po zamknięciu wszystkich jego deskryptorów przestaje istnieć (zwalniany jest jego i-węzeł i wszystkie bloki) Procesy, które chcą komunikować się za pomocą łącza nienazwanego, muszą znać jego deskryptor. Sposobem przekazania deskryptora łącza innemu procesowi jest utworzenie go jako proces potomny – tablica otwartych plików jest dziedziczona. Można w procesie macierzystym otworzyć łącze a następnie utworzyć dwa procesy potomne, które będą komunikować się ze sobą.

  5. Operacje na łączach w UNIX Łącza nazwane mkfifo - funkcja systemowa tworząca łącze nazwane (działa podobnie jak creat dla plików zwykłych, ale w przeciwieństwie do creat nie otwiera łącza) intmkfifo (char* path, mode_t mode) Argumenty funkcji: path- nazwa ścieżkowa pliku specjalnego będącego kolejką FIFO mode- prawa dostępu do łącza Wartości zwracane: poprawne wykonanie funkcji: 0 zakończenie błędne: -1 W przypadku błędnego zakończenie funkcji, kod błędu możemy odczytać w errno.

  6. Operacje na łączach w UNIX Łącza nazwane open – otwiera łącze podobnie jak plik. Uwaga – aby łącze działało musi być otwarte do odczytu i zapisu (np. jeden proces pisze drugi czyta) int open (char*path, int flags) Wartości zwracane: poprawne wykonanie funkcji: deskryptor kolejki FIFO zakończenie błędne: -1 • Argumenty funkcji: • path- nazwa ścieżkowa pliku specjalnego będącego kolejką fifo • mode- prawa dostępu do łącza • flags- określenie trybu w jakim jest otwierana kolejka: • O_RDONLY - tryb tylko do odczytu • O_WRONLY- tryb tylko do zapis

  7. Przykład użycia łącza nazwanego Utworzone metodą mkfifo łącze musi zostać otwarte przez użycie funkcji open. Funkcja ta musi zostać wywołana przynajmniej przez dwa procesy w sposób komplementarny, tzn. jeden z nich musi otworzyć łącze do zapisu, a drugi do odczytu. Odczyt i zapis danych z łącza nazwanego odbywa się za pomocą funkcji read i write mkfifo("kolejka",0666); desc = open("kolejka" , O_RDONLY); desc = open("kolejka" , O_WRONLY); read(desc); write(desc); close(desc); close(desc); unlink("kolejka");

  8. Przykład użycia łącza nazwanego #include <fcntl.h> main(){ intfd; if (mkfifo("/tmp/fifo", 0600) == -1) exit(1); //Błąd tworzenia kolejki switch(fork()){ case -1: // blad w tworzeniu procesu exit(1); case 0: //dotyczy procesu potomnego fd = open("/tmp/fifo", O_WRONLY); if (fd == -1) exit(1); //Błąd otwarcia kolejki do zapisu if (write(fd, "Witaj!", 7) == -1) exit(1); //Błąd zapisu do kolejki exit(0); //normalne zakończenie procesu potomnego default: // dotyczy procesu macierzystego char buf[10]; fd = open("/tmp/fifo", O_RDONLY); if (fd == -1) exit(1); if (read(fd, buf, 10) == -1) exit(1); printf("Odczytano z potoku: %s\n", buf); } }

  9. Użycie łączy nienazwanych

  10. Funkcja pipe intpipe(intfd[2]) Funkcja tworzy parę sprzężonych deskryptorów pliku, wskazujących na inode potoku i umieszcza je w tablicy fd. fd[0] – deskryptor potoku do odczytu fd[1] – deskryptor potoku do zapisu Proces, który utworzył potok może się przez niego komunikować tylko ze swoimi potomkami lub przekazać im deskryptory, umożliwiając w ten sposób wzajemną komunikację. Wartości zwracane: poprawne wykonanie funkcji: 0 zakończenie błędne: -1

  11. Przykład komunikacji przodek-potomek main() { intfd[2]; if (pipe(fd) == -1) exit(1); // błąd tworzenia potoku switch(fork()){ // rozwidlamy procesy case -1: // blad w tworzeniu procesu exit(1); case 0: // proces potomny if (write(fd[1], "Witaj!", 7) == -1) exit(1); // błąd zapisu do potoku exit(0); // kończymy proces potomny default: // proces macierzysty char buf[10]; if (read(fd[0], buf, 10) == -1) exit(1); // błąd odczytu z potoku printf("Odczytano z potoku: %s\n", buf); } }

  12. Przykład użycia potoku Działanie funkcji read w przypadku pustego potoku main() { intfd[2]; pipe(fd); if (fork() == 0){ // proces potomny write(fd[1], "witaj!", 7); exit(0); } else { // proces macierzysty char buf[10]; read(fd[0], buf, 10); read(fd[0], buf, 10); //Uwaga tutaj!!! printf("Odczytano z potoku: %s\n", buf); } } Drugi odczyt spowoduje zawieszenie procesu, gdyż potok jest pusty, a proces potomny ma otwarty deskryptor do zapisu.

  13. Przykład użycia potoku Niskopoziomowa realizacja ls|tr a-z A-Zz użyciem łączy nienazwanych #define MAX 512 //będziemy przetwarzać po 512 znaków main(intargc, char* argv[]) { intfd[2]; pipe(fd) == -1); //Tworzenie potoku if(fork() == 0) {// proces potomny dup2(fd[1], 1); // tworzymy drugie dowiązanie do stdout execvp("ls", argv); // Uruchomienie programu ls } else { // proces macierzysty char buf[MAX]; intlb, i; close(fd[1]); // zamykamy możliwość zapisu do łącza while ((lb=read(fd[0], buf, MAX)) > 0){ //czytamy for(i=0; i<lb; i++) buf[i] = toupper(buf[i]); //konwertujemy znaki w pętli write(1, buf,lb); // zapisujemy znaki na wyjście } } }

  14. Przykład użycia potoku wysokopoziomowa realizacja ls|tr a-z A-Zz użyciem łączy nienazwanych main(intargc, char* argv[]) { intfd[2]; pipe(fd); if(fork() == 0) {// proces potomny dup2(fd[1], 1); execvp("ls", argv); } else { // proces macierzysty close(fd[1]); dup2(fd[0], 0); execlp("tr", "tr", "a-z", "A-Z", 0); //Uruchomienie tr } } W przykładzie tym i poprzednim pominięto obsługę błędów.

  15. Zadanie producenta-konsumenta Jeden proces-producent generuje produkuje dane (zapisuje do łącza) a drugi proces-konsument pobiera je (czyta z potoku). Potok jest wspólną zmienną która jest ograniczonym buforem. Może pojawić się problem przepełnienia łącza lub gdy łącze jest puste.

More Related