--- tags: SO, lista 10 --- # SO lista10 ## ZADANIE 1 :rocket: 1. Czym różni się przetwarzanie równoległe (ang. parallel) od przetwarzania współbieżnego (ang. concurrent)? - Przetwarzanie współbierzne polega na współistnieniu wielu wątków lub procesów. Procesy są uruchamiane naprzemienie i tylko na krótki kawałek czasu przez co występuje wrażenie jakby wszystkie procesy działały jednocześnie, a tak nie jest (w danech chwili działa tylko jeden proces) - Przetwarzanie równoległe pojega na wykonywaniu daych obliczeń jednocześnie. 3. Czym charakteryzują się procedury wielobieżne (ang. reentrant)? - Funkcja jest określana jako wielobieżna jeżeli wykonywanie jej może zostać przerwane (poprzez przerwanie lub wywołanie innej funkcji wewnątrz ciała funkcji), a potem może ona zostać (bezpiecznie) ponownie wywołana zanim poprzednie wywołanie zostanie zakończone. Po zakończeniu drugiego wywołania, można wrócić do przerwanego wywołania, a wykonywanie go może bezpiecznie kontynuować. Funkcje wielobieżne można więc wywoływać rekurencyjnie. 5. Podaj przykład procedury w języku C (a) wielobieżnej, ale nie wielowątkowo-bezpiecznej (ang. MT-safe) (b) na odwrót. (a) ```c /* We save the global state in a local variable and we restore it at the end of the function. The function is now reentrant but it is not thread safe. */ int t; void swap(int *x, int *y) { int s; s = t; t = *x; *x = *y; *y = t; t = s; } ``` (b) ```c /* We use a thread local variable: the function is now thread-safe but still not reentrant (within the same thread). */ __thread int t; void swap(int *x, int *y) { t = *x; *x = *y; *y = t; } ``` * A reentrant function can be invoked, interrupted, and re-invoked. Such a function can be invoked simultaneously by multiple threads if and only if each invocation references or provides unique data and inputs. * A thread-safe function can be invoked simultaneously by multiple threads, even if each invocation references or provides the same data or input, as all access is serialized. 7. Kiedy w jednowątkowym procesie uniksowym może wystąpić współbieżność? Jeśli samemu byśmy zaimplementowali porzełączanie między zadaniami w programi (bez użycia wielu wątków). ## ZADANIE 2 :rocket:/:memo: Wybierz odpowiedni scenariusz zachowania wątków, w którym konkurują o dostęp do zasobów, i na tej podstawie precyzyjnie opisz zjawisko **zakleszczenia** (ang. deadlock), **uwięzienia** (ang. livelock) oraz **głodzenia** (ang. starvation). * **zakleszczenia** Zakleszczenie to sytuacja, w której procesy blokują się nawzajem z powodu pozyskiwania zasobów i żaden z procesów nie robi postępów, czekając na zasób przetrzymywany przez inny proces. ![](https://i.imgur.com/6tT2Xa2.png) * **uwięzienia** W przypadku blokady na żywo stany procesów biorących udział w scenariuszu blokady na żywo stale się zmieniają. Z drugiej strony procesy nadal są od siebie zależne i nigdy nie mogą zakończyć swoich zadań. DZONIENIE DO SIEBIE JEDNOCZEŚNIE ![](https://i.imgur.com/MrYp53Y.png) * **głodzenia** Głód jest wynikiem procesu, który nie jest w stanie uzyskać regularnego dostępu do współdzielonych zasobów potrzebnych do wykonania zadania, a tym samym nie jest w stanie poczynić żadnych postępów. CZEŚTO JEST NASTĘPSTWEM DEAD/LIVE LOCKA ![](https://i.imgur.com/1xpsgfI.png) ## ZADANIE 3 :rocket: 1. W poniższym programie występuje sytuacja wyścigu (ang. race condition) *[wynik zależy od przeplotu]* dotycząca dostępów do współdzielonej zmiennej «tally». Wyznacz jej najmniejszą i największą możliwą wartość. ```c const int n = 50; shared int tally = 0; void total() { for (int count = 1; count <= n; count++) tally = tally + 1; } void main() { parbegin (total(), total()); } ŹLE DA SIĘ ZROBIĆ PRZEPLOT ŻEBY 2 BYLO MINIMIALNA WARTOŚCIĄ ``` * Min wartość: 50 Max wartość: 100 UZASADNIENIE: zwiększenie wartości zmiennej to 3 instrukcje (a) zapisać wartość tally do pomocniczego rejestru %eax (b) zwiększenie wartości rejestru %eax (c) przepisanie spowrotem wartości z %eax do tally Moze wystąpić przeplot, w którym T1 i T2 wykonają po jednej iteracji a tally zwiększy się tylko o 1. 2. Jak zmieni się przedział możliwych wartości zmiennej «tally», gdy wystartujemy k procesów zamiast dwóch? Odpowiedź uzasadnij pokazując przeplot, który prowadzi do określonego wyniku. * Min wartość: 50 Max wartość: k*50 Przeplot, w którym każdy wątek wykonuję jedną iterację (prawie do końca, przerwana gdy (c)), a wartość tally zwiększa się o 1. ## ZADANIE 4 :rocket:/:memo: 1. <div style="text-align: justify"> Podaj procedury wątków POSIX, które pełnią podobną funkcję co fork(2), exit(3), waitpid(2), atexit(3) oraz abort(3) na procesach. Opisz semantykę podanych funkcji i powiedz gdzie się różnią od swoich odpowiedników na procesach.<div> * fork() - **pthread_create()** ```c int pthread_create(pthread_t *restrict thread, const pthread_attr_t *restrict attr, void *(*start_routine)(void *), void *restrict arg); ``` * exit(3) - **pthread_exit()** ```c noreturn void pthread_exit(void *retval); ``` * waitpid(2) - **pthread_join()** ```c int pthread_join(pthread_t thread, void **retval); ``` * atexit(3) - **pthread_key_create()** * when a pthread exits, it calls the destructor function specified by pthread_key_create(). Although all threads need to use the same destructor, we can write a destructor that calls another function whose pointer is specified by the thread specific data, facilitating thread local destructor. ```c int pthread_key_create(pthread_key_t *key, void (*destructor)(void*)); ``` * abort(3) - **pthread_cancel** ```c int pthread_cancel(pthread_t thread); ``` 2. Porównaj zachowanie wątków złączalnych (ang.joinable) i odczepionych (ang. detached). Procedura *pthread_join()* czeka tylko na wątki, które są złączalne. Złączalnle wątki są grzebane przez wątek główny, a wątki odczepione są grzebane przez jądro. 3. Kto zatem odpowiada za usunięcie segmentu stosu z przestrzeni użytkownika, gdy wątek złączalny albo odczepiony zakończy pracę? NA WYKŁADZIE: dealokacja pamięci (czy ze stosu?), gdy detached to jądro, gdy joinable to wątek główny wydaje rozkaz zdealokowania pamięci. ## ZADANIE 5 ## ZADANIE 6 :memo: 1. Na podstawie [2, 14.4] opowiedz jaka motywacja stała za wprowadzeniem wywołania **poll(2)** [3,63.2.2] do systemów uniksowych? Czemu lepiej konstruować oprogramowanie w oparciu o *poll*, niż o odpytywanie deskryptorów albo powiadamianie o zdarzeniach bazujące na sygnale *«SIGIO»*? CEL: chcemy czytać i wypisywać do kilku różnych plików i robić to nieblokująco, tzn jeśli jest coś do wczytanian to to wczytujemy, nie czekamy na input z jedengo plkiku w nieskończoność bo wtedy blokujemy odczytwanie z innych plików. Możliwer rozwiązania 2 procesy (komplikuję się zamykanie deskryptorów - umieranie procesów), dwa wątki (sychronizacja wątków może byc kłopotliwa). ZALETY POLL: wołamy funkcje, która obserwuje podane deskryptory i ma na uwadze co chcemy robić na danym deskryptorze i jak jakiś deskryptor jest gotowy to funkcja wraca z informacjami WADY ODPYTYWANIA: WADY SIGIO: * "przenośność" może być problem (przeniesienie tego rozwiązania na inny system operacyjny (CHYBA)) * przyjście 1 sygnału mówi tyle że jeden deskryptor pliku jest gotowy, a może być więcej otwartych deskryptorów niż możliwych sygnałów -> PROBLEM 3. Na jakich plikach można oczekiwać na zdarzenia przy pomocy **poll(2)** [3, 63.2.3]? * **regular files** * **terminals and pseudeterminals** * **pipes** * **sockets** 4. Czemu wszystkie deskryptory przekazywane do poll powinno skonfigurować się do pracy w trybie nieblokującym? * bo cel jest taki aby ?? * wywołanie systemowe *poll()* umożliwia procesowi albo zablokwać się i czekać dopóki jakiś fd będzie gotowy albo odczekać określoną ilość czasu * co gdyby fd był skonfigurowany w treybie blokującym? * gdyby nie było nic np. do wczytania to read nie zakończył by się dopóki nie pojawi się chciaż jeden bajt do wczytania dla tego deskryptora (nie pożądane zachowanie) 5. Jak zapewnić, żeby wywołania *connect(2)*, *accept(2)*, *read(2)* i *write(2)* na gnieździe sieciowym zawsze zwróciły *«EWOULDBLOCK»* zamiast blokować się w jądrze [7, 16]? * dla read /writefunkcja **str_cli** * dla connent nic specjalnego chyba ... 6. Chcemy by po wywołaniu **poll(2)** pierwsza instancja wyżej wymienionych wywołań systemowych nie zwróciła *«EWOULDBLOCK»*. Jaka wartość musi być wpisana przez jądro do pola *«revents»* struktury *«pollfd»* dla danego deskryptora pliku, żeby mieć tę pewność? ## ZADANIE 7 :memo:/:rocket: ## ZADANIE 8 :memo: ## MATERIAŁY DO PRZECZYTANIA - **Advanced Programming in the UNIX Environment** 11.1-11.5 (Threads), 12.8-12.10 (Threads & Signals & fork) - **Operating Systems: Three Easy Pieces** https://pages.cs.wisc.edu/~remzi/OSTEP/ - 26 (Concurrency & Threads), 27(Thread API), 33(Event Based Concurency)