php扩展开发之最详细的return-ag捕鱼王app官网

php扩展开发之最详细的return_stringl讲解

作者:迹忆客 最近更新:2022/12/26 浏览次数:

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扩展过程中,一定要区分是使用何种方式申请的内存,然后使用相应的方法进行释放。

上一篇:

下一篇:php扩展开发 ini配置项定义

转载请发邮件至 1244347461@qq.com 进行申请,经作者同意之后,转载请以链接形式注明出处

本文地址:

相关文章

如何使用 clion 开发调试 php 扩展

发布时间:2021/07/02 浏览次数:287 分类:php

php 扩展的创建这里就不再赘述,使用ext_skel 生成一个框架,然后编辑相应的文件,编译安装,最后在php.ini 配置文件中加入生成的扩展 例如 my_ext.so

php扩展开发 ini配置项定义

发布时间:2021/07/02 浏览次数:46841 分类:php

本篇主要介绍在php扩展开发过程中,如何定义扩展的ini配置项。本章内容将通过jlog扩展为示例,说明定义ini配置项

扫一扫阅读全部技术教程

社交账号
  • https://www.github.com/onmpw
  • qq:1244347461

最新推荐

教程更新

热门标签

扫码一下
查看教程更方便
网站地图