5

pcntl_fork()的手册说:

pcntl_fork() 函数创建一个子进程,该子进程与父进程的区别仅在于它的 PID 和 PPID。

然而,运行这个简单的测试让我感到惊讶:

class Foo
{
    public function bar()
    {
        if (pcntl_fork()) {
            echo spl_object_hash($this), PHP_EOL;
        } else {
            echo spl_object_hash($this), PHP_EOL;
        }
    }
}

(new Foo)->bar();

结果如下所示:

000000005ec7fd31000000003f0fcfe6
000000006b4cd5fc000000007fee8ab7

从文档的内容来看,我希望父母和孩子共享相同的变量,特别是当从一个对象中进行 fork() 时,我希望对对象的引用在两者中都是相同的过程。但上面的例子表明他们不是。

有趣的是,这里没有发生克隆,看起来对象只是被复制了。如果我添加一个__clone()函数,我可以看到它在分叉期间没有被调用。

两个进程不共享变量/对象的任何原因,或者对主题人员的任何良好阅读?

4

2 回答 2

2

分叉进程中对对象的引用一样的,因为对象在子进程内存空间中的内存位置是一样的。

哈希计算为对象地址异或随机掩码(仅生成一次),您可以在 PHP 源代码中阅读ext/spl/php_spl.c

PHPAPI void php_spl_object_hash(zval *obj, char *result TSRMLS_DC) /* {{{*/
{
    intptr_t hash_handle, hash_handlers;
    char *hex;

    if (!SPL_G(hash_mask_init)) {
        if (!BG(mt_rand_is_seeded)) {
            php_mt_srand(GENERATE_SEED() TSRMLS_CC);
        }    

        SPL_G(hash_mask_handle)   = (intptr_t)(php_mt_rand(TSRMLS_C) >> 1);
        SPL_G(hash_mask_handlers) = (intptr_t)(php_mt_rand(TSRMLS_C) >> 1);
        SPL_G(hash_mask_init) = 1; 
    }    

    hash_handle   = SPL_G(hash_mask_handle)^(intptr_t)Z_OBJ_HANDLE_P(obj);
    hash_handlers = SPL_G(hash_mask_handlers)^(intptr_t)Z_OBJ_HT_P(obj);

    spprintf(&hex, 32, "%016x%016x", hash_handle, hash_handlers);

    strlcpy(result, hex, 33); 
    efree(hex);
}
/* }}} */

如果在调用函数之前随机数生成器被播种,您将获得子进程和父进程完全相同的输出。但在这种情况下它不是,每个进程计算它自己的种子。的代码GENERATE_SEED去:

#ifdef PHP_WIN32
#define GENERATE_SEED() (((long) (time(0) * GetCurrentProcessId())) ^ ((long) (1000000.0 * php_combined_lcg(TSRMLS_C))))
#else
#define GENERATE_SEED() (((long) (time(0) * getpid())) ^ ((long) (1000000.0 * php_combined_lcg(TSRMLS_C))))
#endif

如您所见,种子取决于进程 ID,这对于父进程和子进程来说当然是不同的。

所以,不同的随机数生成器种子,不同的随机掩码,不同的哈希。

于 2013-04-02T18:03:49.733 回答
1

创建对象时不会计算对象哈希(正如人们所想的那样)。对象哈希值将在spl_object_hash()对象第一次被调用时计算。在您的示例中,这是在 fork 之后。

进一步注意,对于散列的计算,使用了一些随机性,因此使用了不同的散列。

于 2013-04-02T17:37:29.033 回答