Futex (Fast Userspace Mutex) Łukasz Białek
Futex informacje podstawowe Stworzony w 2002 roku przez Hubertusa Franke, Matthew Kirkwooda, Ingo Molnára i Rustiego Russella. Jest mechanizmem w Linuxie, który pozwala na implementację podstawowego wzajemnego wykluczania Może być również użyty jako klocek do budowy wysoko-poziomowych mechanizmów wzajemnego wykluczania jak semafory
Od kiedy są z nami? Futexy pojawiły się po raz pierwszy w developerskiej wersji jądra o numerze 2.5.7 Ich semantyka zaczęła stabilizować się od wersji 2.5.40 Ostatecznie są już w stabilnych wersjach jądra o numerach 2.6.*
Co to tak naprawdę jest? Cały interfejs ze strony jądra zamyka się (głównie) w takim wywołaniu systemowym: long sys_futex (void *addr1, int op, int val1, struct timespec *timeout, void *addr2, int val3)
Co to tak naprawdę jest? long sys_futex (void *addr1, int op, int val1, struct timespec *timeout, void *addr2, int val3) Futex to tak naprawdę zwykła zmienna typu int na poziomie użytkownika Bez względu na to, czy system jest 32 czy 64-bitowy jej rozmiar to zawsze 4 bajty Wartość tej zmiennej może być dowolnie zmieniana przez aplikacje
Co to tak naprawdę jest? long sys_futex (void *addr1, int op, int val1, struct timespec *timeout, void *addr2, int val3) Każdy adres w pamięci (oprócz obszarów DMA itp.) może być użyty jako futex Ciekawostka: jeśli dwa procesy odwołują się do futexa w pamięci, którą dzielą, to odwołują się do tego samego futexa!
Co to tak naprawdę jest? - operacje long sys_futex (void *addr1, int op, int val1, struct timespec *timeout, void *addr2, int val3) FUTEX_WAIT sprawia, że proces jest usypiany do czasu, gdy nie zostanie obudzony Przed uśpieniem sprawdzana jest wartość zmiennej futexa jeśli nie jest równa val1, to od razu zwracany jest błąd EWOULDBLOCK. Jeśli timeout!= NULL, to po określonym czasie bez obudzenia zostanie zwrócony ETIMEDOUT.
Co to tak naprawdę jest? - operacje long sys_futex (void *addr1, int op, int val1, struct timespec *timeout, void *addr2, int val3) FUTEX_WAKE sprawia, że val1 procesów śpiących na addr1 zostanie obudzonych Najczęściej używa się 1 albo MAX_INT (wszystkie śpiące) Zwraca liczbę procesów, które zostały obudzone
Co to tak naprawdę jest? - operacje long sys_futex (void *addr1, int op, int val1, struct timespec *timeout, void *addr2, int val3) A po co są addr2 i val3? Są używane na przykład do operacji FUTEX_CMP_REQUEUE, która działa tak jak FUTEX_WAKE, ale dodatkowo nie obudzone procesy przerzuca na futex związany ze zmienną addr2. Całość rozpocznie się, jeśli wartość val3 jest równa wartości zmiennej wskazywanej przez addr1.
Dlaczego Fast? Tradycyjnie w systemie Linux do synchronizacji służą semafory, msgqueues, gniazda, system blokowania plików (flock()) Wszystkie te mechanizmy odwołują się do obiektów systemowych Każde odwołanie do tych mechanizmów wymaga wywołania systemowego A to jest wolne
Dlaczego Fast? Możliwym rozwiązaniem tego problemu jest system wzajemnego wykluczania na poziomie użytkownika Pozwala on pominąć dużą część odwołań do jądra, które były niezbędne przy wcześniej wymienionych mechanizmach Jednym z takich rozwiązań jest właśnie futex
Dlaczego Fast? Wszystkie operacje na futexach rozpoczynają się w przestrzeni użytkownika. Być może będą musiały komunikować się z jądrem, ale nie jest to koniecznie Jeśli aplikacja chce podnieść futex, to powinna po prostu zwiększyć wartość zmiennej futexa Jeśli wartość zmieniła się z 0 na 1 (lub inną dodatnią), to wszystko przebiegło zgodnie z planem (zero odwołań do jądra!)
Dlaczego Fast? Jeśli zmieniana wartość jest ujemna (co oznacza, że są czekający), to trzeba powiedzieć systemowi, żeby obudził czekających na tej zmiennej (co wymaga odwołania systemowego FUTEX_WAKE). Powinno się przed zrobieniem tego ustawić zmienną futexa na 1 Jeśli chcemy zamknąć futex, to powinniśmy zmniejszyć wartość zmiennej. Jeśli wartość po zmianie jest większa od 0, to wszystko wykonało się dobrze (znów żadnych odwołań do jądra!)
Dlaczego Fast? Jeśli jednak nowa wartość jest mniejsza od zera, to proces powinien ustawić tę wartość na -1 i powiedzieć systemowi (przez FUTEX_WAIT), że chce poczekać na podniesienie futexa Jak widać odwołania systemowe występują tylko w niektórych przypadkach!
Uwaga Podkreślane jest, że futexy nie są przeznaczone do bezpośredniego użycia jako prosta abstrakcja dla użytkownika końcowego. Oczekuje się od osób ich używających biegłości w asemblerze oraz konieczności czytania źródeł biblioteki futexów.
Bibliografia http://www.kernel.org/doc/ols/2002/ols2002- pages-479-495.pdf http://dept- info.labri.fr/~denis/enseignement/2004- SSECPD/Articles/01.pdf http://www.kernel.org/doc/manpages/online/pages/man7/futex.7.html http://www.kernel.org/doc/manpages/online/pages/man2/futex.2.html