2010-02-16 6 views
8

Sto provando a scrivere uno script che crea un numero di processi figlio biforcati utilizzando lo pcntl_* functions.biforcazione PHP e segnali figlio multipli

Fondamentalmente, c'è un singolo script che viene eseguito in un ciclo per circa un minuto, periodicamente interrogando un database per vedere se c'è un compito da eseguire. Se ce n'è uno, dovrebbe forchetta ed eseguire l'attività in un processo separato in modo che il genitore non sia bloccato da un'attività di lunga durata.

Poiché potrebbe esserci un numero elevato di attività pronte per essere eseguite, desidero limitare il numero di processi figli creati. Pertanto, sto tenendo traccia del numero di processi incrementando una variabile ogni volta che ne viene creata una (e poi facendo una pausa se ce ne sono troppi), e quindi decrementandola in un gestore di segnale. Un po 'come questo:

define(ticks = 1); 

$openProcesses = 0; // how many we have open 
$max = 3;   // the most we want open at a time 

pcntl_signal(SIGCHLD, "childFinished"); 

while (!time_is_up()) { 
    if (there_is_something_to_do()) { 
     $pid = pcntl_fork(); 
     if (!$pid) {  // I am the child 
      foo();  // run the long-running task 
      exit(0);  // and exit 
     } else {   // I am the parent 
      ++$openProcesses; 
      if ($openProcesses >= $max) { 
       pcntl_wait($status); // wait for any child to exit 
      }       // before continuing 
     } 
    } else { 
     sleep(3); 
    } 
} 

function childFinished($signo) { 
    global $openProcesses; 
    --$openProcesses; 
} 

Questo funziona più o meno bene la maggior parte del tempo, tranne quando due o più processi terminano contemporaneamente - la funzione di gestore di segnale viene chiamato una sola volta, che tiri fuori il mio contatore. La ragione di questo si spiega con "Anonimo" nel notes of the PHP manual:

bambini più ritorno inferiore al numero dei bambini che escono in un dato segnali SIGCHLD momento è un comportamento normale per sistemi Unix (POSIX). SIGCHLD potrebbe essere letto come "uno o più bambini hanno cambiato lo stato - vai a esaminare i tuoi figli e raccogli i loro valori di stato".

La mia domanda è questa: Come esaminare i bambini e raccogliere il loro stato? Esiste un modo affidabile per verificare quanti processi figli sono aperti in un dato momento?

Utilizzo di PHP 5.2.9

+0

probabilmente solo utilizzando https://www.rabbitmq.com/ renderebbe tutto molto meno soggetto a errori –

risposta

0

Si potrebbe avere figli inviare un SIGUSR1 al genitore quando iniziano, poi un SIGUSR2 prima di uscire. L'altra cosa con cui si ha a che fare quando si usano segnali primitivi è il kernel che le unisce, cosa che non fa con i segnali RT. In teoria, QUALSIASI segnale non rt potrebbe essere unito.

Si potrebbe implementare una sorta di blocco semplice con sqlite, in cui solo un bambino alla volta può avere il bastone parlante. Assicurati che i bambini gestiscano normalmente segnali fatali in modo che rimangano vivi per liberare la serratura.

2

Un modo è quello di mantenere un array di PID dei processi figlio e nel gestore di segnali controllare ogni PID per vedere se è ancora in esecuzione. Il codice (non testato) sarebbe simile:

define(ticks = 1); 

$openProcesses = 0; 
$procs = array(); 
$max = 3; 

pcntl_signal(SIGCHLD, "childFinished"); 

while (!time_is_up()) { 
    if (there_is_something_to_do()) { 
     $pid = pcntl_fork(); 
     if (!$pid) {  
      foo();   
      exit(0);  
     } else {   

      $procs[] = $pid; // add the PID to the list 

      ++$openProcesses; 
      if ($openProcesses >= $max) { 
       pcntl_wait($status);  
      }       
     } 
    } else { 
     sleep(3); 
    } 
} 

function childFinished($signo) { 

    global $openProcesses, $procs; 

    // Check each process to see if it's still running 
    // If not, remove it and decrement the count 
    foreach ($procs as $key => $pid) if (posix_getpgid($pid) === false) { 
     unset($procs[$key]); 
     $openProcesses--; 
    } 

}