Futex (czyli fast userspace mutex) to rodzaj szybkiej blokady, która pozwala na rozwiązanie podstawowego problemu wzajemnego wykluczania. Niektóre bardziej złożone typy blokad (np. semafory albo kolejki wiadomości) mogą zostać zaimplementowane z użyciem futeksów. Szybkość futeksa polega na tym, że w przypadku braku rywalizacji o dostęp do blokady nie są wykorzystywane żadne wywołania systemowe.
Twoim zadaniem jest zaimplementować blokadę futex w systemie MINIX 3.
Aby skorzystać z blokady futex, trzeba będzie dołączyć plik nagłówkowy sys/futex.h
:
#include <sys/futex.h>
Po dołączeniu tego pliku dla programisty dostępny jest następujący interfejs biblioteki:
futex_t
int futex_init(futex_t *f)
futex_init
na uprzednio zainicjowanej blokadzie ma nieokreślone działanie.int futex_destroy(futex_t *f)
futex_init
przed ponownym jej użyciem.futex_destroy
na niezainicjowanej bądź zniszczonej blokadzie ma nieokreślone działanie.futex_lock
zwracając dowolny kod błędu (niezerową wartość).int futex_lock(futex_t *f)
futex_lock
powinna się zakończyć i zwrócić dowolny kod błędu (niezerową wartość).futex_lock
na niezainicjowanej bądź zniszczonej blokadzie ma nieokreślone działanie.int futex_unlock(futex_t *f)
futex_unlock
na niezainicjowanej, zniszczonej bądź podniesionej blokadzie ma nieokreślone działanie.Aby rozjaśnić definicje, został przygotowany przykładowy plik nagłówkowy.
Konieczne jest zaimplementowanie blokady według powyższego interfejsu. Zmiana prototypów powoduje automatyczne uzyskanie 0 punktów za zadanie. Strukturę futex_t
można modyfikować do woli!
Zauważ, że blokada typu futex nie musi gwarantować żywotności rozwiązania, a jedynie jego bezpieczeństwo!
Wzorcowe rozwiązanie wymaga użycia funkcji, które w atomowy sposób modyfikują pamięć. Taką funkcją jest np. omawiana na wykładzie instrukcja test-and-set
. Funkcje atomowe można umieścić w kodzie na dwa sposoby: jako odpowiednie instrukcje asemblera (funkcja asm
), lub skorzystać z wbudowanych w GCC funkcji atomowych. Ze względu na prostotę, omówiony zostanie drugi sposób użycia funkcji atomowych.
Domyślnym kompilatorem w systemie MINIX jest ACK. Niestety, nie zawiera on implementacji funkcji atomowych, w związku z tym biblioteka zostanie zbudowana z użyciem GCC.
Na początku należy zainstalować GCC poleceniem:
# pkgin install gcc44Następnie należy ustawić odpowiednią architekturę procesora dla GCC. Aby to zrobić, należy w pliku
/usr/src/lib/gnu_build.sh
pod linijką:
export CC=gccdopisać linijkę:
export CFLAGS="-march=i686"Teraz trzeba zbudować biblioteki i pliki nagłówkowe dla GCC, czyli w katalogu
/usr/src
wywołać polecenie:
# make gnu-includes gnu-librariesTeraz we wszystkich bibliotekach można korzystać z funkcji atomowych wbudowanych w GCC.
Aby przyspieszyć ten proces, został przygotowany domyślny obraz dysku systemu MINIX, zawierający zainstalowany kompilator GCC oraz zbudowane biblioteki w architekturze i686.
Do treści zostały przygotowane przykładowe programy, testujące poprawność zaimplementowanej blokady:
basic_test.cProgram ten testuje poprawną inicjalizację, zamknięcie, otwarcie i zniszczenie blokady.
numbers.cProgram ten testuje bezpieczeństwo, dodając do siebie 200000000 jedynek.
Rozwiązania należy przesyłać ze swojego konta na komputerze students za pomocą skryptu submit
na adres solab(at)mimuw.edu.pl
.
Wewnątrz spakowanego archiwum powinien znaleźć się katalog modified/
, w którym musi znajdować się drzewo wszystkich zmodyfikowanych plików. Na przykład, jeśli rozwiązanie modyfikuje plik /usr/src/include/minix/com.h
, w archiwum musi znajdować się kopia zmodyfikowanego pliku, dostępna pod ścieżką:
modified/usr/src/include/minix/com.h
Aby sprawdzić, czy rozwiązanie zostało prawidłowo spakowane, należy wykorzystać przygotowane skrypty testujące. Ich użycie jest łatwe: wystarczy rozpakować archiwum ze skryptami do katalogu z rozwiązaniem (nie do katalogu modified
!), a następnie na czystej instalacji systemu należy wewnątrz archiwum ze skryptami wykonać polecenie:
# makeSkopiuje ono wszystkie zmodyfikowane pliki do odpowiednich katalogów systemowych. Następnie można wykonać polecenie
# make compileSkompiluje ono na nowo wszystkie biblioteki oraz skompiluje i zainstaluje nową wersję serwera IPC.
Jeśli wszystko się powiedzie i rozwiązanie jest poprawne, to po ponownym uruchomieniu masyzny załączone programy testowe powinny się kompilować za pomocą GCC i uruchamiać bez błędów.
W ramach rozjaśnienia zostało udostępnione przykładowe archiwum, które zawiera rozwiązanie dodające wywołanie systemowe printmessage
, znane z pierwszego scenariusza. W archiwum package.tar.gz
załączone są skrypty testujące poprawność rozwiązania.
Wysłanie paczki w innym formacie, na którym nie działają skrypty instalujące rozwiązanie w systemie, powoduje automatyczne otrzymanie 0 punktów za zadanie!
Wszelkie pytania należy kierować do Maćka Wekseja (mw262978(at)mimuw.edu.pl), po wcześniejszym sprawdzeniu FAQ.