Lix*_*Lix 17 php sockets child-process
放弃
我很清楚在这种情况下,对于套接字服务器来说,PHP可能不是最佳选择.请不要建议使用不同的语言/平台 - 相信我 - 我从各方面都听过.
在Unix环境中工作并使用PHP 5.2.17,我的情况如下 - 我在PHP中构建了一个与Flash客户端通信的套接字服务器.我的第一个问题是每个传入的连接都阻塞了顺序连接,直到它完成处理.我用PHP 来解决这个问题pcntl_fork().我成功地能够产生许多子进程(在父节点中保存它们),这些进程负责向其他客户端广播消息,因此"释放"父进程并允许它继续处理下一个连接[s].
我现在的主要问题是处理/处理这些死/僵尸子进程的集合并终止它们.我已经阅读(一遍又一遍)pcntl_fork()的相关PHP手册页,并意识到父进程负责清理其子进程.当子进程执行时,父进程从其子进程接收SIGNAL exit(0).我能够使用pcntl_signal()函数"捕获"该信号来设置信号处理程序.
我的signal_handler看起来像这样:
declare(ticks = 1);
function sig_handler($signo){
global $forks; // this is an array that holds all the child PID's
foreach($forks AS $key=>$childPid){
echo "has my child {$childPid} gone away?".PHP_EOL;
if (posix_kill($childPid, 9)){
echo "Child {$childPid} has tragically died!".PHP_EOL;
unset($forks[$key]);
}
}
}
Run Code Online (Sandbox Code Playgroud)
我确实看到两个echo都包括需要删除的相关且正确的子PID,但似乎是这样
posix_kill($childPid, 9)
Run Code Online (Sandbox Code Playgroud)
我理解为同义的kill -9 $childPid是返回TRUE,虽然它实际上并没有删除过程......
摘自以下手册posix_kill:
成功时返回TRUE,失败时返回FALSE.
我正在使用该ps命令监视子进程.它们在系统上显示如下:
web5 5296 5234 0 14:51 ? 00:00:00 [php] <defunct>
web5 5321 5234 0 14:51 ? 00:00:00 [php] <defunct>
web5 5466 5234 0 14:52 ? 00:00:00 [php] <defunct>
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,所有这些进程都是具有PID的父进程的子进程 5234
我理解中遗漏了什么吗?我似乎已经设法让一切工作(并且确实如此)但我在系统上留下了无数的僵尸进程!
我对僵尸大灾难的计划是坚如磐石的 -
但即使sudo kill -9不杀死僵尸儿童过程,我还能做些什么呢?
经过一些额外的研究后,我自己已经回答了这个问题,如果你仍然可以随心所欲地继续我的随意.
Lix*_*Lix 20
我承诺,是在最后的解决方案:P
好吧......所以我们在这里,10天后,我相信我已经解决了这个问题.我不想添加已经很长的帖子,所以我会在这个答案中包含一些我尝试过的东西.
根据@ sym的建议,并阅读更多文档和文档的评论,pcntl_waitpid()描述指出:
如果pid请求的子节点在调用时已经退出(所谓的
"僵尸"进程),则该函数立即返回.孩子使用的任何系统资源
都被释放......
所以我设置我的pcntl_signal()处理程序 -
function sig_handler($signo){
global $childProcesses;
$pid = pcntl_waitpid(-1, $status, WNOHANG);
echo "Sound the alarm! ";
if ($pid != 0){
if (posix_kill($pid, 9)){
echo "Child {$pid} has tragically died!".PHP_EOL;
unset($childProcesses[$pid]);
}
}
}
// These define the signal handling
// pcntl_signal(SIGTERM, "sig_handler");
// pcntl_signal(SIGHUP, "sig_handler");
// pcntl_signal(SIGINT, "sig_handler");
pcntl_signal(SIGCHLD, "sig_handler");
Run Code Online (Sandbox Code Playgroud)
为了完成,我将包括我用于分支子进程的实际代码 -
function broadcastData($socketArray, $data){
global $db,$childProcesses;
$pid = pcntl_fork();
if($pid == -1) {
// Something went wrong (handle errors here)
// Log error, email the admin, pull emergency stop, etc...
echo "Could not fork()!!";
} elseif($pid == 0) {
// This part is only executed in the child
foreach($socketArray AS $socket) {
// There's more happening here but the essence is this
socket_write($socket,$msg,strlen($msg));
// TODO : Consider additional forking here for each client.
}
// This is where the signal is fired
exit(0);
}
// If the child process did not exit above, then this code would be
// executed by both parent and child. In my case, the child will
// never reach these commands.
$childProcesses[] = $pid;
// The child process is now occupying the same database
// connection as its parent (in my case mysql). We have to
// reinitialize the parent's DB connection in order to continue using it.
$db = dbEngine::factory(_dbEngine);
}
Run Code Online (Sandbox Code Playgroud)
是的...这是对代码的1:1评论比例:P
所以这看起来很棒,我看到了回声:
发出警报!孩子12345悲惨地死了!
但是,当套接字服务器循环执行下一次迭代时,该socket_select()函数无法抛出此错误:
PHP警告:socket_select():无法选择[4]:系统调用中断...
服务器现在将进入植物人状态,完全忘记了他周围的世界,除了响应来自根终端的手动终止命令之外的任何请求.
我不打算讨论为什么会发生这种情况或者我之后做了什么来调试它...让我们说这是令人沮丧的一周...
很多咖啡,眼睛酸痛,10天后......
请滚筒
提到这里的评论,从2007年的PHP插座文档和本教程stuporglue(搜索"为人父母"),可以简单地"忽略"的信号从子进程(正在添加SIGCHLD通过传递)SIG_IGN的pcntl_signal()功能-
pcntl_signal(SIGCHLD, SIG_IGN);
Run Code Online (Sandbox Code Playgroud)
引用该链接的博客帖子:
如果我们忽略SIGCHLD,子进程将在完成后自动获得.
信不信由你 - 我包括那条pcntl_signal()线,删除了所有其他处理程序和处理孩子的事情,它工作了!没有更多的<defunct>流程留下来了!
在我的情况下,我真的不感兴趣的是确切地知道一个子进程何时死亡,或者它是谁,我对它们根本不感兴趣 - 只是他们没有徘徊并崩溃我的整个服务器:P