SYSTEMY OPERACYJNE
dr. hab. Vitaliy Yakovyna
yakovyna@matman.uwm.edu.pl http://wmii.uwm.edu.pl/~yakovyna/
UNIWERSYTET WARMIŃSKO-MAZURSKI W OLSZTYNIE
Wydział Matematyki i Informatyki
ZAKLESZCZENIE
Zakleszczenie
• Zakleszczenie (ang. deadlock) to sytuacja, gdy każdy proces w zestawie procesów czeka na
zdarzenie, które może być spowodowane tylko przez inny proces w zestawie.
• Problemy zakleszczenia stają się coraz trudniejsze, ze względu na zapotrzebowanie zwiększonej
współbieżności i równoległości w systemach wielordzeniowych
Zasoby
• Zasoby: cykle procesora, pliki, urządzenia We/Wy, blokady mutex, semafory itp.
• W normalnym trybie działania wątek może
wykorzystywać zasób tylko w następującej kolejności:
1) Żądanie 2) Używanie 3) Uwolnienie
Zakleszczenie w Aplikacjach Wielowątkowych
/* thread one runs in this function */
void *do_work_one(void *param)
{ pthread_mutex_lock(&first_mutex);
pthread mutex_lock(&second_mutex);
/*** Do some work
pthread_mutex_unlock(&second_mutex);*/
pthread_mutex_unlock(&first_mutex);
pthread_exit(0);
}
/* thread two runs in this function */
void *do_work_two(void *param)
{ pthread_mutex_lock(&second_mutex);
pthread_mutex_lock(&first_mutex);
/*** Do some work
pthread_mutex_unlock(&first_mutex);*/
pthread_mutex_unlock(&second_mutex);
pthread_exit(0);
}
Zakleszczenie w Aplikacjach Wielowątkowych
Trudno jest zidentyfikować i przetestować zakleszczenia, które mogą wystąpić tylko w pewnych okolicznościach planowania
Livelock
• Livelock to kolejna forma niepowodzenia żywotności
• Livelock występuje, gdy wątek stale próbuje wykonać akcję, która się nie udaje
• Livelock zwykle występuje, gdy wątki jednocześnie ponawiają nieudane operacje
• Można tego na ogół uniknąć, jeśli każdy wątek będzie ponawiać próbę wykonywania akcji, która się nie udaje, w losowych momentach
• Livelock jest mniej powszechny niż zakleszczenie i może wystąpić tylko w określonych okolicznościach
planowania
Przykład Livelock
/* thread one */
void *do_work_one(void *param) {int done = 0;
while (!done) {
pthread_mutex_lock(&first_mutex);
if (pthread_mutex_trylock(&second_mutex)) { /*
* Do some work
*/pthread_mutex_unlock(&second_mutex);
pthread_mutex_unlock(&first_mutex);
done = 1;
}else
pthread_mutex_unlock(&first_mutex);
}
pthread_exit(0);
}
/* thread two */
void *do_work_two(void *param) {int done = 0;
while (!done) {
pthread_mutex_lock(&second_mutex);
if (pthread_mutex_trylock(&first_mutex)) { /*
* Do some work
*/pthread_mutex_unlock(&first_mutex);
pthread_mutex_unlock(&second_mutex);
done = 1;
}else
pthread_mutex_unlock(&second_mutex);
}
pthread_exit(0);
}
Zakleszczenie: Niezbędne Warunki
• Zakleszczenie może wystąpić, jeśli następujące cztery warunki występują jednocześnie w systemie:
1. Wzajemne wykluczenie. Co najmniej jeden zasób musi działać w trybie niepodzielnym.
2. Trzymaj i czekaj. Wątek musi utrzymywać co najmniej jeden zasób i czekać na zdobycie dodatkowych zasobów, które są obecnie w posiadaniu innych wątków.
3. Brak wywłaszczenia. Zasoby nie mogą być wywłaszczone:
tylko wątek, który trzyma zasób, może go dobrowolnie zwolnić.
4. Koliste czekanie. Istnieje zbiór {T0, T1, ..., Tn} oczekujących wątków taki, że T0 czeka na zasób przechowywany przez T1, ..., Tn−1 czeka na zasób przechowywany przez Tn, oraz Tn
czeka na zasób przechowywany przez T0.
Graf Alokacji Zasobów
• Zakleszczenia można opisać dokładniej w
kategoriach grafu ukierunkowanego zwanego grafem alokacji zasobów systemowych
• T = {T1, T2, ..., Tn}, zbiór wszystkich aktywnych wątków w systemie; R = {R1, R2, ..., Rm}, zbiór wszystkich typów zasobów w systemie
• Każda instancja zasobu jest reprezentowana jako kropka w prostokącie
• Skierowana krawędź Ti → Rj nazywa się krawędzią żądania; skierowana krawędź Rj → Ti nazywa się krawędzią przypisania
Graf Alokacji Zasobów
• Jeśli graf alokacji zasobów nie ma cyklu, system nie znajduje się w stanie zakleszczenia. Jeśli graf zawiera cykl, system może, ale nie musi, znajdować się w
stanie zakleszczenia
Graf alokacji zasobów z zakleszczeniem Graf alokacji zasobów z cyklem, ale bez zakleszczenia
Metody Radzenia Sobie z Zakleszczeniami
Ogólnie, problem zakleszczenia można rozwiązać na jeden z trzech sposobów:
• Możemy całkowicie zignorować problem i udawać, że w systemie nigdy nie występują zakleszczenia
• Możemy użyć protokołu, aby zapobiec lub uniknąć zakleszczenia, zapewniając, że system nigdy nie wejdzie w stan zakleszczenia
• Możemy pozwolić systemowi wejść w stan zakleszczenia, wykryć go i odnowić się
Zapobieganie Zakleszczeniom
• Zapobieganie zakleszczeniom zapewnia zestaw metod zapewniających, że co najmniej jeden z
niezbędnych warunków nie może zostać spełniony
Zapobieganie Zakleszczeniom:
Wzajemne Wykluczenie
• Warunek wzajemnego wykluczenia musi spełniać się.
Oznacza to, że co najmniej jeden zasób musi być nie współużytkowany
• Współużytkowane zasoby nie wymagają wzajemnie wykluczającego się dostępu, a zatem nie mogą
powodować zakleszczenia
• Zasadniczo nie możemy jednak uniknąć zakleszczenia, zabraniając spełnienie warunku wzajemnego
wykluczenia, ponieważ niektóre zasoby są z natury nie współużytkowane
Zapobieganie Zakleszczeniom:
Trzymaj i Czekaj
• Jeden protokół, którego możemy użyć, wymaga, aby
wszystkie zasoby zostały zażądane i przydzielone każdemu wątkowi przed rozpoczęciem jego wykonywania –
niepraktyczny
• Alternatywny protokół pozwala wątkowi żądać zasobów tylko wtedy, gdy nie ma żadnego (musi zwalniać wszystkie przydzielone zasoby przed żądaniem dodatkowych)
• Oba mają dwie główne wady:
• wykorzystanie zasobów może być niskie, ponieważ zasoby mogą być przydzielone, ale nieużywane
• głodzenie jest możliwe, wątek, który potrzebuje kilku popularnych zasobów, może musieć czekać w nieskończoność
Zapobieganie Zakleszczeniom: Brak Wywłaszczenia
• Jeśli wątek utrzymuje niektóre zasoby i żąda innego zasobu, którego nie można od razu mu przydzielić, wówczas wszystkie zasoby, które obecnie utrzymuje, są wywłaszczone
• Jeśli wątek żąda niektórych zasobów, sprawdź, czy są one przydzielone do innego oczekującego wątku, jeśli tak: zabierz pożądane zasoby
• Ten protokół jest często stosowany do zasobów,
których stan można łatwo zapisać i później przywrócić, takich jak rejestry procesora i transakcje w bazie
danych
Zapobieganie Zakleszczeniom:
Koliste Czekanie
• Trzy przedstawione do tej pory opcje zapobiegania zakleszczeniom są w większości przypadków
niepraktyczne
• Jednym ze sposobów zapewnienia, że warunek kolistego oczekiwania nigdy się nie sprawdza – całkowite uporządkowanie wszystkich typów zasobów i wymaganie, aby każdy wątek żądał zasobów w rosnącej kolejności wyliczania
Unikanie Zakleszczenia
• Możliwe skutki zapobiegania zakleszczeniom – niskie wykorzystanie urządzenia i zmniejszona przepustowość systemu
• Unikanie zakleszczeń wymaga, aby system
operacyjny otrzymał z góry dodatkowe informacje dotyczące zasobów, o które wątek będzie prosił i z których będzie korzystał w trakcie wykonywania
• Dzięki tej dodatkowej wiedzy system operacyjny może zdecydować dla każdego żądania, czy zasoby zostaną przydzielone czy wątek musi czekać
Unikanie Zakleszczenia: Stan Bezpieczny
• Stan jest bezpieczny, jeśli system może przydzielić zasoby do każdego wątku w określonej kolejności i wciąż unikać zakleszczenia
• Stan bezpieczny nie jest stanem zakleszczenia
• Niebezpieczny stan może doprowadzić do zakleszczenia
Unikanie Zakleszczenia: Algorytmy
• Algorytmy unikania zapewniają, że system zawsze pozostanie w bezpiecznym stanie:
o Algorytm na podstawie Grafu Alokacji Zasobów o Algorytm Bankiera
• Jeśli wątek zażąda zasobu, który jest obecnie dostępny, może nadal wymagać oczekiwania → wykorzystanie zasobów może być niższe
Wykrywanie Zakleszczeń
• Jeśli system nie stosuje algorytmu zapobiegania zakleszczeniom ani unikania zakleszczeń, może wystąpić sytuacja zakleszczenia
o Algorytm, który sprawdza stan systemu w celu ustalenia, czy nastąpiło zakleszczenie
o Algorytm odnawiania się po zakleszczeniu
• Schemat wykrywania i odnawiania się wymaga narzutu, który obejmuje koszty wykonania
algorytmu wykrywania, oraz potencjalne straty związane z odnawianiem się po zakleszczeniu
Wykrywanie Zakleszczeń: Graf Oczekiwania
• Jeśli wszystkie zasoby mają tylko jedną instancję → wariant grafu alokacji zasobów, graf oczekiwania
• Zakleszczenie występuje w systemie tylko wtedy, gdy graf oczekiwania zawiera cykl
Wykorzystanie Algorytmu Wykrywania: Bazy Danych
• Aktualizacje BD mogą być wykonywane jako transakcje
• Transakcja może obejmować kilka blokad, więc możliwe są zakleszczenia
• Aby zarządzać zakleszczeniem, większość
transakcyjnych systemów BD zawiera mechanizm wykrywania i odnawiania się po zakleszczeniu
• Po wykryciu zakleszczenia wybierana jest ofiara, a transakcja jest przerywana i wycofywana, zwalniając blokady i uwalniając pozostałe transakcje od
zakleszczenia
Odnawianie się po Zakleszczeniu
• Gdy algorytm wykrywania wykryje zakleszczenie, dostępnych jest kilka alternatyw
• Jedną z możliwości jest poinformowanie operatora o zakleszczeniu
• Inną możliwością jest automatyczne odnawianie się po zakleszczeniu:
o Przerwać jeden lub więcej wątków, aby usunąć koliste oczekiwanie
o Wywłaszczyć niektóre zasoby z jednego lub więcej zakleszczonych wątków
Zakleszczenie.
Streszczenie
Streszczenie (1)
• Zakleszczenie występuje w zbiorze procesów, gdy każdy proces czeka na zdarzenie, które może być spowodowane tylko przez inny proces w zbiorze
• Istnieją cztery niezbędne warunki zakleszczenia: (1) wzajemne wykluczenie, (2) trzymaj i czekaj, (3) brak wywłaszczenia, i (4) koliste czekanie. Zakleszczenie jest możliwe tylko wtedy, gdy wszystkie cztery
warunki są spełnione
Streszczenie (2)
• Zakleszczenia można modelować za pomocą grafu alokacji zasobów, w którym cykl wskazuje
zakleszczenie
• Zakleszczeniom można zapobiec, zapewniając, że jeden z czterech niezbędnych warunków nie może zostać spełniony
• Zakleszczenia można uniknąć, stosując algorytm bankiera, który nie przydziela zasobów, jeśli
doprowadziłby system do niebezpiecznego stanu, w którym byłoby możliwe zakleszczenie
Streszczenie (3)
• Algorytm wykrywania zakleszczenia może oceniać procesy i zasoby w działającym systemie w celu ustalenia, czy zbiór procesów jest w stanie
zakleszczenia
• Jeśli wystąpi zakleszczenie, system może podjąć próbę odnawiania działania po zakleszczeniu, przerywając jeden z procesów w kolistym
oczekiwaniu lub wywłaszczając zasoby przydzielone zakleszczonemu procesu