Я использую PCNTL для многопроцессорной обработки большого скрипта на PHP на сервере Ubuntu.
Вот код (упрощенный и прокомментированный)
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
и просто использовать потоки? Это процессы, а не потоки. В любом случае, когда ваш дочерний процесс завершится, вызовитеexit(0);
- это означает успешное завершение программы. Не поднимайте сигналы самостоятельно, если не произойдет ошибка. - person N.B.   schedule 22.05.2015pthreads
, но в любом случае у меня нет выбора, я должен использовать PCNTL (если нет реальной проблемы с PCNTL). Я иду проверять разницу между потоком и процессами, чтобы увидеть, что имеет значение... Я также попытаюсь выйти (0) напрямую и вернуться через несколько минут, спасибо! :) - person Random   schedule 22.05.20150
и8
являются чистыми кодами завершения дочернего процесса. Теперь я хотел бы знать, чего вы пытаетесь достичь с помощью этого скрипта и почему каждый дочерний процесс пишет в отдельный блок общей памяти? - person N.B.   schedule 22.05.2015process
вместоthread
- person Random   schedule 22.05.2015shutdown function
. Таким образом, выход (0) вызвал эту функцию отключения и заставил код выхода быть 3. Спасибо за помощь! - person Random   schedule 25.05.2015