php - 如何检查是否在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() 函数中(假设我有一个指向它的指针以某种方式保存在全局哈希表中)
解决方案
通常,您不必担心处理函数返回值的生命周期。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_release
在zend_string
' 的引用计数器达到零时调用(即它不再被任何zval
s 使用)。引擎处理破坏return_value
zval
zval
在可能通过变量赋值、函数调用参数赋值、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
。
我希望这能解决问题。请注意,不久前我也回答了一个与您非常相似的问题;它也可能对您有用。
推荐阅读
- php - 如何在 PHP MySQL 的单个查询中获取相隔 10 分钟的所有行?
- javascript - fetch 语句完成后的 Javascript 执行语句
- javascript - 使用 fs.mkdir(path[, options], callback) 而不使用回调
- python - 如何在 macOS 上的 tkinter 中删除按钮的蓝色边框?
- python - Pandas:根据它们所属的数字范围重命名列中的值?
- mysql - 在每条记录上应用函数 - SQL UDF 或 Spark
- react-native - React Native Paper Icons 存储库
- breadth-first-search - 用 Dafny 证明 BFS 的终止
- aws-sdk - 无法使用 aws-sdk 访问 aws 资源,但能够使用 aws cli 和 Web 控制台访问
- java - 该系统找不到指定的路径。'-Xmx1000M' 不是内部或外部命令、可运行程序或批处理文件