Łącza nienazwane(potoki)



Podobne dokumenty
Łącza nienazwane(potoki) Łącza nienazwane mogą być używane tylko pomiędzy procesami ze sobą powiązanymi.

Kolejki FIFO (łącza nazwane)

Komunikacja za pomocą potoków. Tomasz Borzyszkowski

4.2 Sposób korzystania z l acza

Instrukcja do laboratorium Systemów Operacyjnych (semestr drugi)

Instrukcja do laboratorium Systemów Operacyjnych. (semestr drugi)

Systemy Operacyjne 1 Laboratorium 3 Potoki i łącza nazwane w Linuksie (jeden tydzień) dr inż. Arkadiusz Chrobot

Funkcje zawarte w bibliotece < io.h >

Funkcje zawarte w bibliotece < io.h >

Pliki. Funkcje tworzące pliki i operujące na nich opisane są w części 2 pomocy systemowej. Tworzenie i otwieranie plików:

Temat zajęć: Obsługa łączy komunikacyjnych

J. Ułasiewicz Łącza nienazwane, nazwane, select 1

Laboratorium z systemów operacyjnych. System plików - funkcje systemowe. Anna Wojak

Laboratorium Systemów Operacyjnych. Ćwiczenie 4. Operacje na plikach

Obsługa plików. Systemy Operacyjne 2 laboratorium. Mateusz Hołenko. 25 września 2011

Biblioteka standardowa - operacje wejścia/wyjścia

4. Komunikacja pomiędzy procesami przez łącza nienazwane i nazwane

Argumenty wywołania programu, operacje na plikach

Programowanie proceduralne INP001210WL rok akademicki 2015/16 semestr letni. Wykład 6. Karol Tarnowski A-1 p.

Procesy. Systemy Operacyjne 2 laboratorium. Mateusz Hołenko. 9 października 2011

Operacje na plikach. Informatyka. Standardowe strumienie wejścia i wyjścia

Obsługa plików Procesy

SYSTEMY OPERACYJNE I laboratorium 3 (Informatyka stacjonarne 2 rok, semestr zimowy)

Lekcja 10. Uprawnienia. Dołączanie plików przy pomocy funkcji include() Sprawdzanie, czy plik istnieje przy pmocy funkcji file_exists()

Programowanie w językach wysokiego poziomu

sposób wykonywania operacji zapisu i odczytu dane odczytywane z l acza usuwane (nie można ich odczytać ponownie),

Języki programowania. Przetwarzanie plików amorficznych Konwencja języka C. Część siódma. Autorzy Tomasz Xięski Roman Simiński

ISO/ANSI C dostęp do plików ISO/ANSI C. ISO/ANSI C dostęp do plików. ISO/ANSI C dostęp do plików. ISO/ANSI C dostęp do plików

ISO/ANSI C dostęp do plików ISO/ANSI C. ISO/ANSI C dostęp do plików. ISO/ANSI C dostęp do plików. ISO/ANSI C dostęp do plików

Poniższe funkcje opisane są w 2 i 3 części pomocy systemowej.

Podstawy programowania w języku C++

۰ Elementem jednostkowym takiego pliku jest bajt. ۰ Format pliku binarnego: [bajty pliku][eof]

Temat zajęć: Obsługa systemu plików.

Wstęp do programowania INP001213Wcl rok akademicki 2017/18 semestr zimowy. Wykład 12. Karol Tarnowski A-1 p.

Programowanie Współbieżne. W Linuxie/Unixie

<stdio.h> <iostram> input. FILE *stdin. std::cin output. std::cout error. FILE *stdout. FILE *stderr

Temat zajęć: Obsługa procesów w systemie.

J. Ułasiewicz Łącza nienazwane, nazwane, select 1

Plik jest reprezentowany przez strumień znaków (bajtów) o zmiennej długości. Koniec strumienia identyfikowany jest znacznikiem końca pliku EOF.

aodczytywać zniegoza pomoc afunkcjiread, (niebuforowane funkcje wejścia/wyjścia). e sukcesem, to zwróci liczb, erzeczywiściezapisanychbajtów.

INFORMATYKA Studia Niestacjonarne Elektrotechnika

Programowanie w językach

Języki i metodyka programowania. Typy, operatory, wyrażenia. Wejście i wyjście.

1. Procesy i współbieżność

Program wykonujący operację na plikach powinien zachować schemat działania zapewniający poprawną pracę:

Programowanie Procedurale. Pliki w języku C++

Wykład VI. Programowanie. dr inż. Janusz Słupik. Gliwice, Wydział Matematyki Stosowanej Politechniki Śląskiej. c Copyright 2014 Janusz Słupik

Pliki w C/C++ Przykłady na podstawie materiałów dr T. Jeleniewskiego

Część 4 życie programu

Instytut Teleinformatyki

Obsługa plików. Laboratorium Podstaw Informatyki. Kierunek Elektrotechnika. Laboratorium Podstaw Informatyki Strona 1. Kraków 2013

ISO/ANSI C - funkcje. Funkcje. ISO/ANSI C - funkcje. ISO/ANSI C - funkcje. ISO/ANSI C - funkcje. ISO/ANSI C - funkcje

Programowanie proceduralne INP001210WL rok akademicki 2018/19 semestr letni. Wykład 6. Karol Tarnowski A-1 p.

W języku C każdy plik fizyczny jest ciągiem bajtów, z których każdy może być niezależnie odczytany. Borland 01234

Wykład 3. Procesy i wątki. Wojciech Kwedlo, Wykład z Systemów Operacyjnych -1- Wydział Informatyki PB

MATERIAŁY POMOCNICZE PODSTAWY PROGRAMOWANIA Na podstawie: Programowanie w C - Stworzone na Wikibooks, bibliotece wolny podręczników

Podstawy programowania w języku C++

Ćwiczenie 4. Obsługa plików. Laboratorium Podstaw Informatyki. Kierunek Elektrotechnika. Laboratorium Podstaw Informatyki Strona 1.

Podstawy programowania w języku C++

Wskaźniki do funkcji. Wykład 11. Podstawy programowania ( język C ) Wskaźniki do funkcji (1) Wskaźniki do funkcji (2)

Pobieranie argumentów wiersza polecenia

Procesy i potoki. S. Samolej: Procesy

Podstawy programowania w języku C++

Sygnały. 7. Sygnały (2005/2006)

7 Przygotował: mgr inż. Maciej Lasota

Instrukcja do laboratorium Systemów Operacyjnych. (semestr drugi)

Pliki. Informacje ogólne. Obsługa plików w języku C

Formatowane (tekstowe) wejście/wyjście. Binarne wejście/wyjście.

Strumienie i pliki. Programowanie Proceduralne 1

3. Identyfikacja. SKŁADNIA #include <sys/socket.h> int getpeername(int socket, struct sockaddr *addr, int *addrlen);

Systemy Operacyjne - Operacje na plikach

Ghost in the machine

Klient-Serwer Komunikacja przy pomocy gniazd

Narzędzia informatyczne w językoznawstwie

Pliki. Informacje ogólne. Obsługa plików w języku C

Podstawy informatyki. Informatyka stosowana - studia niestacjonarne. Grzegorz Smyk. Wydział Inżynierii Metali i Informatyki Przemysłowej

Pliki. Operacje na plikach w Pascalu

Operacje wejścia/wyjścia odsłona pierwsza

1 Pierwsze kroki w C++ cz.3 2 Obsługa plików

PRZYKŁADY OPERACJI PLIKOWYCH z wykorzystaniem biblioteki <stdio.h>

Mechanizmy pracy równoległej. Jarosław Kuchta

Systemy Operacyjne 1 Laboratorium 2 Procesy i sygnały w Linuksie (jeden tydzień) dr inż. Arkadiusz Chrobot

Programowanie Proceduralne

Języki programowania. Tablice struktur, pliki struktur. Część ósma. Autorzy Tomasz Xięski Roman Simiński

Języki programowania obiektowego Nieobiektowe elementy języka C++

Iteracyjny serwer TCP i aplikacja UDP

Operacje na plikach (niskiego poziomu) < IO.H >

wer. 7 z drobnymi modyfikacjami Wojciech Myszka :48:

Podstawy programowania. Wykład Pętle. Tablice. Krzysztof Banaś Podstawy programowania 1

SYSTEMY CZASU RZECZYWISTEGO - VxWorks

Skrypty powłoki Skrypty Najcz ciej u ywane polecenia w skryptach:

Tablice, funkcje - wprowadzenie

znajdowały się różne instrukcje) to tak naprawdę definicja funkcji main.

Podstawy programowania skrót z wykładów:

Utworzenie pliku. Dowiesz się:

Model procesu w systemie Linux. Tomasz Borzyszkowski

Powłoka I. Popularne implementacje. W stylu sh (powłoki zdefiniowanej w POSIX) W stylu csh. bash (najpopularniejsza) zsh ksh mksh.

Wprowadzenie do programowania i programowanie obiektowe

PODSTAW PROGRAMOWANIA WYKŁAD 7 ŁAŃCUCHY

Transkrypt:

8. Łącza nienazwane(potoki) Łącze (potok, ang. pipe) jest to urządzenie komunikacyjne pozwalające na przesyłanie informacji w jedną stronę. Jeden proces wysyła dane do łącza za pomocą funkcji write, zaś inny odczytuje dane z łącza za pomocą funkcji read. Procesy zapisujące dopisują dane na końcu łącza, zaś procesy odczytujące odczytują je z początku łącza. Odczytane dane są usuwane z łącza. Przykład: $ ls more Łącza nienazwane mogą być używane tylko pomiędzy procesami ze sobą powiązanymi. Tworzenie łącza int pipe(int filedes[2]); Funkcja pipe umieszcza w tablicy parę deskryptorów reprezentujących potok (jego dwa końce). Pierwszy z nich jest otwarty do czytania z potoku, zaś drugi do zapisu. Funkcja zwraca 0, jeśli potok został utworzony, -1 w przeciwnym wypadku. Przykład: int fd[2]; int r_fd; int w_fd; char buf[512]; if (pipe(fd)!=0 ) { perror("pipe()");... r_fd=fd[0]; w_fd=fd[1];... read(r_fd,buf,sizeof(buf)); proces użytkownika read() write() pipe_fds[0] pipe_fds[1] jądro systemu łącze przepływ danych 1

8. Właściwości łącza Potok buforuje dane: dane przesyłane są do potoku i trzymane w nim, aż zostaną przeczytane. (Łącze automatycznie synchronizuje dwa procesy). Pojemność łącza jest ograniczona. Stała PIPE_BUF określa pojemność łącza w jądrze systemu (Posix wymaga, aby było to co najmniej 512 bajtów, na przykład w Linuksie często jest to 4096 bajtów). Zapisywanie do potoku Proces próbujący pisać do pełnego łącza zostanie zablokowany, dopóki inny proces nie odczyta danych. Zapis do potoku wykonuje się niepodzielnie, o ile ilość bajtów nie przekracza PIPE_BUF, w przeciwnym przypadku system nie gwarantuje niepodzielności zapisu. Jeśli piszemy do łącza, które zostało zamknięte do odczytu, generowany jest sygnał SIGPIPE. Domyślną obsługą tego sygnału jest zakończenie procesu. Jeśli proces obsługuje sygnał SIGPIPE i powraca z procedury obsługi tego błędu (lub ignoruje SIGPIPE), kolejne odwołanie się do funkcji write zwróci błąd EPIPE (Broken pipe). Czytanie z potoku Proces próbujący czytać automatycznie zostaje zablokowany, dopóki w łączu nie pojawią się dane. Jeśli przy odczycie żąda się więcej niż PIPE_BUF, jądro po prostu odczytuje dostępne dane i funkcja read zwraca liczbę przeczytanych bajtów. Kiedy wszystkie procesy piszące zostaną zakończone, read zwracać będzie 0 (czyli koniec pliku). Potok działa podobnie do kolejki; jeśli jest wielu odbiorców, każdy z nich pobierze kolejną porcję danych - nie można skierować danych do konkretnego odbiorcy. Dane w łączu traktuje się jako strumień danych bez zaznaczenia granic komunikatu. Ewentualny podział tego strumienia bajtów należy do aplikacji. Łącze nie może stosowane do rozgłaszania danych dla wielu odbiorców - odczytanie danych powoduje usunięcie ich z łącza). Podobnie, jeśli jest wiele procesów zapisujących nie można stwierdzić, który z nich przesłał dane. Ewentualne identyfikowanie należy do aplikacji. 2

8. Wykorzystanie łącza do komunikacji między procesem macierzystym i potomnym W procesie potomnym powielane są otwarte deskryptory z procesu macierzystego. proces macierzysty: tworzy łącze proces potomny: ma kopię read() pipe_fds[0] write() pipe_fds[1] read() pipe_fds[0] write() pipe_fds[1] łącze jądro systemu Przykład: Chcemy utworzyć potok pozwalający przesyłać komunikaty od procesu macierzystego do procesu potomnego. Wystarczy zamknąć odpowiednie deskryptory odpowiednio w procesie macierzystym i potomnym. proces macierzysty proces potomny read() pipe_fds[0] write() pipe_fds[1] read() pipe_fds[0] write() pipe_fds[1] łącze jądro systemu 3

8. Przykład: Proces macierzysty pisze do potoku, proces potomny czyta i wyświetla na ekranie. Obydwa procesy korzystają ze strumieni we-wy. #include <stdlib.h> void zapisywanie(const char* komunikat, int licznik, FILE* strumien){ for (; licznik > 0; --licznik) { fprintf (strumien, "%s\n", komunikat); fflush (strumien); sleep (1); void odczytywanie (FILE* strumien) { char bufor[1024]; while (!feof (strumien) &&!ferror (strumien) && fgets (bufor, sizeof (bufor), strumien)!= NULL) fputs (bufor, stdout); int main () { int fd[2]; pid_t pid; pipe(fd); pid = fork (); /* proces potomny */ if (pid == (pid_t) 0) { FILE* strumien; close (fd[1]); strumien = fdopen(fd[0], "r"); odczytywanie(strumien); close(fd[0]); / * proces macierzysty */ else { FILE* strumien; close(fd[0]); strumien = fdopen(fd[1], "w"); zapisywanie("witam!", 5, strumien); close(fd[1]); /* konwersja deskryptora do obiektu FILE */ /* konwersja deskryptora do obiektu FILE */ return 0; 4

8. Przykład: Program generuje napisy. Chcemy je wyświetlić w kolejności alfabetycznej. Możemy to zrealizować przekazując wyjście standardowe programu na wejście polecenia systemowego sort. #include <sys/types.h> #include <sys/wait.h> int main () { int fd[2]; pid_t pid; pipe(fd); pid = fork (); if (pid == (pid_t) 0) { close(fd[1]); dup2(fd[0], STDIN_FILENO); close(fd[0]); // dlaczego zamykamy ten deskryptor? execlp("sort", "sort", 0); else { FILE* strumien; close (fd[0]); strumien = fdopen(fd[1], "w"); fprintf(strumien, "Witam.\n"); fprintf(strumien, "Welcome.\n"); fprintf(strumien, "Bienvenue.\n"); fprintf(strumien, "Willkommen.\n"); fflush(strumien); close(fd[1]); waitpid(pid, NULL, 0); return 0; 5

8. Przykład: W programie uruchamiamy polecenie: ls l sort n k5. Wykorzystujemy do tego łącze nienazwane. #include <errno.h> #include <sys/types.h> int main(void) { pid_t childpid; int fd[2]; if ((pipe(fd) == -1) ((childpid = fork()) == -1)) { perror("failed to setup pipeline"); if (childpid == 0) { /* ls jest potomkiem */ if (dup2(fd[1], STDOUT_FILENO) == -1) perror("failed to redirect stdout of ls"); else if ((close(fd[0]) == -1) (close(fd[1]) == -1)) perror("failed to close extra pipe descriptors on ls"); else { execl("/bin/ls", "ls", "-l", NULL); perror("failed to exec ls"); if (dup2(fd[0], STDIN_FILENO) == -1) /* sort jest rodzicem */ perror("failed to redirect stdin of sort"); else if ((close(fd[0]) == -1) (close(fd[1]) == -1)) perror("failed to close extra pipe file descriptors on sort"); else { execl("/bin/sort", "sort", "-n", "-k5", NULL); perror("failed to exec sort"); Pytanie: Co będzie wyświetlane na wyjściu, jeśli deskryptory fd[0] i fd[1] nie będą zamknięte przed wywołaniem execl? 6

8. Funkcje popen i pclose biblioteka standardowa we-wy FILE *popen(const char *cmdstring, const char *type); int pclose(file *fp); Funkcja popen tworzy łącze i inicjuje inny proces (cmdstring ), który albo będzie czytał z łącza, albo do niego zapisywał. Polecenie cmdstring jest wykonywane jako polecenie shellowe. Przykład: fp=popen("sort","w"); proces macierzysty proces potomny ("sort") fp łącze kierunek przepływu danych stdin int main () { FILE* strumien = popen("sort","w"); fprintf (strumien, "Witam.\n"); fprintf (strumien, "Welcome.\n"); fprintf (strumien, "Bienvenue.\n"); fprintf (strumien, "Willkommen.\n"); fflush (strumien); return pclose(strumien); Przykład: fp=popen("who sort","r"); proces macierzysty proces potomny ("who sort") fp łącze kierunek przepływu danych stdout #include <stdlib.h> int main() { FILE *fp; char buf[100]; int i = 0; fp = popen( "who sort", "r" ); while ( fgets( buf, 100, fp )!= NULL ) printf("%3d %s", i++, buf ); pclose( fp ); return 0; Wady: mała efektywność - utworzenie procesu potomnego to za każdym razem uruchomienie shella i zastąpienie go właściwym poleceniem 7

8. Przykład: Dane przetwarzane przez główną aplikację są wstępnie przetwarzane przez program pomocniczy. (Stevens, Programowanie w środowisku Unix, str. 518) proces macierzysty - główna aplikacja proces potomny - filtr popen stdout stdout stdin prompt użytkownik przy terminalu dane wprowadzanie przez użytkownika /* program pomocniczy - filtr */ #include <ctype.h> int main(void){ int c; while ( (c = getchar())!= EOF) { if (isupper(c)) c = tolower(c); if (putchar(c) == EOF) { fprintf(stderr,"%s\n","blad wyjscia"); exit(1); if (c == '\n') fflush(stdout); exit(0); /* główna aplikacja */ #include <sys/wait.h> #define MAXW 80 int main(){ char wiersz[maxw]; FILE *we; if ( (we = popen("./filtr", "r")) == NULL) { perror("popen: blad"); exit(1); for ( ; ; ) { fputs("prompt> ", stdout); fflush(stdout); if (fgets(wiersz, MAXW, we) == NULL) /* czytaj z potoku */ break; if (fputs(wiersz, stdout) == EOF){ fprintf(stderr,"%d\n","blad przesłania do potoku"); exit(1); if (pclose(we) == -1) { perror("pclose"); exit(1); putchar('\n'); exit(0); 8

8. Łącza nazwane (kolejki FIFO) Łącze nazwane jest to plik specjalny, który zachowuje się tak jak potok. Dane są buforowane przez jądro, nie są przechowywane na dysku! Łącze nazwane ma nazwę, zatem może być otwarte i dostępne dla dowolnego procesu, który zna nazwę łącza i ma odpowiednie prawa dostępu do łącza. Łącze istnieje aż do jawnego usunięcia. Tworzenie łącza Łącze nazwane może być utworzone na dwa sposoby: za pomocą funkcji systemowej #include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode); Funkcja mkfifo tworzy łącze nazwane (kolejkę FIFO, ang. named pipe) o nazwie pathname, które ma prawa dostępu określone w argumencie mode. Jeśli łącze zostanie utworzone zwracane jest 0, w przeciwnym wypadku -1 i ustawiana zmienna errno. za pomocą polecenia: $ mkfifo /tmp/fifo $ ll /tmp/fifo prw-rw-rw- 1 user1 users 0 Nov 19 15:01 /tmp/fifo Dostęp do plików FIFO Dostęp do plików FIFO jest realizowany tak, jak do zwykłego pliku. Aby można było przesyłać dane za pomocą pliku FIFO, jeden program musi otworzyć go do czytania, zaś drugi do pisania. Można używać funkcje poziomu niższego (open, write,close, itd.) funkcje biblioteczne we-wy języka C (fopen, fprintf, fscanf, fclose, itd.) int fd = open(nazwa_fifo,o_wronly); write(fd, bufor, ile); close (fd); FILE* fifo=fopen(nazwa_fifo,"r"); fscanf(fifo,"%s",bufor); fclose(fifo); Wywołanie read() dla łącza FIFO, które nie jest już otwarte do zapisu, zwraca koniec pliku (wartość 0). Wywołanie read() dla łącza FIFO, które jest otwarte do zapisu i puste: blokujące czeka na nadejście danych nieblokujące zwraca -1 i errno przyjmuje wartość EAGAIN Wywołanie write() dołącza dane na koniec łącza. 9

Przykład: Prowadzenie dziennika w oparciu o model klient - serwer. Klient zapisuje informację (PID procesu, czas) do nazwanego potoku. Serwer pobiera tę informację i zapisuje do pliku (dziennika) określonego w wierszu wywołania. 8. Pytania: Jaką zaletę ma to rozwiązanie w stosunku do zwykłego zapisywania do określonego pliku? Dlaczego w serwerze potok nazwany jest tworzony z prawami read/write? W jaki sposób zapewnić nieskończone działanie serwera? Również wtedy, kiedy nie ma działającego klienta? Jak zapewnić to, aby komunikaty pochodzące od różnych klientów pojawiały się w dzienniku w całości, tzn. nie były rozdzielana komunikatami pochodzącymi od innych klientów? /* Serwer */ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <errno.h> #define FIFOARG 1 #define FIFO_PERMS (S_IRWXU S_IWGRP S_IWOTH) int main (int argc, char *argv[]) { int fd; if (argc!= 2) { fprintf(stderr, "Usage: %s fifoname > logfile\n", argv[0]); if ((mkfifo(argv[fifoarg], FIFO_PERMS) == -1) && (errno!= EEXIST)) { perror("server failed to create a FIFO"); if ((fd = open(argv[fifoarg], O_RDWR)) == -1) { perror("server failed to open its FIFO"); /* czytaj z łącza i zapisuj na standardowe wyjście funkcję trzeba napisać samemu */ copyfile(fd, STDOUT_FILENO); =========================================================================== 10

8. /* Klient */ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <errno.h> #include <time.h> #include <string.h> #include <limits.h> /* PIPE_BUF */ #define FIFOARG 1 int main (int argc, char *argv[]) { time_t curtime; int len; char buf[pipe_buf]; int fd; if (argc!= 2) { fprintf(stderr, "Usage: %s fifoname", argv[0]); if ((fd = open(argv[fifoarg], O_WRONLY)) == -1) { perror("client failed to open log fifo for writing"); curtime = time(null); snprintf(buf, PIPE_BUF, "%d: %s", (int)getpid(), ctime(&curtime)); len = strlen(buf); if (r_write(fd, buf, len)!= len) { perror("client failed to write"); close(fd); return 0; Uwaga: Funkcja r_write() opakowuje funkcję write() - jeśli zostało zapisane mniej niż len bajtów ponownie jest wywoływane write(). 11