首页 > 解决方案 > 如何检查是否在php用户空间中使用了return_value(来自php函数,在本机扩展内)?

问题描述

所以我正在编写一个扩展来充当某个多线程网络库的包装器。现在,使用的模式是一个简单的请求-回复。

我要做的就是为回复分配一个zend_string(带有zend_string_init),并通过return_value返回它。问题是,我不想显式调用 zend_string_release,因为 php 是如何处理 zvals 的。

我的意思是:如果某个 zval 进入 php 用户空间,它将在不再使用后被销毁并释放。如果它没有到达那里(例如,用户将在 php 中使用“myfunc();”之类的东西,而不是“$result = myfunc();”),我必须销毁它。

我觉得这是一个相当棘手的案例。我想知道 execute_data 参数中是否没有某些函数、宏或字段,可以告诉我函数的结果是否在 php 中使用。如果不使用,则表示我不需要为回复分配内存。如果是,我会分配内存并返回它,它会自动释放。

编辑:如果这种机制不存在或不可靠或不是最佳使用实践(正如另一位用户指出的那样),应该如何管理内存?

例如像

PHP_FUNCTION(myfunc)
{
  zend_string *dummy = zend_string_init("dummy", sizeof("dummy") - 1, 0);
  RETVAL_STR(dummy);
  return;
}

如果用户在 php 中没有执行 $var = myfunc() 之类的操作,则会导致内存泄漏,如果用户确实执行了 $var = myfunc() 并且我执行了类似 zend_string_release( dummy)在 RSHUTDOWN() 函数中(假设我有一个指向它的指针以某种方式保存在全局哈希表中)

标签: phpphp-extension

解决方案


通常,您不必担心处理函数返回值的生命周期。PHP 引擎处理这个值就像用户空间中的任何其他值一样。换句话说,您对用户空间中“正在使用”(或“未使用”)的返回值的概念并不完全正确。事实上,无论用户代码是否将返回值分配给变量,引擎都会“使用”每个返回值。这意味着应用了 PHP 的自动内存管理,一切都得到了正确的处理并最终被释放。

在底层,everyPHP_FUNCTION传递了一个zval具有参数名称的参数return_value(当然,PHP_FUNCTION宏隐藏了这个事实)。这zval总是在函数被调用之前被初始化为NULL(例如)。ZVAL_NULL(return_value)RETVAL_STR(str)计算为调用ZVAL_STR(return_value,str),它将 分配zend_string*给现有的return_value zval. 请记住,azend_string是一个引用计数结构,分配通过zval 继承初始引用集zend_string_init。然后引擎将处理分配的引用计数,zend_string*并最终zend_string_releasezend_string' 的引用计数器达到零时调用(即它不再被任何zvals 使用)。引擎处理破坏return_value zvalzval在可能通过变量赋值、函数调用参数赋值、ETC将值分配给另一个之后。

注意:如果有人读这篇文章比 PHP7 更熟悉 PHP5(比如我自己),那么请注意它是相同的概念,除了zend_string*是 a char*,并且在 PHP5 中,zval它本身是引用计数的,而不是zend_string.

因此,例如,考虑这个:

<?php
myfunc();

在这种情况下,返回值由引擎处理,就像我们期望使用 PHP 的引用计数系统一样。zend_string*峰值的引用计数1在被销毁之前递减一次。换句话说,zend_string生活完全在return_value zval被清理之前。现在考虑另一种情况:

<?php
$val = myfunc();
echo "Got message: $val\n";

在这种情况下,仍然使用引用计数系统以完全相同的方式处理返回值,尽管需要更长的时间(即更多的引用计数递增/递减)。2当返回值分配给 时,引用计数至少会命中$val。当引擎销毁 时return_value zval,引用计数将减少到,1因为zend_string*仍然存在$val

我希望这能解决问题。请注意,不久前我也回答了一个与您非常相似的问题;它也可能对您有用。


推荐阅读