PHP 分叉和多个子信号
我正在尝试编写一个脚本,使用 < 创建多个分叉子进程代码>pcntl_*函数。
基本上,有一个脚本循环运行大约一分钟,定期轮询数据库以查看是否有要运行的任务。如果有的话,它应该在一个单独的进程中分叉并运行该任务,以便父进程不会被长时间运行的任务所阻碍。
由于可能有大量任务可供运行,因此我想限制创建的子进程的数量。因此,我通过每次创建一个变量时递增一个变量(如果太多则暂停)来跟踪进程的数量,然后在信号处理程序中递减它。有点像这样:
define(ticks = 1);
$openProcesses = 0; // how many we have open
$max = 3; // the most we want open at a time
pcntl_signal(SIGCHLD, "childFinished");
while (!time_is_up()) {
if (there_is_something_to_do()) {
$pid = pcntl_fork();
if (!$pid) { // I am the child
foo(); // run the long-running task
exit(0); // and exit
} else { // I am the parent
++$openProcesses;
if ($openProcesses >= $max) {
pcntl_wait($status); // wait for any child to exit
} // before continuing
}
} else {
sleep(3);
}
}
function childFinished($signo) {
global $openProcesses;
--$openProcesses;
}
在大多数情况下,这工作得很好,除了两个或多个进程同时完成时 - 信号处理函数仅被调用一次,这会抛出我的计数器。其原因由 PHP 手册的 注释中的“匿名”解释:
多个子进程返回的数量小于给定时刻退出的子进程的数量。对于 Unix (POSIX) 系统,SIGCHLD 信号是正常行为。 SIGCHLD 可能被解读为“一个或多个孩子改变了状态——去检查你的孩子并收获他们的状态值”。
我的问题是:如何检查子进程并获取他们的状态?有没有可靠的方法来检查在任何给定时间有多少子进程打开?
使用 PHP 5.2.9
I'm trying to write a script which creates a number of forked child processes using the pcntl_*
functions.
Basically, there is a single script which runs in a loop for about a minute, periodically polling a database to see if there is a task to be run. If there is one, it should fork and run the task in a separate process so that the parent isn't held up by a long-running task.
Since there possibly could be a large number of tasks ready to be run, I want to limit the number of child processes that are created. Therefore, I am keeping track of the number of processes by incrementing a variable each time one is created (and then pausing if there's too many), and then decrementing it in a signal handler. Kind of like this:
define(ticks = 1);
$openProcesses = 0; // how many we have open
$max = 3; // the most we want open at a time
pcntl_signal(SIGCHLD, "childFinished");
while (!time_is_up()) {
if (there_is_something_to_do()) {
$pid = pcntl_fork();
if (!$pid) { // I am the child
foo(); // run the long-running task
exit(0); // and exit
} else { // I am the parent
++$openProcesses;
if ($openProcesses >= $max) {
pcntl_wait($status); // wait for any child to exit
} // before continuing
}
} else {
sleep(3);
}
}
function childFinished($signo) {
global $openProcesses;
--$openProcesses;
}
This works pretty much ok most of the time, except for when two or more processes finish simultaneously - the signal handler function is only called once, which throws out my counter. The reason for this is explained by "Anonymous" in the notes of the PHP manual:
Multiple children return less than the number of children exiting at a given moment SIGCHLD signals is normal behavior for Unix (POSIX) systems. SIGCHLD might be read as "one or more children changed status -- go examine your children and harvest their status values".
My question is this: How do I examine the children and harvest their status? Is there any reliable way to check how many child processes are open at any given time?
Using PHP 5.2.9
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
一种方法是保留子进程的 PID 数组,并在信号处理程序中检查每个 PID 以查看它是否仍在运行。 (未经测试的)代码如下所示:
One way is to keep an array of the PIDs of the child processes, and in the signal handler check each PID to see if it's still running. The (untested) code would look like:
您可以让子级在启动时向父级发送 SIGUSR1,然后在退出前向父级发送 SIGUSR2。使用原始信号时要处理的另一件事是内核合并它们,而 RT 信号则不会这样做。理论上,任何非 rt 信号都可以合并。
您可以使用 sqlite 实现某种简单的锁定,其中一次只有一个孩子可以拥有说话棒。只要确保孩子们正常处理致命信号,以便他们保持活动状态以释放锁即可。
You could have children send a SIGUSR1 to the parent when they start,then a SIGUSR2 before they exit. The other thing you are dealing with when using primitive signals is the kernel merging them, which it does not do with RT signals. In theory, ANY non-rt signal could be merged.
You might implement some kind of simple locking using sqlite, where only one child at a time can have the talking stick. Just make sure that children handle normally fatal signals so that they remain alive to free the lock.
我知道这已经晚了大约 8 年(我希望你找到答案),但为了防止它对其他人有帮助,我会回答。
pcntl_w* 函数的使用将成为您的朋友,您可能想要实现一个进程收割机。该文档不是很有帮助,并且仍然不包含任何有用的示例。
这将是一个由多个部分组成的过程:
1 - 使用 pcntl_signal 将捕获的信号发送到您的信号处理程序
2 - 在该循环内进行循环/轮询;
3 - 迭代您的子数组(您将在下面创建)并根据需要收割它们
4 - fork():这将包括以下内容:
在收割机中,您将需要以下内容:
上面的收割机代码将允许您可以轮询您孩子的状态,如果您使用的是 php7+,则在信号处理程序中填写的 $signalInfo 数组将包含许多您可以使用的有用信息.. var_dump 它.. 检查一下。另外,在 php7+ 中使用 pcntl_async_signals(true) 取代了声明(ticks=1) 和手动调用 pcntl_signal_dispatch() 的需要;
我希望这有帮助。
I know this is about 8 years too late (and I hope you found an answer), but just in case it helps someone else I am going to answer.
The use of the pcntl_w* functions will be your friend here and you will probably want to implement a process reaper. The documentation is not very helpful and still does not contain any useful examples.
This would be a multi-part process:
1 - use pcntl_signal send trapped signals to your signal handler
2 - Do your looping/polling and within that loop;
3 - Iterate through the array of your children (which you will create below) and reap them as necessary
4 - fork(): This will consist of the following:
In the reaper, you will need the following:
The above reaper code will allow you to poll the status of your children and if you are using php7+, the $signalInfo array which is filled in at your signal handler will contain a lot of useful information you can use.. var_dump it.. check it out. Also, using pcntl_async_signals(true) in php7+ replaces the need for declare(ticks=1) and manually calling pcntl_signal_dispatch();
I hope this helps.