pcntl_fork然后是pcntl_exec或pcntl_exec,然后是pcntl_fork

时间:2010-10-22 05:08:44

标签: php fork

说明: 我正在创建的应用程序的一部分需要检查数千条记录并以及时方式对其进行操作。因此,对于每条记录,我都希望分叉一个新进程。但是,我需要一个数据库连接来更多地检查该记录。据我了解,孩子继承了DB连接。因此后续的分叉有DB错误。

我以为我可以用pcntl_exec('php /path/script.php');然后是pcntl_fork,这样就不会阻止调用进程。

或者我可以在子进程中使用pcntl_fork然后再使用pcntl_exec。或者也许我应该使用exec()而不是pcntl_exec()。

我的问题:任何一个订单都有任何缺点或优势吗?

备注: 也许我正在想象这个问题,因为我认为调用php进程会等待pcntl_exec返回。但这不是文档所说的:

  

出错时返回FALSE,成功后不返回。

函数如何有时返回一个值而没有其他时间?这听起来像是写得不好的文档。

fahadsadah的评论说:

  

执行过程结束后,控制权将返回到网络服务器进程。

如果是这种情况,那么我需要分叉。

编辑:困惑的代码 - 包括我;)

<?php

class Process
{

    public function __construct($arg = false)
    {
        if ($arg == "child")
        {
            $this->act();
        }
        else
        {
            $this->run();
        }
    }

    public function run()
    {
        echo "parent before fork:", getmypid(), PHP_EOL;
        $pid = @ pcntl_fork();
        echo $pid, PHP_EOL;

        if ($pid == -1)
        {
            throw new Exception(self::COULD_NOT_FORK);
        }
        if ($pid)
        {
        // parent
            echo "parent after fork:", getmypid(), PHP_EOL;
        }
        elseif ($pid == 0)
        {
        // child
            echo "child after fork:", getmypid(), PHP_EOL;
            //echo exec('php Process.php child');
            echo pcntl_exec('/usr/bin/php', array('Process.php', 'child'));
        }
        return 0;
    }

    private function act()
    {
        sleep(1);
        echo "forked child new process:", getmypid(), PHP_EOL;
        return 0;
    }
}

$proc = new Process($argv[1]);

如果取消注释exec并注释pcntl_exec,您将看到pcntl_exec替换了该进程。我猜的是节省了一些资源。

3 个答案:

答案 0 :(得分:1)

这真的很困惑 - 你正试图应用非常复杂的技术 - 但你是以完全错误的方式应用它们。

fork创建当前进程的新运行副本。 Exec启动一个新进程。你不会同时使用它们来启动一个过程。

但在我开始正确解释如何正确使用fork和exec之前,我应该指出它们不是解决这个问题的正确工具。

应尽可能避免批处理。数据通常以有限的速率到达(尽管速率可能是随机的) - 通常避免批处理的正确方法是同步或通过排队处理请求。在批处理不可避免的情况下,并行化和/或流水线处理通常可以提高吞吐量。虽然有许多复杂的方法可以实现这一点(例如map-reduce),但简单地分割数据通常就足够了。虽然你的基本想法相当于分成单个部分,但是:

1)效率低于处理小批量

2)很难限制系统的资源消耗(如果你产生了500个进程并且你的DBMS只支持200个并发连接怎么办?)

假设您无法同步处理处理并且运行具有多个订户的队列是不切实际的,我建议将数据拆分为(有限数量的)较小批次并生成进程以处理这些数据。请注意,popen(),proc_open()和pcntl_fork()不会在生成进程的执行期间阻塞。 (提示 - 使用modulus operator

如果您想从HTTP请求启动处理(或者有其他原因在单独的会话组中运行它们),那么请使用谷歌“PHP长时间运行的进程setsid”。

答案 1 :(得分:0)

这没有意义。一旦你执行了exec(),你就会运行不同的代码,因此之后不能 fork()。 成功后不会返回。

答案 2 :(得分:0)

现在已经定义了fork和exec,fork将无法执行您想要的操作。它会将所有当前脚本环境复制到新空间,包括文件描述符指针。是的,这是正确的 - 孩子们与父母共享相同的文件描述符。

想象一下使用mysql扩展时的破坏,它维护着自己的文件描述符; - )