2013-04-26 69 views
2

所以,我有这个PHP后台工作人员监听到IPC消息。 奇怪的是,父进程(从pcntl_fork结果)离开[PHP] <解散>进程,直到子进程完成,但只有当脚本启动命令行形成一个cronjob,不直接。pcntl_fork()导致倒闭的父进程

我知道<解散>过程不是邪恶的,而是从一个cronjob运行时,我想不通为什么它只是发生。

命令

/path/to/php/binary/php /path/to/php/file/IpcServer.php 

分岔代码:

$iParent = posix_getpid(); 
$iChild = pcntl_fork(); 

if ($iChild == -1) 
    throw new Exception("Unable to fork into child process."); 

elseif ($iChild) 
{ 
    echo "Forking into background [{$iChild}].\n"; 

    Log::d('Killing parent process (' . $iParent. ').'); 
    exit; 
} 

输出

Forking into background [20835]. 
Killing parent process (20834). 

ps aux | grep的PHP

root 20834 0.0 0.0 0 0 ? Zs 14:28 0:00 [php] <defunct> 
root 20835 0.0 0.2 275620 8064 ? Ss 15:35 0:00 /path/to/php/binary/php /path/to/php/file/IpcServer.php 

,我发现这是一个众所周知的Apache错误,但随后从运行的cronjob时,为什么会出现这样的错误?

+0

我有相同的问题,但是我跑我的PHP守护进程通过[Upstart](http://upstart.ubuntu.com/cookbook/)。我的申请分叉一次。通过Upstart启动父进程成为僵尸。通过CLI启动 - 无父进程僵尸。 – user12345 2014-07-26 17:55:42

回答

2

在PHP中,孩子将成为一个僵尸进程,除非父母等待它返回pcntl_wait()pcntl_waitpid()。所有进程结束或处理后,僵尸将被销毁。如果孩子没有得到处理,并且孩子比父母长,那么父母看起来也会变成僵尸。

来自实例pcntl_fork页:

$pid = pcntl_fork(); 
if ($pid == -1) { 
    die('could not fork'); 
} else if ($pid) { 
    // we are the parent 
    pcntl_wait($status); //Protect against Zombie children 
} else { 
    // we are the child 
} 

或者使用信号处理,像这样以防止等待主线程:

$processes = array(); // List of running processes 
$signal_queue = array(); // List of signals for main thread read 
// register signal handler 
pcntl_signal(SIGCHLD, 'childSignalHandler'); 

// fork. Can loop around this for lots of children too. 
switch ($pid = pcntl_fork()) { 
    case -1: // FAILED 
     break; 
    case 0: // CHILD 
     break; 
    default: // PARENT 
     // ID the process. Auto Increment or whatever unique thing you want 
     $processes[$pid] = $someID; 
     if(isset($signal_queue[$pid])){ 
      childSignalHandler(SIGCHLD, $pid, $signal_queue[$pid]); 
      unset($signal_queue[$pid]); 
     } 
     break; 
} 

function childSignalHandler($signo, $pid=null, $status=null){ 
    global $processes, $signal_queue; 
    // If no pid is provided, Let's wait to figure out which child process ended 
    if(!$pid){ 
     $pid = pcntl_waitpid(-1, $status, WNOHANG); 
    } 

    // Get all exited children 
    while($pid > 0){ 
     if($pid && isset($processes[$pid])){ 
      // I don't care about exit status right now. 
      // $exitCode = pcntl_wexitstatus($status); 
      // if($exitCode != 0){ 
      //  echo "$pid exited with status ".$exitCode."\n"; 
      // } 
      // Process is finished, so remove it from the list. 
      unset($processes[$pid]); 
     } 
     else if($pid){ 
      // Job finished before the parent process could record it as launched. 
      // Store it to handle when the parent process is ready 
      $signal_queue[$pid] = $status; 
     } 
     $pid = pcntl_waitpid(-1, $status, WNOHANG); 
    } 
    return true; 
} 
+0

我有一个僵尸父母,而不是孩子。但是你是否说我的父母程序应该经常等待孩子完成?对于我来说,当主要目标是进行守护进程时没有任何意义。 – Milananas 2013-05-02 09:31:32

+0

啊,我误解了你的问题。我相信你也可以用pcntl_signal来防止僵尸小孩,但我并不擅长PHP进程。我怀疑所有PHP进程成为僵尸(父母或子女),如果他们结束,而其他进程仍在运行不处理。 – 2013-05-02 17:00:20

+0

我的孩子和父母进程都有适当的信号处理。我现在正在调查是否也许是PHP不是垃圾清理父进程真的很好,因为孩子使用一些资源从父进程。 – Milananas 2013-05-03 06:48:56