2010-01-13 8 views
39

Ho più app compilate con g ++, in esecuzione su Ubuntu. Sto usando i nomi dei semafori per coordinare tra diversi processi.Come si ripristina un semaforo quando il processo che lo ha ridotto a zero si arresta in modo anomalo?

Tutto funziona bene, tranne nella seguente situazione: se uno dei processi chiamate sem_wait() o sem_timedwait() per decrementare il semaforo e poi si blocca o viene ucciso -9 prima che ha la possibilità di chiamare sem_post(), poi da quel momento in poi , il semaforo nominato è "inutilizzabile".

Con "inutilizzabile", ciò che intendo è che il conteggio del semaforo è ora zero e il processo che dovrebbe averlo riportato a 1 è morto o è stato ucciso.

Non riesco a trovare un'API sem_*() che potrebbe dirmi che il processo che ha decrementato per ultimo si è arrestato.

Mi manca un'API da qualche parte?

Ecco come apro il semaforo di nome:

sem_t *sem = sem_open("/testing", 
    O_CREAT  | // create the semaphore if it does not already exist 
    O_CLOEXEC , // close on execute 
    S_IRWXU  | // permissions: user 
    S_IRWXG  | // permissions: group 
    S_IRWXO  , // permissions: other 
    1   ); // initial value of the semaphore 

Ecco come ho farlo diminuire:

struct timespec timeout = { 0, 0 }; 
clock_gettime(CLOCK_REALTIME, &timeout); 
timeout.tv_sec += 5; 

if (sem_timedwait(sem, &timeout)) 
{ 
    throw "timeout while waiting for semaphore"; 
} 

risposta

32

Si scopre che non esiste un modo per ripristinare in modo affidabile il semaforo. Certo, chiunque può aggiungere post_sem() al semaforo specificato per ottenere nuovamente il conteggio oltre lo zero zero, ma come sapere quando è necessario un tale ripristino? L'API fornita è troppo limitata e non indica in alcun modo quando ciò è accaduto.

Attenzione agli strumenti ipc disponibili anche - gli strumenti comuni ipcmk, ipcrm e ipcs sono solo per i semafori obsoleti di SysV. In particolare, non funzionano con i nuovi semafori POSIX.

Ma sembra che ci siano altre cose che possono essere utilizzate per bloccare le cose, che il sistema operativo rilascia automaticamente quando un'applicazione muore in un modo che non può essere catturato in un gestore di segnale. Due esempi: un socket di ascolto associato a una porta specifica o un blocco su un file specifico.

Ho deciso che il blocco su un file è la soluzione di cui avevo bisogno. Così, invece di una chiamata sem_wait() e sem_post(), sto usando:

lockf(fd, F_LOCK, 0) 

e

lockf(fd, F_ULOCK, 0) 

alla chiusura dell'applicazione in qualsiasi modo, il file viene chiuso automaticamente, che rilascia anche il blocco di file. Altre app client in attesa del "semaforo" sono quindi libere di procedere come previsto.

Grazie per l'aiuto, ragazzi.

+1

+1, in fin dei conti fare la stessa cosa, i semafori sono inutili in tali scenari –

+0

Qualcuno mi ha mandato una e-mail per chiedere maggiori dettagli. Ho scritto un piccolo post sul blog quasi 3 anni fa quando mi sono imbattuto in questo problema. Maggiori dettagli su come ho risolto il problema con il blocco dei file sono disponibili qui: http://charette.no-ip.com:81/programming/2010-01-13_PosixSemaphores/index.html –

+0

La stessa cosa può essere ottenuta semplicemente aprendo un chiudere un file? Ho trovato questo nella pagina man per open(): "Quando si apre un file, è possibile ottenere una semantica lock con flock (2) impostando O_SHLOCK per un blocco condiviso o O_EXLOCK per un blocco esclusivo." –

2

Si dovrebbe essere in grado di trovare dalla shell utilizzando lsof. Allora forse puoi cancellarlo?

Aggiornamento

Ah sì ... man -k semaphore in soccorso.

Sembra che sia possibile utilizzare ipcrm per sbarazzarsi di un semaforo. Sembra che tu non sia il primo con questo problema.

+1

Sì, so di ipcrm, ma non aiuta. Se avessi saputo che il semaforo era andato perduto, avrei potuto facilmente sem_post() "riprenderlo". Sembra che il problema sia che non è stato attivato alcun evento per indicare che l'applicazione che l'ha decrementata per ultima è stata uccisa. –

+1

Inoltre, appena notato nella pagina man che ipcrm funziona solo sui vecchi semafori SysV, non sui semafori POSIX. Lo stesso con IPS. –

1

Se il processo è stato ANNULLATO, non ci sarà alcun modo diretto per determinare che è andato via.

È possibile controllare periodicamente l'integrità di tutti i semafori di cui si dispone: utilizzare semctl (cmd = GETPID) per trovare il PID per l'ultimo processo che ha toccato ogni semaforo nello stato descritto, quindi verificare se tale processo è ancora in giro. In caso contrario, eseguire la pulizia.

+0

Qualcosa in questo senso è quello che stavo cercando, ma ovviamente per i semafori POSIX che si trovano in #include . Da quello che posso dire, lo stile semctl() delle chiamate è specifico per i vecchi semafori SysV da . –

2

Dovrai ricontrollare ma credo che sem_post possa essere chiamato da un gestore di segnale. Se sei in grado di cogliere alcune delle situazioni che stanno riducendo il processo, questo potrebbe essere d'aiuto.

A differenza di un mutex, qualsiasi processo o thread (con autorizzazioni) può inviare al semaforo. Puoi scrivere una semplice utility per resettarla. Presumibilmente, sai quando il tuo sistema è in fase di stallo. È possibile portarlo giù ed eseguire il programma di utilità.

Anche il semaphone è solitamente elencato in/dev/shm e può essere rimosso.

I semafori di SysV sono più accomodanti per questo scenario. È possibile specificare SEM_UNDO, in cui il sistema eseguirà il back-up delle modifiche al semaforo creato da un processo se questo dovesse decadere. Hanno anche la capacità di dirti l'ultimo id del processo per modificare il semaforo.

+1

Alcuni segnali come kill -9 bypassa i gestori dei segnali, che è la situazione in cui mi sono imbattuto. Ho un gestore di segnale per quelli che posso catturare, e in un distruttore per un oggetto basato su scope, chiamo sem_post() mentre lo stack si svolge. Ma quei pochi segnali inafferrabili persistenti sono ciò che speravo di risolvere. –

+1

Penso che una domanda giusta sia chiedere chi sono gli utenti e perché stanno uccidendo l'app in questo modo?Puoi provare la rotta SysV o anche i blocchi di file, che dovrebbero essere ripristinati quando il processo muore. – Duck

+0

In realtà, è quello che ho deciso di fare ieri sera. Poiché i file che sono stati aperti() e lockf() vengono rilasciati automaticamente quando le applicazioni vengono uccise -9, questo metodo di "comunicazione" funziona in modo più affidabile dei semafori considerando ciò che ho bisogno di coordinare. –

4

Questo è un tipico problema nella gestione dei semafori. Alcuni programmi utilizzano un singolo processo per gestire l'inizializzazione/cancellazione del semaforo. Di solito questo processo fa solo questo e nient'altro.Le altre applicazioni possono attendere fino a quando il semaforo è disponibile. Ho visto questo fatto con l'API di tipo SYSV, ma non con POSIX. Simile a ciò che è stato menzionato 'Duck', utilizzando il flag SEM_UNDO nella chiamata semop().


Ma, con le informazioni che ci hai fornito vorrei suggerire che non fare per utilizzare i semafori. Soprattutto se il tuo processo è in pericolo di essere ucciso o in crash. Prova a utilizzare qualcosa che il sistema operativo ripulirà automaticamente per te.

-1

Basta fare uno sem_unlink() immediatamente dopo il sem_open(). Linux rimuoverà dopo che tutti i processi hanno chiuso la risorsa, che include le chiusure interne.

+1

Non causerebbe l'eliminazione del semaforo con nome? Non volevo cancellarlo, volevo pubblicarlo quando una delle app si è bloccata o è stata uccisa. (Molte app in uso qui, tutte che lavorano con lo stesso insieme di semafori nominati per coordinare alcuni lavori interni.) –

+2

Questo non funzionerà: se un altro processo è bloccato sul semaforo e il processo che lo ha bloccato si blocca, quindi il processo bloccato manterrà il semaforo aperto e quindi il semaforo non sarà mai distrutto. –

5

Utilizzare un file di blocco anziché un semaforo, in modo simile alla soluzione di @Stephane ma senza le chiamate flock(). È sufficiente aprire il file utilizzando un blocco esclusivo:

//call to open() will block until it can obtain an exclusive lock on the file. 
errno = 0; 
int fd = open("/tmp/.lockfile", 
    O_CREAT | //create the file if it's not present. 
    O_WRONLY | //only need write access for the internal locking semantics. 
    O_EXLOCK, //use an exclusive lock when opening the file. 
    S_IRUSR | S_IWUSR); //permissions on the file, 600 here. 

if (fd == -1) { 
    perror("open() failed"); 
    exit(EXIT_FAILURE); 
} 

printf("Entered critical section.\n); 
//Do "critical" stuff here. 

//exit the critical section 
errno = 0; 
if (close(fd) == -1) { 
    perror("close() failed"); 
    exit(EXIT_FAILURE); 
} 

printf("Exited critical section.\n"); 
+1

Buon codice, con 1 modifica: è necessario creare il file di blocco prima che inizi il conflitto e, naturalmente, tenerlo collegato. In caso contrario, nei miei test in Mac OS X 10.10 DP5, open() può avere successo per due processi peer che si contendono di creare inizialmente il file, se entro pochi millisecondi. Il problema si verifica con il codice di Stéphane o Raffi. Poi ho provato lo stress. Risultato: il codice di Raffi funzionava perfettamente, il codice di Stéphane non era del tutto corretto. Non ho studiato il motivo. Se interessati, consultare https://github.com/jerrykrinock/ClassesObjC/blob/master/SSYSemaphore.h e .m. –

+0

@JerryKrinock Ma, non 'open()' crea il file di blocco se non è presente (quando viene fornito il flag O_CREAT)? –

+1

Ho intenzione di scattare dal basso, perché non ho i 30 minuti necessari per aggiornare correttamente la mia comprensione di questo disponibile al momento. Penso che la risposta sia, sì, open() con O_CREAT creerà il file se necessario, ma se due processi eseguono open() in pochi millisecondi l'uno dall'altro, i risultati sono imprevedibili. Da qui il mio suggerimento di creare il file di blocco prima che abbia importanza; bene, aggiungerò, a meno che non sia OK per il primo conflitto essere un buttato via. –

Problemi correlati