我的编程空间,编程开发者的网络收藏夹
学习永远不晚

PHP扩展开发之最详细的RETURN_STRINGL讲解

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

PHP扩展开发之最详细的RETURN_STRINGL讲解

RETURN_STRINGL的定义如下:


#define RETURN_STRINGL(s, l, duplicate) { RETVAL_STRINGL(s, l, duplicate); return; }

#define RETVAL_STRINGL(s, l, duplicate)     ZVAL_STRINGL(return_value, s, l, duplicate)

第一个参数s 是我们要传入的字符串地址——( char * )。第二个参数l是字符串的长度。 第三个参数duplicate: 这个参数表示的是新申请一个空间保存传入的字符串,还是直接使用传入的字符串。

例如:


PHP_FUNCTION(varinfo)
{
    char *var_name = NULL;
    int var_name_len,len;
    char *p;
    zval **var;
    int res;
    HashTable *vars;

    if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"s",&var_name,&var_name_len) == FAILURE) {
        return ;
    }

    vars = EG(active_symbol_table);

    if(hash_find(&var,vars,var_name,var_name_len TSRMLS_CC)) {
        php_printf("%s: ", var_name);

        p = get_var_info(var,&len TSRMLS_CC);
        return;

    }else{
        PHPWRITE("no such symbol.\n",16);
    }

    RETURN_STRINGL(p,len,0);
}

在这个例子中,p是通过内存分配函数realloc 直接向操作系统申请的内存。如果直接作为返回值,函数是可以将字符串返回的,但是这里会有一个问题就是在释放内存的过程中会出现错误。


Zend/zend_execute.h(95) : Block 0x7fb984802800 status:
.../php56/Zend/zend_variables.c(37) : Actual location (location was relayed)
Invalid pointer: ((thread_id=0x834A7CA0) != (expected=0x118F6DC0))
Invalid pointer: ((size=0x00000000) != (next.prev=0x7fb983723c10))

为什么会出现这种问题呢,原因就是php有自己的内存管理。因为我们的p是直接向操作系统申请的内存,而不是通过PHP的内存管理分配的内存,所以在最后释放内存的时候发现这块儿内存不是有效的内存,因此会报错。 通过上面的错误信息我们也大概能看出原因。

所以这就需要RETURN_STRINGL的第三个参数了, 将第三个参数传1,让系统先通过PHP内存管理分配一块儿内存,然后将p地址的内容复制到新分配的内存中。这样在释放的时候就不会有问题了。


PHP_FUNCTION(varinfo)
{
    ...

    RETURN_STRINGL(p,len,1);

当然,除了这种方式,我们也可以自己手动使用PHP的内存管理分配一个新的内存。代码如下:


PHP_FUNCTION(varinfo)
{
    char *var_name = NULL;
    int var_name_len,len;
    char *p,*strg;
    zval **var;
    int res;
    HashTable *vars;

    if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"s",&var_name,&var_name_len) == FAILURE) {
        return ;
    }

    vars = EG(active_symbol_table);

    if(hash_find(&var,vars,var_name,var_name_len TSRMLS_CC)) {
        php_printf("%s: ", var_name);

        p = get_var_info(var,&len TSRMLS_CC);

    }else{
        PHPWRITE("no such symbol.\n",16);
        return;
    }

    len = spprintf(&strg, 0, "%s", p);
    
    RETURN_STRINGL(strg,len,0);
}

spprintf 函数会自动分配给变量strg内存。这种方式也是可以的。

通过RETURN_STRINGL可以说明一类问题。像返回整型、布尔型等等,原理都是一样的。要注意内存的释放,处理不好容易导致内存泄漏

扩展1 —— 内存泄漏简单说明

关于内存泄漏,PHP底层会为我们进行检测。前提是我们的变量都是通过PHP的内存管理进行分配的。如果是我们自己手动直接向操作系统申请的,PHP底层是不容易检测的,需要借助其他工具来检测。我们这里先简单介绍PHP本身的检测。

内存泄漏这很容易理解。就是我们通过PHP内存管理申请的内存之后,最终没有进行释放。还是拿上面的例子,我们稍微改动一下


PHP_FUNCTION(varinfo)
{
    char *var_name = NULL;
    int var_name_len,len;
    char *p,*strg;
    zval **var;
    int res;
    HashTable *vars;

    if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"s",&var_name,&var_name_len) == FAILURE) {
        return ;
    }

    vars = EG(active_symbol_table);

    if(hash_find(&var,vars,var_name,var_name_len TSRMLS_CC)) {
        php_printf("%s: ", var_name);

        p = get_var_info(var,&len TSRMLS_CC);

    }else{
        PHPWRITE("no such symbol.\n",16);
        return;
    }

    len = spprintf(&strg, 0, "%s", p);
    
    RETURN_STRINGL(p,len,1);
}

这里,我们使用PHP内存管理器给变量strg分配了内存,但是最后我们并没有将其作为return_value返回给应用层,这就导致PHP底层回收的时候不能正确回收变量strg的内存,所以这里会检测到有可能会造成内存泄漏


.../php56/main/spprintf.c(794) :  Freeing 0x10252CC68 (79 bytes), script=/Users/mihuan/workspace/php/varinfo.php
=== Total 1 memory leaks detected ===

那为什么如果将strg传给return_value 作为返回值就不会造成内存泄漏呢。因为PHP底层会对return_value这种默认的变量自动进行回收。从而对于strg的内存也会进行回收。

扩展2 —— valgrind 内存泄漏检测

上面我们提过,对于不是使用PHP内存管理器分配的内存,PHP底层是不容易检测是否存在内存泄漏的可能。例如下面的例子


PHP_FUNCTION(varinfo)
{
    char *var_name = NULL;
    int var_name_len,len;
    char *p,*strg;
    zval **var;
    int res;
    HashTable *vars;

    if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"s",&var_name,&var_name_len) == FAILURE) {
        return ;
    }

    vars = EG(active_symbol_table);

    if(hash_find(&var,vars,var_name,var_name_len TSRMLS_CC)) {
        php_printf("%s: ", var_name);

        p = get_var_info(var,&len TSRMLS_CC);

    }else{
        PHPWRITE("no such symbol.\n",16);
        return;
    }

    len = spprintf(&strg, 0, "%s", p);
    
    RETURN_STRINGL(strg,len,0);
}

变量p并没有手动进行释放,并且PHP底层也不会自动对其进行回收,因为这是直接向操作系统申请的内存。 但是在执行的过程中PHP底层并不会报出内存泄漏的错误。这时我们就要借助第三方工具——valgrind进行检测


$ valgrind --leak-check=full --show-reachable=yes /usr/local/bin/php -f /root/workspace/php/varinfo.php

得出的结果如下


...
==27216== 1,025 bytes in 1 blocks are definitely lost in loss record 11 of 11
==27216==    at 0x4C29DAD: malloc (vg_replace_malloc.c:308)
==27216==    by 0x4C2C100: realloc (vg_replace_malloc.c:836)
==27216==    by 0xED4AD57: ???
==27216==    by 0xED4AE67: ???
==27216==    by 0xED4B092: ???
==27216==    by 0x8FA213: zend_do_fcall_common_helper_SPEC (zend_vm_execute.h:558)
==27216==    by 0x900D32: ZEND_DO_FCALL_SPEC_CONST_HANDLER (zend_vm_execute.h:2602)
==27216==    by 0x8F946C: execute_ex (zend_vm_execute.h:363)
==27216==    by 0x8F9552: zend_execute (zend_vm_execute.h:388)
==27216==    by 0x8AD44D: zend_execute_scripts (zend.c:1341)
==27216==    by 0x7EEA4B: php_execute_script (main.c:2613)
==27216==    by 0x976345: do_cli (php_cli.c:998)
==27216==
==27216== LEAK SUMMARY:
==27216==    definitely lost: 1,025 bytes in 1 blocks
==27216==    indirectly lost: 0 bytes in 0 blocks
==27216==      possibly lost: 0 bytes in 0 blocks
==27216==    still reachable: 552 bytes in 10 blocks
==27216==         suppressed: 0 bytes in 0 blocks
==27216==
==27216== For lists of detected and suppressed errors, rerun with: -s
==27216== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

很明显上面报出了一个错误。这就是变量p没有被手动释放造成的。下面我们来手动释放


PHP_FUNCTION(varinfo)
{
    ...

    if(hash_find(&var,vars,var_name,var_name_len TSRMLS_CC)) {
        php_printf("%s: ", var_name);

        p = get_var_info(var,&len TSRMLS_CC);

    }else{
        PHPWRITE("no such symbol.\n",16);
        return;
    }

    len = spprintf(&strg, 0, "%s", p);
    
    free(p); // 释放内存
    
    RETURN_STRINGL(strg,len,0);
}

然后再次运行上面的valgrind命令,得到结果如下


==27864== 176 bytes in 1 blocks are still reachable in loss record 10 of 10
==27864==    at 0x4C29E63: malloc (vg_replace_malloc.c:309)
==27864==    by 0x640F5B7: CRYPTO_malloc (in /usr/lib64/libcrypto.so.1.0.2k)
==27864==    by 0x64C747F: lh_new (in /usr/lib64/libcrypto.so.1.0.2k)
==27864==    by 0x6410984: ??? (in /usr/lib64/libcrypto.so.1.0.2k)
==27864==    by 0x6410A34: ??? (in /usr/lib64/libcrypto.so.1.0.2k)
==27864==    by 0x6410B83: ??? (in /usr/lib64/libcrypto.so.1.0.2k)
==27864==    by 0x47BAA5: zm_startup_openssl (openssl.c:1153)
==27864==    by 0x8B56D9: zend_startup_module_ex (zend_API.c:1797)
==27864==    by 0x8B5786: zend_startup_module_int (zend_API.c:1810)
==27864==    by 0x8C12FD: zend_hash_apply (zend_hash.c:641)
==27864==    by 0x8B5D65: zend_startup_modules (zend_API.c:1930)
==27864==    by 0x7EDE93: php_module_startup (main.c:2323)
==27864==
==27864== LEAK SUMMARY:
==27864==    definitely lost: 0 bytes in 0 blocks
==27864==    indirectly lost: 0 bytes in 0 blocks
==27864==      possibly lost: 0 bytes in 0 blocks
==27864==    still reachable: 552 bytes in 10 blocks
==27864==         suppressed: 0 bytes in 0 blocks
==27864==
==27864== For lists of detected and suppressed errors, rerun with: -s
==27864== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

错误已经消失了。

上面举的例子有些变量其实是多余的,我们这里仅仅是为了说明情况。例如,去掉变量strg


PHP_FUNCTION(varinfo)
{
    char *var_name = NULL;
    int var_name_len,len;
    char *p;
    zval **var;
    int res;
    HashTable *vars;

    if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"s",&var_name,&var_name_len) == FAILURE) {
        return ;
    }

    vars = EG(active_symbol_table);

    if(hash_find(&var,vars,var_name,var_name_len TSRMLS_CC)) {
        php_printf("%s: ", var_name);

        p = get_var_info(var,&len TSRMLS_CC);

    }else{
        PHPWRITE("no such symbol.\n",16);
        return;
    }
    
    RETURN_STRINGL(p,len,1);
}

但是这个方式就又有了问题了,p没有地方手动进行释放。所以还需要替换RETURN_STRINGL


PHP_FUNCTION(varinfo)
{
    ...
    
    // RETURN_STRINGL(p,len,1);
    // 替换如下
    RETVAL_STRINGL(p,len,1);
    free(p);
    return;
}

申请的内存一定要记着释放。在开发PHP扩展过程中,一定要区分是使用何种方式申请的内存,然后使用相应的方法进行释放。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

PHP扩展开发之最详细的RETURN_STRINGL讲解

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

PHP扩展开发之最详细的RETURN_STRINGL讲解

RETURN_STRINGL的定义如下:#define RETURN_STRINGL(s, l, duplicate) { RETVAL_STRINGL(s, l, duplicate); return; }#define RETVAL_S
PHP扩展开发之最详细的RETURN_STRINGL讲解
2024-02-27

Vue开发工具之vuejs-devtools安装教程及常见问题解决(最详细)

这篇文章主要介绍了Vue开发工具vuejs-devtools超级详细安装教程以及常见问题解决本篇文章是最详细的vue开发工具vuejs-devtools安装教程,需要的朋友可以参考下
2022-11-16

PHP扩展开发:如何使用类型注解优化自定义函数的性能?

通过使用类型注解,可以提高php自定义函数的执行速度:php无需检查输入类型,节省了开销,提高了速度。强制接受和返回正确的数据类型,防止类型错误和漏洞。优化案例:使用类型注解的自定义函数sum执行速度提高了约16%。PHP扩展开发:使用类型
PHP扩展开发:如何使用类型注解优化自定义函数的性能?
2024-05-15

javascript当对象的属性之一发生变化时触发此事件使用什么函数,详细讲解

JavaScript中可以通过Object.defineProperty()函数监听对象属性的更改。该函数允许定义自定义属性描述符,包括get和set函数。当属性发生更改时,这些函数被调用,触发自定义事件。通过使用this关键字,可以访问对象属性和方法。确保指定get和set函数,否则将使用默认行为。
javascript当对象的属性之一发生变化时触发此事件使用什么函数,详细讲解
2024-04-02

javascript当文本内容选择将开始发生时触发的事件使用什么函数,详细讲解

selectionchange事件用于检测用户在文本字段或区域中选择文本时的更改。其语法为element.addEventListener("selectionchange",callbackFunction)。事件对象提供selectionStart、selectionEnd和target属性来获取文本选择信息。该事件可用于文本编辑器、表单验证、复制粘贴和代码编辑器等应用程序中。
javascript当文本内容选择将开始发生时触发的事件使用什么函数,详细讲解
2024-04-02

javascript在Marquee内的内容移动至Marquee显示范围之外时触发此事件使用什么函数,详细讲解

当鼠标指针移出Marquee元素时,触发onmouseout事件,可执行特定行为。onmouseout事件语法为,其中myFunction()为回调函数。常用做法是利用onmouseout事件在Marquee内容移动至显示范围之外时采取措施,如停止滚动、隐藏Marquee或更改滚动方向。示例中通过onmouseout事件停止Marquee滚动:。当鼠标移出Marquee,将执行回调函数this.stop(),调用Marquee元素的stop()方法,停止滚动。
javascript在Marquee内的内容移动至Marquee显示范围之外时触发此事件使用什么函数,详细讲解
2024-04-02

javascript当对象被鼠标拖动的对象离开其容器范围内时触发此事件使用什么函数,详细讲解

当对象被鼠标拖动离开容器时触发的JavaScript函数是dragleave。此事件在以下条件下触发:对象完全离开容器边界对象进入另一个元素边界dragleave事件处理程序负责更新视觉提示、停止接收数据传输事件并恢复元素的默认状态。最佳实践包括使用容器元素作为事件监听器、防止默认操作并清除视觉提示。
javascript当对象被鼠标拖动的对象离开其容器范围内时触发此事件使用什么函数,详细讲解
2024-04-02

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录