Procesy, pliki, potoki, sygnały - uzupełnienie

Podobne dokumenty
Pliki, potoki, sygnały

Procesy. S. Samolej: Procesy

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

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

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

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

Obsługa sygnałów. Tomasz Borzyszkowski

Obsługa plików Procesy

procesy odrębne dzielone

Krótki kurs programowania współbieżnego

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

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

2. Zarządzanie procesami

przerwany proces móg l zareagować na określone zdarzenie. Można je traktować jako software owe wersje przerwań sprz etowych.

Model procesu w systemie Linux. Tomasz Borzyszkowski

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

Procesy i potoki. S. Samolej: Procesy

Zarządzanie procesami

POSIX: IEEE Std (Issue 6, 2004 edition)

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

pami eć operacyjna przechowuje dane do przetworzenia, tymczasowe dane pomocnicze,

4.2 Sposób korzystania z l acza

Procesy. 5. Procesy (2005/2006)

Łącza nienazwane(potoki)

Procesy w systemach UNIX i Linux

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

Uruchamianie programów w systemie Linux, potoki, strumienie, procesy, alias

jeszcze dygresja o macierzach...

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

1. Timery i zdarzenia

Laboratorium Procesy w systemach UNIX 3.2 Polecenia związane z procesami

przypadków wywo lanie systemowe (funkcja systemowa) lub funkcja biblioteczna zwraca wartość 1(czasamiNULL) iprzypisujezmiennej zewn etrznej,

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

2. Zarządzanie procesami

2. Zarządzanie procesami

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

Funkcje jadra systemu operacyjnego UNIX

UNIX. mgr inż. Marcin Borkowski

PROGRAMOWANIE SYSTEMÓW CZASU RZECZYWISTEGO

Pobieranie argumentów wiersza polecenia

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

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

Wątki, sygnały i szeregowanie w systemach UNIX, Linux

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

Krótkie wprowadzenie do korzystania z OpenSSL

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

Systemy Operacyjne Ćwiczenia

Wykład 5 Przerwania i wywołania systemowe. Wojciech Kwedlo, Systemy Operacyjne II -1- Wydział Informatyki PB

Sygnały i ich obsługa

Argumenty wywołania programu, operacje na plikach

Wstęp do Programowania, laboratorium 02

Systemy Operacyjne I: Procesy

Sygnał mechanizm asynchronicznego powiadamiania procesów o zdarzeniach zwykle awaryjnych.

Shared memory and messages. Functions. process 0. process 1. program 0. program 0. data 0. data 1. program 1. data 0. data 1.

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

Procesy i sygnały. Procesy. 6. Procesy i sygnały (2006/2007)

Zarządzanie Procesami

Funkcje jadra systemu operacyjnego UNIX

1. Utwórz blok pamięci współdzielonej korzystając z poniższego kodu:

Komunikacja za pomocą potoków. Tomasz Borzyszkowski

Podstawy administracji systemu Linux

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

Unix: programowanie procesów

Wskaźniki. Informatyka

Funkcja (podprogram) void

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

9. Procesy, urządzenia i system plików w systemie Linux

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

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

Autor: dr inż. Zofia Kruczkiewicz, Programowanie aplikacji internetowych 1

Unix: programowanie procesów

Serwery współbieżne c.d.

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

1. Ustanawianie ograniczeń na użycie zasobów

System operacyjny Linux

Instrukcja do laboratorium Systemów Operacyjnych (semestr drugi)

Zwielokrotnianie wejścia wyjścia

Podział programu na moduły

Temat: Dynamiczne przydzielanie i zwalnianie pamięci. Struktura listy operacje wstawiania, wyszukiwania oraz usuwania danych.

Unix: programowanie procesów

Kolejki komunikatów POSIX

Unix: programowanie procesów

SUMA KONTROLNA (icmp_cksum) NUMER KOLEJNY (icmp_seq)

dynamiczny przydział pamięci calloc() memset() memcpy( ) (wskaźniki!! )

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

Laboratorium systemów operacyjnych ćwiczenie nr 3. [ilość modułów: 1] Temat zajęć: Procesy w systemie operacyjnym

Problem producentakonsumenta

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

ĆWICZENIE 5. TEMAT: OBSŁUGA PORTU SZEREGOWEGO W PAKIECIE KEILuVISON WYSYŁANIE PORTEM SZEREGOWYM

1. Wartość, jaką odczytuje się z obszaru przydzielonego obiektowi to: a) I - wartość b) definicja obiektu c) typ oboektu d) p - wartość

Wprowadzenie do systemu operacyjnego Linux zarzdzanie procesami, cz. 2

Języki i paradygmaty programowania 1 studia stacjonarne 2018/19. Lab 9. Tablice liczbowe cd,. Operacje na tablicach o dwóch indeksach.

UŻYCIE I ZARZĄDZANIE WĄTKAMI

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

RPC. Zdalne wywoływanie procedur (ang. Remote Procedure Calls )

Gniazda BSD. komunikacja bezpołączeniowa

Programowanie Proceduralne

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

Struktura programu. Projekty złożone składają się zwykłe z różnych plików. Zawartość każdego pliku programista wyznacza zgodnie z jego przeznaczeniem.

Transkrypt:

Procesy, pliki, potoki, sygnały - uzupełnienie dr inż. Sławomir Samolej Katedra Informatyki i Automatyki Politechnika Rzeszowska Program przedmiotu oparto w części na materiałach opublikowanych na: http://wazniak.mimuw.edu.pl/ oraz Na materiałach opracowanych przez dr inż. Jędrzeja Ułasiewicza: jedrzej.ulasiewicz.staff.iiar.pwr.wroc.pl S. Samolej: Procesy 1

Uruchomienie programu z innego programu #include <stdlib.h> int system (const char *string); #include <stdlib.h> int main() { printf( Running ps with system\n ); system( ps -ax ); printf( Done.\n ); exit(0); Wada: nowy program jest uruchamiany przez powłokę. $./system1 Running ps with system PID TTY STAT TIME COMMAND 1? S 0:05 init 2? SW 0:00 [keventd]... 1262 pts/1 S 0:00 /bin/bash 1273 pts/2 S 0:00 su - 1274 pts/2 S 0:00 -bash 1463 pts/1 S 0:00 oclock -transparent - geometry 135x135-10+40 1465 pts/1 S 0:01 emacs Makefile 1480 pts/1 S 0:00./system1 1481 pts/1 R 0:00 ps -ax Done. S. Samolej: Procesy 2

Zastępowanie procesu (1) Rodzina funkcji: exec* (execl, execlp, execle, execv, execvp, execve) zastępuje bieżący proces innym, tworzonym na podstawie podanych argumentów. int main() { printf( Running ps with execlp\n ); execlp( ps, ps, -ax, 0); printf( Done.\n ); exit(0); $./pexec Running ps with execlp PID TTY STAT TIME COMMAND 1? S 0:05 init 2? SW 0:00 [keventd]... 1262 pts/1 S 0:00 /bin/bash 1273 pts/2 S 0:00 su - 1274 pts/2 S 0:00 -bash 1465 pts/1 S 0:01 emacs Makefile 1514 pts/1 R 0:00 ps ax S. Samolej: Procesy 3

exec*() char **environ; int execl(const char *path, const char *arg0,..., (char *)0); int execlp(const char *path, const char *arg0,..., (char *)0); int execle(const char *path, const char *arg0,..., (char *)0, char *const envp[]); int execv(const char *path, char *const argv[]); int execvp(const char *path, char *const argv[]); int execve(const char *path, char *const argv[], char *const envp[]); path nazwa programu arg0 argn argumenty programu dla wersji funkcji execv argumenty mogą być przekazane przez tablicę argv Funkcje z przyrostkiem p przeszukują zmienną środowiskową PATH (echo $PATH) Zmienna environ może zawierać wartość nowego środowiska programu W funkcjach execle i execve jest dodatkowa zmienna do przekazania tablicy ciągów, która zostanie wykorzystana jako nowe środowisko programu S. Samolej: Procesy 4

Zastępowanie procesu (2) Jeśli wystąpiło zastąpienie procesu to PID procesu jest taki sam jak procesu, w którym nastąpiło wywołanie funkcji exex* Efekt jest taki, jakby program rozpoczął wykonywać nowy kod z nowego pliku wykonywalnego, określonego w argumentach funkcji exec* Funkcje exec* zwykle nie powracają, chyba, że wystąpi błąd. Wtedy funkcja zwraca wartość -1 i ustawiana jest zmienna errno. Nowy proces uruchomiony przez exec* dziedziczy między innymi deskryptory pliku. Zamykane są natomiast wszystkie strumienie katalogowe, otwarte w pierwotnym procesie. S. Samolej: Procesy 5

Blokowanie dostępu do pliku #include <sys/file.h> int lockf(int fd, int cmd, off_t len); Parametry funkcji: fd Uchwyt pliku cmd len Wartości zwracane: -1 Błąd >0 Sukces Specyfikacje operacji: F_LOCK F_ULOCK F_TEST F_TLOCK Specyfikacja operacji: F_LOCK, F_ULOCK, F_TEST,F_TLOCK Zakres blokowania (o ile bajtów od bieżącego położenia plik ma być zablokowany) (0 - blokowany jest cały plik) Zablokuj dostęp do pliku na długości zakres od pozycji bieżącej Zwolnij dostęp do pliku. Testuj czy fragment pliku jest zablokowany przez inny proces Testuj czy fragment pliku jest zablokowany przez inny proces. Gdy nie to zajmij plik S. Samolej: Pliki, potoki, sygnały 6

Kontrolowany zapis 2 procesów do 1 pliku - przykład #include <stdlib.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/wait.h> #include <fcntl.h> int main() { int fd, i; pid_t pid; char buf1[]="to jest tekst1\n"; char buf2[]="to jest tekst2\n"; printf("tworze proces potomny...\n"); fd=open("plik1.txt",o_creat O_WRONLY,0777); pid=fork(); switch(pid) { case -1: perror("blad...\n"); exit(1); case 0: for(i=0;i<10;i++) { lockf(fd,f_lock,0); write(fd,buf1,sizeof(buf1)); sleep(2); lockf(fd,f_ulock,0); exit(112); default: { int stat_val; for(i=0;i<10;i++) { if(lockf(fd,f_test,0)==-1) { printf("zablokowany...\n"); else { lockf(fd,f_lock,0); write(fd,buf2,sizeof(buf2)); lockf(fd,f_ulock,0); sleep(1); wait(&stat_val); exit(0); S. Samolej: Pliki, potoki, sygnały 7

Potoki procesowe FILE *popen(const char *command, const char *open_mode); int pclose(file *stream_to_close); popen uruchamia inny programu jako nowy proces oraz przekazuje lub odbiera od niego dane parametr command jest nazwą programu, który należy uruchomić wraz ze wszystkimi parametrami wywołania, parametr open_mode musi mieć wartość r lub w. pclose czeka do momentu zakończenia uruchomionego procesu zwraca kod wyjściowy uruchomionego procesu jeśli proces uruchamiający wykona funkcję wait tracimy wówczas kod wyjściowy, pclose zwróci -1, a zamienna errno będzie zawierała wartość ECHILD S. Samolej: Pliki, potoki, sygnały 8

Przykład użycie popen i pclose #include <stdlib.h> #include <string.h> int main() { FILE *read_fp; char buffer[bufsiz + 1]; int chars_read; memset(buffer, \0, sizeof(buffer)); read_fp = popen( uname -a, r ); if (read_fp!= NULL) { chars_read = fread(buffer, sizeof(char), BUFSIZ, read_fp); if (chars_read > 0) { printf( Output was:-\n%s\n, buffer); pclose(read_fp); exit(exit_success); exit(exit_failure); Output was:- Linux gw1 2.4.20-8 #1 Thu Mar 13 17:54:28 EST 2003 i686 i686 i386 GNU/Linux S. Samolej: Pliki, potoki, sygnały 9

Wysyłanie danych ze pomocą popen #include <stdlib.h> int main() { FILE *write_fp; char buffer[bufsiz + 1]; sprintf(buffer, Once upon a time, there was...\n ); write_fp = popen( od -c, w ); if (write_fp!= NULL) { fwrite(buffer, sizeof(char), strlen(buffer), write_fp); pclose(write_fp); exit(exit_success); exit(exit_failure); $./popen2 0000000 O n c e u p o n a t i m e 0000020, t h e r e w a s... \n 0000037 S. Samolej: Pliki, potoki, sygnały 10

Przekazywanie większej ilości danych #include <stdlib.h> #include <string.h> int main() { FILE *read_fp; char buffer[bufsiz + 1]; int chars_read; memset(buffer, \0, sizeof(buffer)); read_fp = popen( ps -ax, r ); if (read_fp!= NULL) { chars_read = fread(buffer, sizeof(char), BUFSIZ, read_fp); while (chars_read > 0) { buffer[chars_read 1] = \0 ; printf( Reading:-\n %s\n, buffer); chars_read = fread(buffer, sizeof(char), BUFSIZ, read_fp); pclose(read_fp); exit(exit_success); exit(exit_failure); $./popen3 Reading:- PID TTY STAT TIME COMMAND 1? S 0:04 init 2? SW 0:00 [kflushd] 3? SW 0:00 [kpiod] 4? SW 0:00 [kswapd] 5? SW< 0:00 [mdrecoveryd]... 240 tty2 S 0:02 emacs draft1.txt Reading:- 368 tty1 S 0:00./popen3 369 tty1 R 0:00 ps -ax... S. Samolej: Pliki, potoki, sygnały 11

Producent: Potoki i exec #include <stdlib.h> #include <string.h> int main() { int data_processed; int file_pipes[2]; const char some_data[] = 123 ; char buffer[bufsiz + 1]; pid_t fork_result; memset(buffer, \0, sizeof(buffer)); if (pipe(file_pipes) == 0) { fork_result = fork(); if (fork_result == (pid_t)-1) { fprintf(stderr, Fork failure ); exit(exit_failure); if (fork_result == 0) { sprintf(buffer, %d, file_pipes[0]); (void)execl( pipe4, pipe4, buffer, (char *)0); exit(exit_failure); else { data_processed = write(file_pipes[1], some_data, strlen(some_data)); printf( %d - wrote %d bytes\n, getpid(), data_processed); exit(exit_success); Konsument: #include <stdlib.h> #include <string.h> int main(int argc, char *argv[]) { int data_processed; char buffer[bufsiz + 1]; int file_descriptor; memset(buffer, \0, sizeof(buffer)); sscanf(argv[1], %d, &file_descriptor); data_processed = read(file_descriptor, buffer, BUFSIZ); printf( %d - read %d bytes: %s\n, getpid(), data_processed, buffer); exit(exit_success); $./pipe3 980 - wrote 3 bytes 981 - read 3 bytes: 123 S. Samolej: Pliki, potoki, sygnały 12

Jak to działa? Program producenta korzysta z funkcji pipe, aby stworzyć potok, a następnie z funkcji fork, aby utworzyć nowy proces Zapisuje w buforze bufor deskryptor pliku do odczytu otrzymany z funkcji pipe W ramach procesu potomnego wywoływana jest funkcja exec uruchamiająca inny program (konsument), do której jako parametry wywołania przekazywany jest deskryptor pliku do odczytu. Program konsumenta pobiera deskryptor pliku do odczytu i z niego czyta. S. Samolej: Pliki, potoki, sygnały 13

Sygnały - wprowadzenie Sygnał to zdarzenie w systemie Linux powstałe w odpowiedzi na zaistnienie pewnych okoliczności Po otrzymaniu sygnału proces może podjąć określone czynności Sygnały są generowane przez niektóre błędy (naruszenie segmentów pamięci, błędy jednostki zmiennoprzecinkowej) Mogą być generowane przez powłokę i programy obsługi terminala, aby wykonać przerwanie. Mogą być też jawnie wysyłane z jednego procesu do drugiego w celu przesłania informacji lub modyfikacji pracy procesu. Na poziomie interfejsu można: Generować sygnały Przechwytywać sygnały Wykonywać na ich podstawie pewne czynności Zignorować (ale nie wszystkie). S. Samolej: Pliki, potoki, sygnały 14

Tablica sygnałów Nazwa sygnału SIGABORT SIGALRM SIGFPE SIGHUP SIGILL SIGINT SIGKILL SIGPIPE SIGQUIT SIGSEGV SIGTERM SIGUSR1 SIGUSR2 Opis *Przerwanie procesu Zegar alarmowy *Wyjątek związany z jednostką zmiennoprzecinkową Zawieszenie *Nielegalna instrukcja Przerwanie z terminala Zabójstwo (nie można go przechwycić ani zignorować) Zapis do potoku bez odbiorcy Sygnał zakończenia terminala *Dostęp do niewłaściwego segmentu pamięci Zakończenie Sygnał 1 zdefiniowany przez użytkownika Sygnał 1 zdefiniowany przez użytkownika S. Samolej: Pliki, potoki, sygnały 15

Reakcja procesów na sygnały Jeśli proces otrzyma jeden z wymienionych wyżej sygnałów i nie jest przygotowany na jego przechwycenie, to zostaje natychmiast zakończony W przypadkach sygnałów oznaczonych * mogą zostać podjęte w systemie czynności zależne od implementacji W chwili takiego zakończenia procesu w jego bieżącym katalogu tworzony jest plik core zawierający zrzut pamięci S. Samolej: Pliki, potoki, sygnały 16

Sygnały dodatkowe Nazwa sygnału SIGCHLD SIGCONT SIGSTOP SIGTSTP SIGTTIN SIGTTOU Opis Proces potomny zatrzymał się lub zakończył pracę Wznowienie wykonania, o ile proces był zatrzymany Zatrzymanie wykonywania (nie można go przechwycić ani zignorować) Sygnał zatrzymania terminala Proces pracujący w tle próbuje przeprowadzić odczyt Proces pracujący w tle próbuje przeprowadzić zapis SIGCHILD można zastosować do sterowania procesami potomnymi Pozostałe, poza SIGCONT powodują zatrzymanie otrzymujących je procesów Normalnie skonfigurowany terminal po przyciśnięciu kombinacji Ctrl+C powoduje wysłanie sygnału SIGINT do pierwszoplanowego procesu uruchomionego na tym terminalu. S. Samolej: Pliki, potoki, sygnały 17

Manipulowanie sygnałami #include <signal.h> void (*signal(int sig, void (*func)(int)))(int); Funkcja signal przyjmuje 2 parametry: Sygnał, który należy przechwycić lub zignorować (sig) Funkcję, którą należy wywołać po otrzymaniu sygnału, która: Musi przyjmować pojedynczy argument tupu int, a sama być typu void Funkcja signal zwraca z kolei funkcję tego samego typu poprzednią wartość funkcji ustawionej do obsługi sygnału, albo jedną z wartości specjalnych: SIG_IGN = zignorować sygnał SIG_DFL = przywrócić domyślne zachowanie W systemie Linux domyślne zachowanie przywracane jest automatycznie S. Samolej: Pliki, potoki, sygnały 18

Prosta obsługa sygnałów - przykład #include <signal.h> void ouch(int sig) { printf( OUCH! - I got signal %d\n, sig); (void) signal(sigint, SIG_DFL); int main() { (void) signal(sigint, ouch); while(1) { printf( \n ); sleep(1); $./ctrlc1 ^C OUCH! - I got signal 2 ^C $ Sygnał jest domyślnie obsługiwany tylko raz. Do ponownej obsługi trzeba procedurę obsługi ponownie ustanowić (pojawia się możliwość niezdążenia z ponowną obsługą). S. Samolej: Pliki, potoki, sygnały 19

Wysyłanie sygnałów Wysłanie sygnału do dowolnego procesu: #include <sys/types.h> #include <signal.h> int kill(pid_t pid, int sig); Funkcja alarm planuje dostarczenie sygnału SIGALRM za seconds sekund unsigned int alarm(unsigned int seconds); S. Samolej: Pliki, potoki, sygnały 20

Symulator funkcji alarm - budzik #include <sys/types.h> #include <signal.h> static int alarm_fired = 0; void ding(int sig) { alarm_fired = 1; int main() { pid_t pid; printf( alarm application starting\n ); pid = fork(); switch(pid) { case -1: /* Failure */ perror( fork failed ); exit(1); case 0: /* child */ sleep(5); kill(getppid(), SIGALRM); exit(0); /* if we get here we are the parent process */ printf( waiting for alarm to go off\n ); (void) signal(sigalrm, ding); pause(); if (alarm_fired) printf( Ding!\n ); printf( done\n ); exit(0); $./alarm alarm application starting waiting for alarm to go off <5 second pause> Ding! done $ Jeśli zastosujemy pause, a sygnał już został wysłany do naszego procesu, to możemy spowodować zawieszenie procesu. S. Samolej: Pliki, potoki, sygnały 21

Odporny interfejs sygnałowy #include <signal.h> int sigaction( int sig, const struct sigaction *act, struct sigaction *oact); Struktura sigaction jest zdefiniowana w pliku nagłówkowym signal.h i musi się składać przynajmniej z następujących elementów: void (*) (int) sa_handler funkcja, SIG_DFL lub SIG_IGN sigset_t sa_mask sygnały, które należy zablokować w sa_handler int sa_flags modyfikatory reakcji na sygnał Funkcja sigaction ustala reakcję na sygnał sig. Jeśli oact ma wartość inną niż null, sigaction zapisuje poprzednią reakcję na sygnał w lokacji wskazanej przez oact Jeśli act ma wartość null, to jest to jedyny rezultat działania sigaction; jeśli zaś act jest różne od null, wówczas ustawiana jest reakcja na określony sygnał Wewnątrz struktury sigaction, na którą wskazuje argument act, sa_handelr jest wskaźnikiem do funkcji wywoływanej po otrzymaniu sygnału sig. S. Samolej: Pliki, potoki, sygnały 22

Przykładowy program stosujący sigaction #include <signal.h> void ouch(int sig) { printf( OUCH! - I got signal %d\n, sig); int main() { struct sigaction act; act.sa_handler = ouch; sigemptyset(&act.sa_mask); act.sa_flags = 0; sigaction(sigint, &act, 0); while(1) { printf( \n ); sleep(1); $./ctrlc2 ^C OUCH! - I got signal 2 ^C OUCH! - I got signal 2 ^\ Quit $ S. Samolej: Pliki, potoki, sygnały 23