从wexitstatus收到错误的退出代码

时间:2015-05-22 14:43:16

标签: php multiprocessing signals pcntl wexitstatus

我正在使用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 也许我误解了信号是如何工作的......任何线索?

1 个答案:

答案 0 :(得分:1)

我发现了问题 在我的应用程序中,register_shutdown_function(myFrameworkShutdownFunction)用于平稳关闭脚本&#34; 因此exit(0)没有立即停止子进程。它首先进入myFrameworkShutdownFunction,并将返回码0转换为代码3(因为配置错误)。