Почему 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(), я увижу, что она не вызывается во время разветвления.

Какая-либо причина, по которой переменные/объекты не являются общими для обоих процессов, или какое-либо хорошее чтение по этой теме, ребята?

Author: Benjamin, 2013-04-02

2 answers

Хэш объекта не будет вычисляться при создании объекта (как можно было бы подумать). Хэш объекта будет вычислен при первом вызове spl_object_hash() для объекта. Это после развилки в вашем примере.

Далее обратите внимание, что для вычисления хэша используется некоторая случайность, поэтому используются разные хэши.

 1
Author: hek2mgl, 2013-04-02 17:37:29

Ссылка на объект является одинаковой в разветвленном процессе, поскольку расположение объекта в памяти дочернего процесса одинаково.

Хэш вычисляется как адрес объекта ИЛИ случайная маска (которая генерируется только один раз), как вы можете прочитать в исходном коде 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

Как вы можете видеть, начальное значение зависит от идентификатора процесса , который, конечно, отличается для родителя и ребенка.

Итак, другое начальное значение генератора случайных чисел, другая случайная маска, другой хэш.

 1
Author: Joni, 2013-04-02 18:03:49