L6: Pamięć dzielona i synchronizacja#

L6: Wiosenne sprzątanie#

Mimo daty ogłoszenia, studenci Wydziału Lingwistyki Matematycznej Politechniki Skaryszewskiej, świadomi problemu, bardzo poważnie potraktowali akcję czyszczenia klawiatur w salach laboratoryjnych, zainicjowaną przez samorząd studencki. Tłumnie i ochoczo wsparli tę inicjatywę i w Wielką Sobotę, zamiast ze święconką, zebrali się na miejscu z szczotkami, szpachlami i zestawem środków dezynfekujących (wersja studencka).

Liczba chętnych przerosła najśmielsze oczekiwania organizatorów i zaczęli się obawiać, że zamiast efektywnej pracy, zapanuje jeszcze większy chaos. Dlatego zwrócili się do swojego wieloletniego partnera, Wydziału MiNI, z prośbą o opracowanie systemu zarządzającego procesem sprzątania i obsługującego nieprzewidziane wypadki.

System ma nadzorować sprzątanie w jednej sali. Sala zawiera \(m\) klawiatur, z których każda ma \(k\) klawiszy. Aby uniknąć tłoku i właściwie alokować zasoby, przyjęto, że przy jednej klawiaturze może znajdować się jednocześnie najwyżej KEYBOARD_CAP studentów.

Pomóż studentom skończyć sprzątanie wydziału przed Wielkanocą!

Etapy:#

Etap 1 (6 pkt)#

Program przyjmuje trzy argumenty: KEYBOARD_CAP\( \leq n \leq 20\) — liczbę studentów sprzątających salę — oraz \(1 \leq m \leq 5\) i \(5 \leq k \leq\) KEYBOARD_CAP — jak opisano powyżej. Utwórz \(n\) procesów potomnych (studentów). Każdy z nich otwiera \(m\) nazwanych semaforów /sop-sem-1, \(\cdots\), /sop-sem-m. Jeśli dany semafor nie istnieje, należy go utworzyć i zainicjalizować wartością KEYBOARD_CAP.

Po otwarciu semafora, proces-student w pętli 10 iteracji wybiera losową klawiaturę, a następnie próbuje do niej podejść, czekając jeśli nie ma miejsca — użyj odpowiedniego semafora. Następnie wypisuje Student <PID>: cleaning keyboard <i> (gdzie \(i\) to numer wylosowanej klawiatury), czeka 300 ms i zwalnia semafor. Po zakończeniu pętli proces kończy działanie.

Proces główny czeka na zakończenie wszystkich procesów potomnych, a następnie usuwa wszystkie utworzone semafory, wypisuje Cleaning finished! i kończy działanie.

Natychmiast po uruchomieniu programu usuń wymienione semafory, jeśli istnieją.

Wskazówka: Możesz zignorować kod błędu ENOENT w określonej funkcji.

Etap 2 (5 pkt)#

Zanim zamodelujemy klawiatury w programie, ogranicz do nich dostęp przed inicjalizacją. Przed tworzeniem procesów potomnych, utwórz blok anonimowej pamięci dzielonej, który (na razie) powinien przechowywać jedną barierę współdzieloną między procesami. Utwórz tę barierę natychmiast po utworzeniu pamięci dzielonej, aby studenci mieli do niej dostęp. Bariera powinna czekać na łącznie \(n+1\) procesów. Studenci, przed tworzeniem semaforów, czekają na barierze. Proces główny, po utworzeniu procesów potomnych, śpi 500 ms, a następnie czeka na barierze.

Wskazówka: Tworzenie współdzielonej bariery jest analogiczne do tworzenia współdzielonego muteksu (zamiast pthread_mutexattr_t użyj pthread_barrierattr_t).

Etap 3 (8 pkt)#

Zamodeluj w systemie proces czyszczenia klawiatur. Klawiatury są reprezentowane przez jeden wspólny blok pamięci dzielonej, który powinien być zmapowany z nazwanego obiektu pamięci o nazwie SHARED_MEM_NAME. Blok powinien zawierać \(mk\) liczb typu double, po jednej dla każdego klawisza w klawiaturach. Wykonaj tworzenie i mapowanie obiektu pamięci w procesie głównym po utworzeniu procesów studentów. Następnie zainicjalizuj pamięć dzieloną, wypełniając wszystkie jej pola jedynkami (\(1.0\)). Upewnij się, że żaden proces potomny nie używa zawartości pamięci dzielonej przed inicjalizacją — użyj bariery z poprzedniego etapu.

Procesy studentów otwierają i mapują obiekt pamięci SHARED_MEM_NAME, a następnie, po wylosowaniu klawiatury, losują numer klawisza i przystępują do czyszczenia: proces powinien nadal czekać 300 ms, a następnie podzielić wartość odpowiedniego pola przez 3. Dodaj informację o wylosowanym numerze klawisza do komunikatu z etapu 1: Student <PID>: cleaning keyboard <i>, key <j>.

Chroń procesy studentów przed jednoczesnym czyszczeniem tego samego klawisza — użyj oddzielnego muteksu dla każdego klawisza. Umieść muteksy, razem z barierą, w pamięci dzielonej z poprzedniego etapu.

Po zakończeniu wszystkich procesów studenckich, proces główny wypisuje stan wszystkich klawiatur (użyj dostarczonej funkcji print_keyboards_state) i usuwa obiekt pamięci dzielonej.

Etap 4 (5 pkt)#

Czyszczenie klawiatury to bez wątpienia wyczerpująca czynność, szybko wysysająca wszystkie siły sprzątającego. Tym razem studenci wykonują proces czyszczenia w nieskończonej pętli. Jednak podczas czyszczenia student ma 1% szans na upadek z wyczerpania. Zasymuluj to przez wypisanie Student <PID>: I have no more strength!, zwolnienie semafora i wywołanie funkcji abort() tuż przed aktualizacją wartości brudu na klawiszu.

Gdy student próbuje wyczyścić klawisz, którego inny student nie był w stanie dokończyć czyścić, wypisuje Student <PID>: someone is lying here, help!!!, a następnie ogłasza to wszystkim studentom za pomocą współdzielonej flagi (umieść ją i chroniący ją muteks w bloku pamięci obok innych muteksów i bariery). Studenci, zaniepokojeni taką sytuacją, kończą sprzątanie i wybiegają z sali w panice (tzn. przerywają pętlę i kończą działanie).

Zadanie 5 (0 pkt)#

Gratulacje! Teraz Twoja kolej na wyczyszczenie klawiatury na swoim stanowisku pracy.

Kod startowy#