2012-02-06 31 views
5

Tôi đã có khối mã này hoạt động hoàn hảo cho nhu cầu của tôi trong các chương trình cli php khác nhau của tôi. Ngoại trừ rằng đôi khi một đứa trẻ sẽ trở thành một zombie.PHP Forking: Giết con khi nó trở thành một zombie

Câu hỏi của tôi là nơi đặt mã để kiểm tra xem một đứa trẻ có chạy trong vòng 5 phút và nếu nó còn lâu hơn để giết nó?

Tôi biết về posix_kill để giết nó và cách theo dõi nó. There are examples of taskmanagers here.

Tôi không chắc chắn cách kết hợp các tính năng mới này vào mã. Mỗi khi tôi cố gắng, tôi chỉ nhận được vào một mớ hỗn độn. Có lẽ ai đó biết về forking có thể sửa mã của tôi không?

Bỏ qua tất cả các error_logs - Tôi thích xem điều gì đang xảy ra khi nó chạy.

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

Tiêu đề tuyệt vời ... –

+1

Trẻ em trở thành thây ma vì chúng không bị gặt hái, không phải vì chúng vẫn còn sống ... –

+0

@Ignacio - có trường hợp khi nói, tôi đang kiểm tra proxy qua curl. Sẽ có những lúc đứa trẻ đó trở nên không hồi đáp vì Curl đã trở nên bất tỉnh và sau đó nếu tôi kiểm tra 1000 proxy sớm hay muộn thì tất cả các con tôi đều bị kìm hãm. Vì vậy, đó là một ví dụ mà tôi cần phải biết bao lâu họ đã được chạy vì vậy tôi có thể giết họ và làm cho trẻ em mới. – PaulM

Trả lời

1

Trước hết: WNOHANG OR WUNTRACED equals (bool true), WNOHANG | WUNTRACED là int (3), làm cho tất cả rất nhiều sự khác biệt, mặc dù không nhất thiết phải ở đây.

//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

Cảm ơn bạn đã nỗ lực, tôi thực sự đánh giá cao nó. Tôi sẽ xem xét điều này sớm và sẽ quay lại. – PaulM

+0

Lưu ý 'unset ($ this-> children [$ pid];' with 'case 0:' nên ở trong câu lệnh if ... Chỉ cần chỉnh sửa câu trả lời, nếu không bạn sẽ hủy bỏ toàn bộ mảng bừa bãi. – Wrikken

0

Đây là những gì đã giúp tôi thoát khỏi quá trình zombie ... trẻ em thậm chí có thể nói chuyện với stdin, thây ma của họ sẽ bị giết khi chấm dứt (SIGCHLD). Không chờ đợi bất cứ điều gì, hoàn toàn không đồng bộ.

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

    ?> 

Bộ hẹn giờ phải được thực hiện theo cách đó, vì SIGCHLD sẽ thức giấc mỗi lần(). Creds để SztupY cho thông tin về điều đó và một ý tưởng làm thế nào để tránh nó.

Các vấn đề liên quan