2012-02-06 4 views
5

Tengo este bloque de código que funciona perfectamente para mis necesidades en mis diversos programas php cli. Excepto que a veces un niño se convertirá en un zombie.PHP Bifurcación: matar a un niño cuando se convierte en un zombi

Mi pregunta es dónde colocar el código para verificar si un niño corre, por ejemplo, durante 5 minutos y si es más largo que matarlo.

Sé acerca de posix_kill para matarlo y cómo seguirlo. There are examples of taskmanagers here.

No estoy seguro de cómo combinar estas nuevas características en el código. Cada vez que intento hacerlo, me meto en un desastre. ¿Tal vez alguien que sabe sobre bifurcación puede arreglar mi código?

Ignora todos los errores_logs - Me gusta ver qué sucede cuando se ejecuta.

public function __construct($data) {   
    //Keep track of all of the children processes 
    $this->children = Array(); 

    //Specify the maximum number of child processes to fork at any given time 
    $this->max_children = 5; 
} 

private function process() 
{ 
    foreach ($collection as $stuff) 
    { 
     //FORK THE PROCESS 
     $pid = pcntl_fork(); 

     //Something went wrong 
     if($pid == -1) 
     { 
      error_log ("could not fork"); 
      die(); 
     } 

     //PARENT PROCESS 
     if($pid) 
     { 
      //error_log ("Parent: forked " . $pid); 
      $this->children[] = $pid; 
     } 
     //CHILD PROCESS 
     else 
     { 
      // Do stuff here             

      exit(); //Exit the child thread so it doesn't continue to process the data 
     } 

     //COLLECT ALL OF THE CHILDREN AS THEY FINISH 
     while(($c = pcntl_wait($status, WNOHANG OR WUNTRACED)) > 0) 
     { 
      //error_log ("Collected Child - " . $c); 
      $this->remove_thread($this->children, $c); 

      error_log ("children left: " . count($this->children)); 
     } 

     //WAIT FOR A CHILD TO FINISH IF MAXIMUM PROCESSES IS EXCEEDED 
     if(sizeof($this->children) > $this->max_children) 
     { 
      //error_log ("Maximum children exceeded. Waiting..."); 
      if(($c = pcntl_wait($status, WUNTRACED)) > 0) 
      { 
       //error_log ("Waited for Child - " . $c); 
       $this->remove_thread($this->children, $c); 

       error_log ("children left: " . count($this->children)); 
      } 
     } 
    } 

    //COLLECT ALL OF THE CHILDREN PROCESSES BEFORE PROCEEDING 
    while(($c = pcntl_wait($status, WUNTRACED)) > 0){ 
     //error_log ("Child Finished - " . $c); 
     $this->remove_thread($this->children, $c); 

     error_log ("children left: " . count($this->children)); 
    }   
} 

    //Function to remove elements from an array 
private function remove_thread(&$Array, $Element) 
{ 
    for($i = 0; $i < sizeof($Array); $i++) 
    { 
     //Found the element to remove 
     if($Array[$i] == $Element){ 
      unset($Array[$i]); 
      $Array = array_values($Array); 
      break; 
     } 
    } 
} 
+9

Título impresionante ... –

+1

Los niños se vuelven zombis porque no han sido cosechados, no porque todavía estén vivos ... –

+0

@Ignacio - hay casos en los que decir, estoy comprobando un proxy mediante curl. Habrá momentos en los que el niño no responderá porque Curl se ha despegado y, luego, si estoy verificando 1000 proxies, tarde o temprano todos mis hijos estarán zombificados. Así que ese es un caso en el que necesito saber cuánto tiempo han estado corriendo para poder matarlos y crear nuevos hijos. – PaulM

Respuesta

1

En primer lugar: WNOHANG OR WUNTRACED iguales (bool true), WNOHANG | WUNTRACED es int (3), hace que todos mucha diferencia, aunque no necesariamente aquí.

//set maximum child time. 
    $maxruntime = 300; 

    //..... 
    //..... skip a lot of code, prefer trigger_error($msg,E_USER_ERROR) above die($msg) though 
    //..... 

    //if we are the parent 
    if($pid) 
    { 
     //store the time it started 
     $this->children[$pid] = time(); 
    } 

    //..... 
    //..... skip 
    //..... 


    //COLLECT ALL OF THE CHILDREN AS THEY FINISH 
    while(count($this->children) > 0){ 
     //copy array as we will unset $this->children items: 
     $children = $this->children; 
     foreach($children as $pid => $starttime){ 
      $check = pcnt_waitpid($pid, $status, WNOHANG | WUNTRACED); 
      switch($check){ 
       case $pid: 
        //ended successfully 
        unset($this->children[$pid]; 
        break; 
       case 0: 
        //busy, with WNOHANG 
        if(($starttime + $maxruntime) < time() || pcntl_wifstopped($status)){ 
         if(!posix_kill($pid,SIGKILL)){ 
          trigger_error('Failed to kill '.$pid.': '.posix_strerror(posix_get_last_error()), E_USER_WARNING); 
         } 
         unset($this->children[$pid]; 
        } 
        break; 
       case -1: 
       default: 
        trigger_error('Something went terribly wrong with process '.$pid, E_USER_WARNING); 
        // unclear how to proceed: you could try a posix_kill, 
        // simply unsetting it from $this->children[$pid] 
        // or dying here with fatal error. Most likely cause would be 
        // $pid is not a child of this process. 
        break; 

     } 
     // if your processes are likely to take a long time, you might 
     // want to increase the time in sleep 
     sleep(1); 
    } 
+0

Gracias por su esfuerzo, realmente lo aprecio. Voy a echar un vistazo a esto pronto y volveré. – PaulM

+0

Tenga en cuenta que 'unset ($ this-> children [$ pid];' con 'case 0:' debe estar en la instrucción if ... Acaba de editar la respuesta, de lo contrario, podría deshacer la matriz entera indiscriminadamente. – Wrikken

0

Esto es lo que funcionó para mí en deshacerse de los procesos de zombies ... los niños pueden incluso hablar con la entrada estándar, los zombis se pone los muertos durante la terminación (SIGCHLD). Sin esperar nada, totalmente asincrónico.

<?php 

    declare(ticks = 1); // cpu directive 

    $max=10; 
    $child=0; 

    $children = array(); 

    function sig_handler($signo) { // we release zombie souls in here, optimal place - shot exactly after childs death. 
     global $child,$children; 
     switch ($signo) { 
       case SIGCHLD: 
        $child -= 1; 
        foreach($children as $pid){ 
         $res = pcntl_waitpid($pid,$status, WNOHANG | WUNTRACED); 
         if($res != 0) unset($children[$pid]); 
        } 
     } 
    } 

    pcntl_signal(SIGCHLD, "sig_handler"); // register fork signal handler to count running children 

    while (true){ // <main_loop> - could be whatever you want, for, while, foreach... etc. 

      while ($child >= $max) { 
       sleep(1); 
      } 

      $child++; 

      $pid=pcntl_fork(); 

      if($pid == -1){ 

      }else if($pid){ 

       $children[$pid] = $pid; // register new born children 

      }else{ // <fork> 

       echo "HELLO DADDY! I'M ALIVE! I CAN DO WHATEVER YOU WANT, DAD."; 

       sleep(1); // avoid segmentation fault, when fork ends before handling signals 
       exit(0); 

      } // </fork> 

     // optional timer between child spawn, avoiding wakeup on SIGCHLD 
     $timeLeft = 5; // 5 seconds 
     while ($timeLeft > 0) { 
      $timeLeft = sleep($timeLeft); 
     } 

    } // </main_loop> 

    while($child != 0){ 
     sleep(1); 
    } 

    ?> 

temporizador tiene que ser implementado esa manera, porque SIGCHLD despierta cada sueño(). Creen a SztupY para obtener información sobre eso y una idea de cómo evitarlo.

Cuestiones relacionadas