所以,我有这个监听IPC消息的PHP守护进程工作者。 奇怪的是父进程(来自pcntl_fork的结果)留下了[php]<被禁>进程,直到子进程完成,但仅当脚本从cronjob启动时,而不是直接从命令行启动。
我知道<被禁>进程并不邪恶,但我无法弄清楚为什么只有在从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 :(得分: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;
}