2010-02-16 10 views
8

Estoy tratando de escribir una secuencia de comandos que crea una serie de procesos secundarios bifurcados utilizando el pcntl_* functions.bifurcación de PHP y múltiples señales secundarias

Básicamente, hay un único script que se ejecuta en un bucle durante aproximadamente un minuto, sondeando periódicamente una base de datos para ver si hay una tarea para ejecutar. Si hay uno, debe bifurcar y ejecutar la tarea en un proceso separado para que el padre no quede retenido por una tarea de larga ejecución.

Dado que posiblemente podría haber una gran cantidad de tareas listas para ejecutarse, quiero limitar el número de procesos secundarios que se crean. Por lo tanto, estoy haciendo un seguimiento del número de procesos al incrementar una variable cada vez que se crea (y luego pausar si hay demasiados), y luego disminuirla en un manejador de señal. Algo así como esto:

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

Esto funciona bastante bien la mayor parte del tiempo, excepto cuando dos o más procesos terminen al mismo tiempo - la función de controlador de señal sólo se llama una vez, lo que arroja un vistazo a mi contador. La razón de esto se explica por "Anónimo" en el notes of the PHP manual:

niños Múltiples vuelven menos que el número de niños que salen en un momento dado señales SIGCHLD es un comportamiento normal para sistemas Unix (POSIX). SIGCHLD podría leerse como "uno o más niños cambiaron de estado: examine a sus hijos y coseche sus valores de estado".

Mi pregunta es esta: ¿Cómo examino a los niños y cosecho su estado? ¿Hay alguna manera confiable de verificar cuántos procesos hijos están abiertos en un momento dado?

Uso de PHP 5.2.9

+0

probablemente solo usando https://www.rabbitmq.com/ haría todo menos propenso a errores –

Respuesta

0

Se podía tener hijos envían un SIGUSR1 al padre cuando empiezan, a continuación, un SIGUSR2 antes de la salida. La otra cosa con la que te enfrentas cuando usas señales primitivas es el kernel fusionándolas, lo que no ocurre con las señales RT. En teoría, CUALQUIER señal no rt podría fusionarse.

Puede implementar algún tipo de bloqueo simple usando sqlite, donde solo un niño a la vez puede tener el parlante. Solo asegúrese de que los niños manejen señales normalmente fatales para que permanezcan vivos y liberen el bloqueo.

2

Una forma es mantener una matriz de los PID de los procesos hijos, y en el manejador de señales comprobar cada PID para ver si aún se está ejecutando. El código (no probado) se vería así:

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

} 
Cuestiones relacionadas