Дочерний процесс умирает сразу после fork () при выполнении с помощью shell shell (v2.10.15) с php 7 - PullRequest
0 голосов
/ 01 февраля 2019

Когда я запускаю команду оболочки с cake, сценарий завершает дочерние процессы, даже не выполняя сообщение журнала «Запущенный процесс {PID}».Я знаю, что если я извлекаю классы из инфраструктуры CakePHP и выполняю сценарий непосредственно в командной строке как php file.php, дочерние процессы выполняют свои команды и весь сценарий успешно завершается.

ЯМиграция моего бэкэнда из PHP 5.6 в PHP 7.3 и я уже перенесли версию CakePHP на 2.10.15, которая исправила почти все мои проблемы, кроме этой с JobDaemonComponent.Раньше у меня был declare( ticks=1 );, который с PHP 7.1+ больше не действителен, и вам нужно выполнить функцию pcntl_async_signals(true), чтобы она заработала.

JobDaemonComponent.php

class JobDaemonComponent extends Component {
    const MAX_PROCESSES = 25;
    protected $jobsStarted = 0;
    protected $currentJobs = [];
    protected $signalQueue = [];
    protected $parentPID;
    protected $runnable;
    protected $processesNumber = JobDaemonComponent::MAX_PROCESSES;

    public function __construct(\ComponentCollection $collection, $settings = []) {
        parent::__construct($collection, $settings);
        pcntl_async_signals(true);
        $this->parentPID = getmypid();
        pcntl_signal(SIGCHLD, [$this, "childSignalHandler"]);
    }

    public function run() {
        for ($i = 0; $i < $this->processesNumber; $i++) {
            $jobID = rand(0, 10000000000000);
            $this->launchJob($jobID);
        }

        //Wait for child processes to finish before exiting here
        while (($jobCount = count($this->currentJobs))) {
            $this->log("Waiting for current jobs ({$jobCount}) to finish...", LOG_INFO);
            sleep(1);
        }
    }

    /**
     * Launch a job from the job queue
     */
    protected function launchJob($jobID) {
        $pid = pcntl_fork();
        if ($pid == -1) {
            //Problem launching the job
            $this->log('Could not launch new job, exiting');
            return;
        } else if ($pid) {
            $this->log("Gave birth to child process {$pid}.", LOG_DEBUG);
            // Parent process
            // Sometimes you can receive a signal to the childSignalHandler function before this code executes if 
            // the child script executes quickly enough!
            $this->currentJobs[$pid] = $jobID;

            // In the event that a signal for this pid was caught before we get here, it will be in our signalQueue array
            // So let's go ahead and process it now as if we'd just received the signal
            if (isset($this->signalQueue[$pid])) {
                $this->log("Found {$pid} in the signal queue, processing it now", LOG_INFO);
                $sign_info = [
                    "signo" => SIGCHLD,
                    "pid" => $pid,
                    "status" => $this->signalQueue[$pid],
                    "code" => 0
                ];
                $this->childSignalHandler(SIGCHLD, $sign_info);
        unset($this->signalQueue[$pid]);
            }
            return;
        } else {
            $this->log("Launched process {$pid}.", LOG_DEBUG);
            //Forked child, do your deeds.... 
            $pid = getmypid();
            exit($this->runnable->run($pid));
        }
        return;
    }

    public function childSignalHandler($signo, $signinfo = null) {
        $this->log("SignInfo: " . print_r($signinfo, true), LOG_DEBUG);
        //If no pid is provided, that means we're getting the signal from the system.  Let's figure out
        //which child process ended
        $status = 0;
        $exitCode = 0;
        if (!isset($signinfo)) {
            $this->log("Unknown siginfo.", LOG_INFO);
            $pid = pcntl_wait($status, WNOHANG);
            $exitCode = pcntl_wexitstatus($status);
        } else {
            $pid = $signinfo["pid"];
            $status = $signinfo["status"];
            $exitCode = $signinfo["code"];
        }
        //Make sure we get all of the exited children
        while ($pid > 0) {
            if (isset($this->currentJobs[$pid])) {
                $this->log("Process {$pid} wait status {$status} with exit code: {$exitCode}.", LOG_INFO);
                if ($exitCode != 0) {
                    $this->log("{$pid} exited with code {$exitCode}.", LOG_DEBUG);
                }
                unset($this->currentJobs[$pid]);
            } else {
                //Oh no, our job has finished before this parent process could even note that it had been launched!
                //Let's make note of it and handle it when the parent process is ready for it
                $this->log("Adding {$pid} to the signal queue.", LOG_DEBUG);
                $this->signalQueue[$pid] = $status;
            }
            $pid = pcntl_wait($status, WNOHANG);
            if (pcntl_wifexited($status)) {
                $exitCode = pcntl_wexitstatus($status);
            } else {
                $exitCode = -1;
            }
        }
        return true;
    }

    public function setRunnable($runnable) {
        $this->runnable = $runnable;
    }

    public function setProcessesNumber($processesNumber) {
        $this->processesNumber = $processesNumber;
    }
}

RunnableProcess.php

interface RunnableProcess{
    public function run($pid);
}

ForkShell.php

class ForkShell extends Shell implements RunnableProcess{
    function main() {
        $Collection = new ComponentCollection();
        $this->daemon = new JobDaemonComponent($Collection);
        $this->daemon->setRunnable($this);
        $this->daemon->run();
    }

    public function run($pid) {
        $this->processIOSMessagingQueue($pid);
        $this->log("Process {$pid} done!", LOG_INFO);
    }
}

Ожидается: дочерние процессы должны успешно завершиться и вывести «Process {pid} done!».Фактически: процессы даже не выводят «Launched process {pid}.», А код состояния - 11, что, похоже, дочерний процесс был уничтожен

...