PHP 7 如何从常规列表中删除资源?

How does PHP 7 remove the resources from the regular list?

我正在将 php 扩展升级到 php 7,我想删除使用 zend_register_resource 在常规列表中注册的资源,稍后我将关闭资源使用 zend_list_close。关闭函数如下所示:

    PHP_FUNCTION(myFunc_cleanAndExit)
    {
     zval* rsrc = NULL;
     ...
     int res = zend_parse_parameters(ZEND_NUM_ARGS() , "r", &rsrc );
     if (res == FAILURE)
     {
        //handle it
     }
     ...
     zend_fetch_resource(Z_RES_P(rsrc), ./other args/..)
     ...
     zend_list_close(Z_RES_P(rsrc));
     ...
     }

原来在PHP5中,rsrc是用zend_hash_index_del( &EG( regular_list ), Z_RESVAL_P( rsrc))去掉的,所以如果这个函数被调用两次,zend_parse_parameterreturnsFAILURE,因为该资源已从常规列表中删除并且不存在。

在PHP7中,zend_list_close调用zend_resource_dtor(rsrc refcount是2(为什么是递增的?注册资源后它是1))并清除任何rsrc 的记忆。但是,当我们像这样调用它两次时,乐趣就来了:

    myFunc_cleanAndExit($var-rsrc);
    myFunc_cleanAndExit($var-rsrc);

第二次zend_parse_parameter没有return失败(因为资源还没有从常规列表中删除),rsrc的引用计数增加到3解析后类型为-1,ptr为NULL(第一次调用时在zend_resource_dtor中赋值)。当我在 PHP 脚本中调用 get_resources() 时,我得到 rsrc 及其 ID 和类型 UNKOWN,在 PHP5 rsrc 中变为NULL。这是我的问题:

1- 什么时候从常规列表中删除资源? (如果我使用 zend_hash_index_del 从常规列表中手动删除它,它会在 zend_alloc 中中断)

2- 在关闭一次后让类型为 UNKOWN 的资源处于活动状态是否可以?不吃掉内存?如果不是,我应该怎么做才能让它为 NULL?

3- 当我注册资源并检查它的引用计数时,它是 1,但是当我调用关闭函数时,在 zend_parse_parameter 之后引用计数增加了,为什么?

谢谢

示例 PHP 脚本:

    <?php
    $var_rsrc = myFunc_startUp();
    var_dump($var_rsrc); // resource(2) of type 'MyFunc'
    myFunc_cleanAndExit($var_rsrc);
    var_dump($var_rsrc); // resource(2) of type 'UNKOWN' (originally this was  NULL)
    myFunc_cleanAndExit($var_rsrc);
    var_dump($var_rsrc); // resource(2) of type 'UNKOWN'

简短的回答是资源在请求关闭时被删除,特别是在 zend_hash_graceful_reverse_destroy 中。您还应该检查 zend_fetch_resource 的 return 值,因为它将 return NULL(并发出警告)用于已破坏的资源。

我认为您所描述的一切都是正常的,包括 zend_parse_parameters 成功解析了先前被破坏的资源。下面的程序表现出完全相同的行为:

<?php
$r = popen('ls', 'r');
echo fgets($r);
pclose($r);
var_dump(get_resources());
pclose($r);

您的资源的引用计数是 2,因为 SEND_VAR 操作码(即,将参数传递给函数)会将其 ZVAL_COPY 到 arglist,这会增加引用计数。 (稍后在 zend_vm_stack_free_args 中取消引用)

关于内存使用,我不担心资源占用内存。我们已经知道它不会泄漏,因为它会在 RSHUTDOWN 被清理干净。即使您手动从散列中删除该项目,我也不认为保证会立即释放底层内存。