## Systemy Operacyjne - Lista 10
### Zadanie 1

*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

*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

*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

fork - pthread_create

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

kończy wątek z return value retval
waitpid - pthread_join

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


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

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