2

だから、IPC メッセージをリッスンするこの PHP デーモン ワーカーがあります。奇妙なことに、親プロセス (pcntl_fork からの結果) は、子プロセスが完了するまで [php] < defunct> プロセスを残しますが、スクリプトがコマンドラインから直接ではなく、cronjob から開始された場合のみです。

< defunct> プロセスが悪意を持っていないことは知っていますが、cron ジョブから実行しているときにのみ発生する理由がわかりません。

指示

/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補助 | 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 から実行するとこのバグが発生するのでしょうか?

4

1 に答える 1

4

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;
}
于 2013-05-01T22:23:41.640 に答える