【发布时间】:2015-05-22 14:43:16
【问题描述】:
我正在使用 PCNTL 在 ubuntu 服务器上多处理 PHP 中的大脚本。
这是代码(简化和注释)
function signalHandler($signo = null) {
$pid = posix_getpid();
switch ($signo) {
case SIGTERM:
case SIGINT:
case SIGKILL:
// a process is asked to stop (from user or father)
exit(3);
break;
case SIGCHLD:
case SIGHUP:
// ignore signals
break;
case 10: // signal user 1
// a process finished its work
exit(0);
break;
case 12: // signal user 2
// a process got an error.
exit(3);
break;
default:
// nothing
}
}
public static function run($nbProcess, $nbTasks, $taskFunc, $args) {
$pid = 0;
// there will be $nbTasks tasks to do, and no more than $nbProcess children must work at the same time
$MAX_PROCESS = $nbProcess;
$pidFather = posix_getpid();
$data = array();
pcntl_signal(SIGTERM, "signalHandler");
pcntl_signal(SIGINT, "signalHandler");
// pcntl_signal(SIGKILL, "signalHandler"); // SIGKILL can't be overloaded
pcntl_signal(SIGCHLD, "signalHandler");
pcntl_signal(SIGHUP, "signalHandler");
pcntl_signal(10, "signalHandler"); // user signal 1
pcntl_signal(12, "signalHandler"); // user signal 2
for ($indexTask = 0; $indexTask < $nbTasks ; $indexTask++) {
$pid = pcntl_fork();
// Father and new child both read code from here
if ($pid == -1) {
// log error
return false;
} elseif ($pid > 0) {
// We are in father process
// storing child id in an array
$arrayPid[$pid] = $indexTask;
} else {
// We are in child, nothing to do now
}
if ($pid == 0) {
// We are in child process
$pidChild = posix_getpid();
try {
//$taskFunc is an array containing an object, and the method to call from that object
$ret = (array) call_user_func($taskFunc, $indexTask, $args);// similar to $ret = (array) $taskFunc($indexTask, $args);
$returnArray = array(
"tasknb" => $indexTask,
"time" => $timer,
"data" => $ret,
);
} catch(Exception $e) {
// some stuff to exit child
}
$pdata = array();
array_push($pdata, $returnArray);
$data_str = serialize($pdata);
$shm_id = shmop_open($pidChild, "c", 0644, strlen($data_str));
if (!$shm_id) {
// log error
} else {
if(shmop_write($shm_id, $data_str, 0) != strlen($data_str)) {
// log error
}
}
// We are in a child and job is done. Let's exit !
posix_kill($pidChild, 10); // sending user signal 1 (OK)
pcntl_signal_dispatch();
} else {
// we are in father process,
// we check number of running children
while (count($arrayPid) >= $MAX_PROCESS) {
// There are more children than allowed
// waiting for any child to send signal
$pid = pcntl_wait($status);
// A child sent a signal !
if ($pid == -1) {
// log error
}
if (pcntl_wifexited($status)) {
$statusChild = pcntl_wexitstatus($status);
} else
$statusChild = $status;
// father ($pidFather) saw a child ($pid) exiting with status $statusChild (or $status ?)
// ^^^^ ^^^^^^
// (=3) (= random number ?)
if(isset($arrayPid[$pid])) {
// father knows this child
unset($arrayPid[$pid]);
if ($statusChild == 0 || $statusChild == 10 || $statusChild == 255) {
// Child did not report any error
$shm_id = shmop_open($pid, "a", 0, 0);
if ($shm_id === false)
// log error
else {
$shm_data = unserialize(shmop_read($shm_id, 0, shmop_size($shm_id)));
shmop_delete($shm_id);
shmop_close($shm_id);
$data = array_merge($data, $shm_data);
}
// kill (again) child
posix_kill($pid, 10);
pcntl_signal_dispatch();;
}
else {
// Child reported an error
}
}
}
}
}
}
我面临的问题是关于 wexitstatus 返回的值。
为了简单起见,有一个父进程,它必须创建 200 个线程。
他一次创建一个进程,如果实际运行的线程超过 8 个,则等待一个进程完成。
我添加了许多日志,所以我看到一个孩子完成了它的工作。
我看到它打电话给posix_kill($pidChild, 10);。
我看到信号处理程序是用signal user 1 调用的(导致exit(0))。
我看到父亲醒了,但是当他从wexitstatus 获取返回的代码时,他看到了代码3,因此认为孩子出错了,而它已经以代码0 退出了!!。
pid 是好孩子的 pid。
也许我误解了信号的工作原理......有什么线索吗?
【问题讨论】:
-
为什么不安装
pthreads扩展而只使用线程?这些是进程,而不是线程。无论如何,当您的孩子 process 完成时,调用exit(0);- 这表明程序成功终止。除非发生错误,否则不要自行发出信号。 -
@N.B.从未听说过
pthreads,但无论如何我别无选择,我必须使用 PCNTL(除非 PCNTL 存在真正的问题)。我去检查线程和进程之间的区别,看看有什么相关的......我也会尝试直接退出(0)并在几分钟后回来,谢谢! :) -
Here's the GitHUB repo for the pthreads extension。关于您的问题:父进程捕获子进程发出的所有信号。当孩子完成工作时,它应该以状态 0 退出,表示成功。所有其他退出代码都表示某种错误。如果我没记错的话,状态代码
0和8是干净的子进程退出代码。现在,我想知道的是你想用这个脚本实现什么,为什么每个子进程都写入一个单独的共享内存块? -
@N.B.每个线程都在编写和通过电子邮件发送 PDF。我们目前正在对它进行单处理,但是在发送电子邮件时,该过程只是等待,所以这是浪费时间。我们现在使用 8 个进程(在四核 proc 上)来解决这个问题。共享内存用于获取有关子进程中发生的小错误的数据(我们在所有子进程完成工作时打印)。我没有编写此代码,但我被要求使用此代码并使其适用于此任务。所以我记录了很多关于 PCNTL 的内容(我想我很理解),但没有看 shmop。有什么问题吗?
-
我只是在问你在做什么——所以,你有一些长时间运行的任务,你正在尝试异步执行它,这很好。但是,我询问了共享内存,因为它应该是共享的,但每个进程都获得自己的。这种方法没有错,只是有些事情不太清楚。您是否考虑过使用消息队列和任务分配模式?你有一个带有子工作进程的主管进程。使用 ZeroMQ 和扇出方法比您拥有的代码要少得多,如果您当然能够更改它
标签: php multiprocessing signals pcntl wexitstatus