2011-09-11 18 views
5

Ho riscontrato un problema con l'operazione syscall di Linux futex (FUTEX_WAIT), che a volte ritornava in anticipo apparentemente senza motivo. La documentazione specifica alcune condizioni che potrebbero causare un ritorno anticipato (senza uno FUTEX_WAKE) ma tutte implicano valori di ritorno diversi da zero: EAGAIN se il valore all'indirizzo futex non corrisponde, ETIMEDOUT per le attese temporizzate che scadono, EINTR quando interrotte da un segnale (non riavviato), ecc. Ma sto vedendo un valore di ritorno pari a 0. Ciò che, a parte lo FUTEX_WAKE o la chiusura di una discussione il cui puntatore set_tid_address punta al futex, potrebbe causare FUTEX_WAIT restituire con un valore di ritorno di 0?Linux wex spysc di syscall con valore di ritorno 0?

Nel caso è utile, il futex particolare ero in attesa sul filo è l'indirizzo tid (fissato dal clone syscall con CLONE_CHILD_CLEARTID), e il filo aveva non terminato. La mia supposizione (apparentemente errata) che l'operazione FUTEX_WAIT che restituisce 0 possa avvenire solo quando il thread terminato porta a errori gravi nella logica del programma, che da allora ho risolto in loop e riprovando anche se restituisce 0, ma ora sono curioso al perché è successo.

Ecco un test minimo:

#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); 
     } 
} 

farlo funzionare per qualche tempo, esso dovrebbe eventualmente terminare con Killed (SIGKILL), che è possibile solo se il filo ancora esiste quando il FUTEX_WAIT rendimenti. codice userspace

Prima che qualcuno va assumendo questo è solo il kernel svegliare il futex prima che finisce di distruggere il filo (che potrebbe in realtà essere accadendo nel mio caso di test minima qui), si ricorda che nel mio codice originale, in realtà ho osservato eseguito correttamente nel thread dopo il FUTEX_WAIT restituito.

+0

Penso che potremmo aver bisogno di vedere un esempio minimo; è difficile trovare un consiglio sostanziale, dal momento che tanto è sconosciuto (posterò la mia unica intuizione come risposta temporanea in ogni caso, perché è troppo grande per un commento) – sehe

+0

Infatti, vedrò se riesco a mettere insieme un minimo esempio. –

+0

hm, penso che la pagina man sia piuttosto poco chiara. le condizioni sotto il valore di ritorno di "FUTEX_WAIT" qualifica le condizioni diverse da zero come condizioni di * errore *, non solo la diagnostica. Quindi in seguito dice "Nel caso di un errore, tutte le operazioni restituiscono -1 e impostano errno per indicare l'errore." D'altra parte le condizioni qui non vengono ripetute nella sezione ** ERRORI **. –

risposta

0

Potrebbe esserci una condizione di competizione tra il completamento delle operazioni padre o figlio? Probabilmente si può indagare su questa teoria mettendo piccoli sleep all'inizio di foo() o immediatamente dopo il clone() per determinare se una sequenza forzata di eventi maschera il problema. Non consiglio di aggiustare nulla in questo modo, ma potrebbe essere utile investigare. Forse il futex non è pronto per essere aspettato fino a quando il bambino non si allontana dalla sua inizializzazione, ma il clone del genitore ha abbastanza per tornare al chiamante?

In particolare, la presenza dell'opzione CLONE_VFORK sembra implicare che si tratta di uno scenario pericoloso. Potrebbe essere necessario un meccanismo di segnalazione bidirezionale in modo tale che il bambino segnali al genitore che è arrivato abbastanza lontano da essere sicuro di aspettare il bambino.

+0

Se 'tid' non era già stato scritto con il valore tid nel momento in cui viene chiamato' FUTEX_WAIT', l'operazione ritorna con 'EAGAIN' invece di 0 (Ad ogni modo, l'intero punto del flag "CLONE_PARENT_SETTID' su' clone' è di assicurare che il valore sia stato scritto prima che uno dei thread sia in grado di eseguirlo.) Non vedo alcuna possibilità per una gara qui in userspace poiché nulla interessante sta accadendo in userspace ... –