2011-09-11 14 views
5

Wystąpił problem z działaniem systemu Linux futex (FUTEX_WAIT), czasami powracający wcześnie pozornie bez przyczyny. Dokumentacja określa pewne warunki, które mogą spowodować, że powróci wcześniej (bez wartości FUTEX_WAKE), ale wszystkie z nich obejmują niezerowe wartości zwracane: EAGAIN, jeśli wartość na adresie futex nie jest zgodna, ETIMEDOUT dla oczekiwania na czas, który upłynął, EINTR po przerwaniu przez a (bez ponownego uruchamiania) sygnału itp. Ale widzę wartość zwracaną równą 0. Co, poza FUTEX_WAKE lub zakończeniem wątku, którego wskaźnik wskazuje na futex, może spowodować powrót do wartości z wartością zwracaną przez: FUTEX_WAIT 0?Linux futex syscall fałszywy budzi się z wartością zwracaną 0?

W przypadku, jest to przydatne, szczególny futex Czekałem na to adres wątek tid (ustawiony przez clone syscall z CLONE_CHILD_CLEARTID), a nić miał nie zakończone. Moje (najwyraźniej niepoprawne) założenie, że operacja FUTEX_WAIT powracająca do 0 może się zdarzyć tylko wtedy, gdy zakończony wątek prowadzi do poważnych błędów w logice programu, które od tego czasu naprawiłem przez zapętlenie i ponowienie, nawet jeśli zwraca 0, ale teraz jestem ciekawy jak dlaczego tak się stało.

Oto minimalne przypadek testowy:

#define _GNU_SOURCE 
#include <sched.h> 
#include <sys/syscall.h> 
#include <unistd.h> 
#include <linux/futex.h> 
#include <signal.h> 

static char stack[32768]; 
static int tid; 

static int foo(void *p) 
{ 
     syscall(SYS_getpid); 
     syscall(SYS_getpid); 
     syscall(SYS_exit, 0); 
} 

int main() 
{ 
     int pid = getpid(); 
     for (;;) { 
       int x = clone(foo, stack+sizeof stack, 
         CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND 
         |CLONE_THREAD|CLONE_SYSVSEM //|CLONE_SETTLS 
         |CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID 
         |CLONE_DETACHED, 
         0, &tid, 0, &tid); 
       syscall(SYS_futex, &tid, FUTEX_WAIT, x, 0); 
       /* Should fail... */ 
       syscall(SYS_tgkill, pid, tid, SIGKILL); 
     } 
} 

Niech go uruchomić na chwilę, co powinno ostatecznie zakończyć z Killed (SIGKILL), co jest możliwe tylko wtedy, gdy nitka wciąż istnieje, gdy FUTEX_WAIT zyski.

Zanim ktokolwiek pójdzie, zakładając, że to tylko jądro budzi futex, zanim skończy się niszczenie nici (co w rzeczywistości może się zdarzyć w moim minimalnym przypadku testowym tutaj), proszę zauważyć, że w moim oryginalnym kodzie faktycznie zaobserwowałem kod obszaru użytkownika uruchomione w wątku po powrocie FUTEX_WAIT.

+0

Myślę, że możemy potrzebować zobaczyć minimalny przykład; ciężko jest wymyślić znaczną radę, ponieważ tak wiele jest nieznanych (w każdym razie opowiem moje przeczucie jako tymczasową odpowiedź, bo to jest wielkie na komentarz) – sehe

+0

Rzeczywiście, zobaczę, czy mogę zebrać minimalne przykład. –

+0

hm, myślę, że strona man jest dość niejasna. warunki pod zwracaną wartością "FUTEX_WAIT" kwalifikują warunki inne niż zerowe jako warunki * błędu *, a nie tylko diagnostykę. Później mówi: "W przypadku błędu wszystkie operacje zwracają -1 i ustawiają errno, aby wskazać błąd." Z drugiej strony warunki tutaj nie są powtarzane w sekcji ** ERRORS **. –

Odpowiedz

0

Czy możesz mieć do czynienia z warunkiem wyścigowym pomiędzy tym, czy operacje rodzica lub dziecka są ukończone jako pierwsze? Prawdopodobnie możesz zbadać tę teorię, umieszczając małe sny na początku swojego foo() lub bezpośrednio po klonie(), aby określić, czy wymuszona kolejność zdarzeń maskuje problem. Nie zalecam naprawiania niczego w ten sposób, ale może to być pomocne w zbadaniu. Może futex nie jest gotowy na czekanie, dopóki dziecko nie przejdzie dalej przez jego inicjalizację, ale klon rodzica ma wystarczająco dużo, aby powrócić do dzwoniącego?

W szczególności obecność opcji CLONE_VFORK sugeruje, że jest to niebezpieczny scenariusz. Być może potrzebny jest dwukierunkowy mechanizm sygnalizacyjny, tak aby dziecko sygnalizowało rodzicowi, że dotarło wystarczająco daleko, aby można było bezpiecznie poczekać na dziecko.

+0

Jeśli 'tid' nie został jeszcze napisany z wartością Tid w czasie wywołania' FUTEX_WAIT', operacja powróci z 'EAGAIN' zamiast 0 (W każdym razie, cały punkt flagi 'CLONE_PARENT_SETTID' na' clone' ma na celu upewnienie się, że wartość została zapisana przed każdym wątkiem, który jest w stanie wykonać.) Nie widzę żadnej możliwości wyścigu tutaj w przestrzeni użytkownika, ponieważ nic interesujące dzieje się w przestrzeni użytkownika ... –

Powiązane problemy