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()
函数,我可以看到它在分叉期间没有被调用。
两个流程都没有共享变量/对象的任何原因,或者对主题人员有任何好的阅读?
答案 0 :(得分:2)
对象的引用在分叉进程中是相同,因为子进程的内存空间中对象的内存位置是相同的。
哈希计算为对象地址XOR随机掩码(只生成一次),正如您可以在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 ,这对于父级和子级当然是不同的。
所以,不同的随机数生成器种子,不同的随机掩码,不同的散列。
答案 1 :(得分:1)
创建对象时不会计算对象哈希(正如人们所想的那样)。当第一次为对象调用spl_object_hash()
时,将计算对象哈希。这是在你的例子中的fork之后。
进一步注意,对于散列的计算,使用了一些随机性,因此使用了不同的散列。