首页 > 解决方案 > 无法在某些子进程成为僵尸之前终止它们

问题描述

环境:Debian Buster,php 7.4

几天前我写了一个新的任务管理器,我遇到了终止子进程的问题。我阅读了以下页面以获取信息:

还有一堆其他的,但这些是最重要的。

所以我的问题是,在我的任务管理器中,我有一个信号接收器功能,理论上应该从内存中清除 PID,但它并不总是发生。它没有发生的方式就像父进程开始忽略这些信号,从那时起,一切都不再重要,每个子进程都将成为僵尸进程。

所以简化的主流程看起来像这样:

    public function childCaretaker(int $signo): void
    {
        $this->info("Received PCNTL signal {$signo}");
        $pid = pcntl_wait($status);
        // actually this part below doesn't matter
        if ($pid !== 0 && posix_kill($pid, 9)) {
            $this->info("Child {$pid} has tragically died!");
            unset($this->processes[$pid]);
        }
    }
    public function actionRunWorker(bool $priorityLevel = false)
    {
        set_time_limit(0);
        pcntl_async_signals(true);

        pcntl_signal(SIGCHLD, [$this, 'childCaretaker']);

        // not important code here
        while (1) {
            // Processes tasks in individual processes if there is any available in the database
            $taskCount = $this->queue->processTasks($this->processes, $priorityLevel);

            // not important code here
        }
    }

然后在实际处理块中,我检查是否有足够的内存来从最大可用减去安全区域创建新进程。如果有,那么我查询最多 100 个任务,然后使用 foreach 创建新进程 - 如果有足够的内存,再次创建新进程 - 并为其创建新的数据库连接并在退出前关闭它。

    public function processTasks(array &$processes, $priorityLevel = false): int
    {
        // not important code

        /** @var BaseTask $task */
        foreach ($executableTasks as $task) {
            // not important code

            $pid = pcntl_fork();
            // not important code

            switch ($pid) {
                case -1:
                    Yii::error('Could not fork!', 'tasks');
                    exit(0);
                case 0:
                    // not important code

                    try {
                        // Process the task
                        $task = $this->process($priorityLevel);
                    } catch (\Exception $exception) {
                        // not important code
                        exit(0);
                    }
                    $executionTime = microtime(true) - $taskExecutionStarted;

                    // not important code

                    exit(0);
                default:
                    $processes[$pid] = true;
                    break;
            }
        }

        // not important code
    }

那么我实际上能做些什么来摆脱僵尸一代呢?在上面的一个链接中,有人解释说它不可能杀死僵尸进程 - 因为它的设计是我在 cli 中手动尝试的,这也有点令人困惑,因为在其他一些来源上,人们提到 SIGKILL 应该总是工作(这不是真的)。

标签: phpmultiprocessing

解决方案


推荐阅读