## Systemy Operacyjne - Lista 10 ### Zadanie 1 ![](https://i.imgur.com/Le0qOIY.png) *Przetwarzanie równoległe (ang. parallel)* - polega na równoległym wykonywaniu wielu zadań. Np wykorzystując 4 procesory można wykonywać RÓWNOLEGLE 4 zadania (no wiadomo procesor ma rdzenie itd) *Przetwarzanie współbieżne (ang. concurrent)* - polega na wykonywaniu wielu zadań JEDNOCZEŚNIE, ale zadania te są wykonywane przez JEDEN procesor, standardzik, procesy są poprzeplatane, wykonuje się kawałek jednego, wymieniania jest przestrzeń adresowa, context switch i leci kolejne klasyk. Fajne bo pozwala na zarządzaniewieloma zadaniami bez całkowitego zatrzymania któregoś z nich. *Procedury wielobieżne (ang. reentrant)* - To procedura, której wykonanie może zostać przerwana, sygnałem lub wywołaniem innej funkcji w jej ciele, wtedy wywołujemy ją jeszcze raz, to pierwsze wywołanie musi poczekać aż drugie się zakończy, a jak 2 się zakończy, to wracając do pierwszego możemy je dalej bezpiecznie kontynuować Generalnie uzyskuje się to przez używania jedynie parametrów i lokalnych zmiennych leżących na stosie, nie lockuje żadnych globalnym zasobów podczas wykonania. Nie może modyfikować swojego kodu, wołać innych procedur NIEwielobieżnych (no całkiem logiczne). *Procedura wielowątkowo-bezpieczna (ang. MT-safe)* - procedura, która może być bezpiecznie wołana z kilku wątków jednocześnie jednocześnie gwarantując brak race conditions Przykład procedury wielobieżnej (reentrant), ale NIE wielowątkowo-bezpiecznej Procedura wielowątkowo-bezpieczna, ale nie wielobieżna (reentrant) ```clike= # include <pthread.h> int increment_counter () { static int counter = 0; static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // only allow one thread to increment at a time pthread_mutex_lock(&mutex); ++counter; // store value before any other threads increment it further int result = counter; pthread_mutex_unlock(&mutex); return result; } ``` W powyższej procedurze `increment_counter` może być wołany przez różne wątki bez problemu ponieważ mutex jest używany do sychronizowania wszystkich dostępów to dzielonej zmiennej `counter`. Ale jeśli funkcja zostanie użyta przez reentrant interrupt handler i drugie przerwanie dotrze gdy mutex jest zablokowany, druga procedura zawiesi się na zawsze, będzie deadlock. Procedura wielobieżna, ale nie wielo-wątkowa ```clike= int t; void swap(int *x, int *y) { int s; s = t; // save global variable t = *x; *x = *y; // hardware interrupt might invoke isr() here! *y = t; t = s; // restore global variable } void isr() { int x = 1, y = 2; swap(&x, &y); } ``` Reentrant: 1) swap1(1,2) 2) t = 1 3) swap2(3,4) 4) t = 3 5) swap2 przywraca t = 1 6) wracamy do swap1, który widzi t = 1 (czyli git) Thread A Thread B 1) int s; int s; 2) s = 100; s = 100; 3) t = 5; no operation; 4) no operation; t = 20; // t is global so Thread A also sees the value as t = 20 5) x = 10; x = 30; 6) y = 20; y = 20; // Thread A exchange is wrong, Thread B exchange is OK *Kiedy w jednowątkowym procesie uniksowym może wystąpić współbieżność?* Nie wiem ale czy chodzi tutaj o event processing? - ślisko ale tak, bo te nasze wewnętrzne joby są współbieżne względem siebie więc git, ale z punktu widzenia procesora dalej to jest jeden proces Obsługa sygnałów ### Zadanie 2 ![](https://i.imgur.com/NxWKInV.png) *Zakleszczenie (ang. deadlock)* - sytuacja w której dwa lub więcej wątków blokuje się na stałe, ponieważ każdy z nich chce uzyskać dostęp do zasobu, który jest już zajęty przez drugi wątek. Np: ```clike= //thread A if(lock(x)) if(lock(y)) //do sth //thread B if(lock(y)) if(lock(x)) //do sth ``` Thread A 1) lock(x); 2) czekaj dopóki zasób y zostanie zwolniony Thread A 1) lock(y); 2) czekaj dopóki zasób x zostanie zwolniony *Uwięzenie (ang. livelock)* - sytuacja w której dwa lub więcej wątków konkurują o dostęp do tego samego zasobu, lecz ściągają swoje blokady, żeby uniknać deadlock'a i pozwolić uzyskać dostęp do tego zasobu innym zasobom, i to też może ciągnać się w nieskończoność - dobrym przykładem są dwie osoby na korytarzu, które aby się minać wybierają cały czas stronę, w którą się przesunąć, i tak cały czas przesuwają się w lewo, potem w prawo, potem w lewo itd. ```clike= //thread A if(lock(x)) while(y is locked) { x unlock wait x lock } //thread B if(lock(y)) while(x is locked) { y unlock wait y lock } ``` *Głodzenie (ang. starvation)* - sytuacja w której scheduler priorytetuje jakąś grupę wątków A, która blokuje zasób X przez co grupa wątków B nie dostanie możliwości dostępu do zasobu X i będzie głodzona. ### Zadanie 3 ![](https://i.imgur.com/noXs91I.png) *Sytuacja wyścigu (ang. race condition)* - występuje wtedu, gdy dwa lub więcej proceów jednocześnie czytają/zapisują współdzielone dane, a rezultat zależy od tego, jaki był przeplot wykonania procesów. MIN: 1. tally = 0 2. tally = 0 wykonuje 49 iteracji zapisując tally = 49 2. wykonuje 1 iteracje zapisujac tally = 1 2. wykonujac 50 iteracje pobiera z rejestru tally = 1 1. wykonuje wszystkie iteracje i zapisuje tally = 50 2. drugi ma pobrane tally = 1, dodaje 2 i zapisuje tally = 2 MAX: wykonuja sie naprzemienne proces 1. ustawia tally = 0 proces 2. ustawia tally = 0 proces 1. pobiera tally = 0, dodaje 1 i zapisuje tally = 1 proces 2. pobiera tally = 1, dodaje 1 i zapisuje tally = 2 proces 1. pobiera tally = 2, dodaje 1 i zapisuje tally = 3 itd itd ... ### Zadanie 4 ![](https://i.imgur.com/ks4537Q.png) fork - pthread_create ![](https://i.imgur.com/J5hC0D0.png) thread - identyfikator wątku attr - atrybuty wątku, PTHREAD_CREATE_JOINABLE/DETACHED, rozmiar i adres stosu itd ... start_routine - funkcja wykonywana w wątku arg - argumenty przekazywane do funkcji (wskaźnik na strukturę danych) exit - pthread_exit ![](https://i.imgur.com/Qo4qHdn.png) kończy wątek z return value retval waitpid - pthread_join ![](https://i.imgur.com/Dg6nxgA.png) czeka na thread aż się zakończy i go grzebie (zwalnia jego zasoby - chyba wlasny stos maja watki). Jeśli ten tred już się zakończy, to pthread_join wraca od razu. Thread musi być joinable. Jeśli thread zwrócił jakąś wartość to jest zwracana w retval, jeśli został anulowany to znajdzie się tam wartosć PTHREAD_CANCELED atexit - pthread_cleanup_push ![](https://i.imgur.com/8BfJR6z.png) ![](https://i.imgur.com/U2hFU27.png) dodanie na górę stosu wątku procedurę routine: Program czyszczący jest ściągany ze stosu i wykonywany w następujących okolicznościach: 1. Gdy wątek jest anulowany (wszystkie handlery czyszące są ściągane ze stosu i wykonywane w odwrotnej kolejności do ich wrzucania) 2. Gdy wątek jest zakończony przez pthread_exit (dzieje się to samo co wyżej) 3. Kiedy zostanie zawołane pthread_cleanup_pop z niezerowym argumentem zostanie wykonana top-most procedura abort - pthread_cancel ![](https://i.imgur.com/nsmVCFw.png) wysyła request o anulowanie do wątku thread, wątek zachowa się w zależności od jego cancelability state i type: state - może być wyłączony, przez co żądanie anulowania zostanie zakolejkowane i wywołane gdy wątek ustawi swój stan na enabled type: jeśli jest asonchronous - anulowanie dzieje się od razu jeśli jest deffered - dzieje się, gdy wątek zawoła jedną z funkcji zdefiniowanych jako cancellation point *wątki złączalne (ang. joinable)* - domyślny rodzaj działania wątków, są przywiązane do procesu i musimy wykonać na nich pthread_join, żeby je pogrzebać, caller po powrocie w wywołania pthread_join ma gwarancje, ze watek się zakończył i może wybrać jakie prace "sprzątające" chce wykonać. (taki kinda waitpid?) jeśli caller sam nie posprząta to jest sprzątane na koniec programu *wątki odczepione (ang. detached)* - wątki które po zakończeniu działania same zwalniają wykorzystane przez siebie zasoby. wywołanie exit skutkuje posrzątaniem stosu po sobie