Почему pcntl fork() копирует объекты PHP?
В руководстве для 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()
, я увижу, что она не вызывается во время разветвления.
Какая-либо причина, по которой переменные/объекты не являются общими для обоих процессов, или какое-либо хорошее чтение по этой теме, ребята?
2 answers
Хэш объекта не будет вычисляться при создании объекта (как можно было бы подумать). Хэш объекта будет вычислен при первом вызове spl_object_hash()
для объекта. Это после развилки в вашем примере.
Далее обратите внимание, что для вычисления хэша используется некоторая случайность, поэтому используются разные хэши.
Ссылка на объект является одинаковой в разветвленном процессе, поскольку расположение объекта в памяти дочернего процесса одинаково.
Хэш вычисляется как адрес объекта ИЛИ случайная маска (которая генерируется только один раз), как вы можете прочитать в исходном коде 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
Как вы можете видеть, начальное значение зависит от идентификатора процесса , который, конечно, отличается для родителя и ребенка.
Итак, другое начальное значение генератора случайных чисел, другая случайная маска, другой хэш.