资讯专栏INFORMATION COLUMN

PHP 进阶之路 - PHP7 使用资源包裹第三方扩展原理分析

ctriptech / 1608人阅读

摘要:重点分析其中宏,返回表的,后面用来作为索引查询的依据。中资源的解析比中解析简单快捷很多,得益于其结构的改变。先从逆向通过其在中索引层层关联,找到该类资源的释放回调函数,然后对该资源执行释放回调函数。

PHP 扩展开发的文章,我均已更新至《TIPI》
本篇承接上篇 PHP7 使用资源包裹第三方扩展的实现

PHP7 使用资源包裹第三方扩展原理分析 注册资源类型源码
[c]
ZEND_API int zend_register_list_destructors_ex(rsrc_dtor_func_t ld, rsrc_dtor_func_t pld, const char *type_name, int module_number)
{
   zend_rsrc_list_dtors_entry *lde;
   zval zv;
 
   lde = malloc(sizeof(zend_rsrc_list_dtors_entry));
   lde->list_dtor_ex = ld;
   lde->plist_dtor_ex = pld;
   lde->module_number = module_number;
   lde->resource_id = list_destructors.nNextFreeElement;
   lde->type_name = type_name;
   ZVAL_PTR(&zv, lde);
 
   if (zend_hash_next_index_insert(&list_destructors, &zv) == NULL) {
      return FAILURE;
   }
   return list_destructors.nNextFreeElement-1;
}

其中

[c]
ZVAL_PTR(&zv, lde);

等价于

[c]
zv.value.ptr = (lde);
zv.u1.type_info = IS_PTR;

list_destructors 是一个全局静态 HashTable,资源类型注册时,将一个 zval 结构体变量 zv 存放入 list_destructorsarData 中,而 zvvalue.ptr 却指向了 zend_rsrc_list_dtors_entry *ldelde中包含的该种资源释放函数指针、持久资源的释放函数指针,资源类型名称,该资源在 hashtable 中的索引依据 (resource_id)等。
而这里的 resource_id 则是该函数的返回值,所以后面我们在解析该类型变量时,都需要将 resource_id 带上。
整个的注册步骤可以总结为下图:

资源的注册
[c]
ZEND_API zend_resource* zend_register_resource(void *rsrc_pointer, int rsrc_type)
{
   zval *zv;
 
   zv = zend_list_insert(rsrc_pointer, rsrc_type);
 
   return Z_RES_P(zv);
}

该函数的功能则是将 zend_list_insert 返回的 zval 中的资源指针返回。Z_RES_P 宏在 Zend/zend_types.h 中定义。
重点分析 zend_list_insert

[c]
ZEND_API zval *zend_list_insert(void *ptr, int type)
{
   int index;
   zval zv;

   index = zend_hash_next_free_element(&EG(regular_list));
   if (index == 0) {
      index = 1;
   }
   ZVAL_NEW_RES(&zv, index, ptr, type);
   return zend_hash_index_add_new(&EG(regular_list), index, &zv);
}

其中zend_hash_next_free_element宏,返回&EG(regular_list)表的nNextFreeElement,后面用来作为索引查询的依据。
ZVAL_NEW_RES宏是 PHP7 新增的一套东西,把一个资源装载到zval里去,因为PHP7 中Bucket只能存zval了。

[c]
#define ZVAL_NEW_RES(z, h, p, t) do {                            
        zend_resource *_res =                                    
        (zend_resource *) emalloc(sizeof(zend_resource));        
        zval *__z;                                            
        GC_REFCOUNT(_res) = 1;                                    
        GC_TYPE_INFO(_res) = IS_RESOURCE;                        
        _res->handle = (h);                                        
        _res->type = (t);                                        
        _res->ptr = (p);                                        
        __z = (z);                                            
        Z_RES_P(__z) = _res;                                    
        Z_TYPE_INFO_P(__z) = IS_RESOURCE_EX;                    
    } while (0)

代码比较清晰,首先根据h,p,t新建了一个资源,然后一起存入了z这个zval的结构体。(最后两个宏前面刚刚讨论过了)
最后就是zend_hash_index_add_new宏了,追踪代码发现其最后等价于调用的是

[c]
_zend_hash_index_add_or_update_i(&EG(regular_list), index, &zv, HASH_ADD | HASH_ADD_NEW ZEND_FILE_LINE_RELAY_CC)

关于PHP7 HashTable的具体操作,这里暂不做细致的分析,后期更新前面的数据结构的章节。注册的整个逻辑如下图:

解析资源源码分析
[c]
ZEND_API void *zend_fetch_resource(zend_resource *res, const char *resource_type_name, int resource_type)
{
   if (resource_type == res->type) {
      return res->ptr;
   }
 
   if (resource_type_name) {
      const char *space;
      const char *class_name = get_active_class_name(&space);
      zend_error(E_WARNING, "%s%s%s(): supplied resource is not a valid %s resource", class_name, space, get_active_function_name(), resource_type_name);
   }
 
   return NULL;
}

在上面的例子中我们是这样解析的

[c]
(FILE *)zend_fetch_resource(Z_RES_P(filehandle), TIPI_FILE_TYPE, le_tipi_file)

首先通过Z_RES_P宏,获取filehandle这个zval变量中的zend_resource。然后zend_fetch_resource中只是对比了zend_resourcetype与我们预想的资源类型是否一致,然后返回了zend_resource*ptr,最后转换成FILE *指针。
PHP7 中资源的解析比 PHP5中解析简单快捷很多,得益于其 zval 结构的改变。
原来PHP5中则需要通过EG(regular_list)查找,如下图所示:

而现在 PHP7的解析则直接从zval里解析出zend_resource,如下图所示:

删除资源源码分析
[c]
ZEND_API int zend_list_close(zend_resource *res)
{
   if (GC_REFCOUNT(res) <= 0) {
      return zend_list_free(res);
   } else if (res->type >= 0) {
      zend_resource_dtor(res);
   }
   return SUCCESS;
}

与PHP5 不同的地方,这里不是每次都进来将其引用计数减一操作,而是直接调用zend_resource_dtor函数。

[c]
static void zend_resource_dtor(zend_resource *res)
{
   zend_rsrc_list_dtors_entry *ld;
   zend_resource r = *res;

   res->type = -1;
   res->ptr = NULL;

   ld = zend_hash_index_find_ptr(&list_destructors, r.type);
   if (ld) {
      if (ld->list_dtor_ex) {
         ld->list_dtor_ex(&r);
      }
   } else {
      zend_error(E_WARNING, "Unknown list entry type (%d)", r.type);
   }
}

如果引用计数已经等于0或者小于0了,那么才从EG(regular_list)中删除

[c]
ZEND_API int zend_list_free(zend_resource *res)
{
   if (GC_REFCOUNT(res) <= 0) {
      return zend_hash_index_del(&EG(regular_list), res->handle);
   } else {
      return SUCCESS;
   }
}

原理图还是引用上面的注册资源类型、并注册资源的图。
先从zend_resource逆向通过其typelist_destructors中索引层层关联,找到该类资源的释放回调函数,然后对该资源执行释放回调函数。
而后面的从EG(regular_list)中删除,则是通过res->handler做为索引的依据。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/23257.html

相关文章

  • PHP 进阶之路 - PHP7 使用资源包裹三方扩展的实现

    摘要:释放用于在不同请求中始终存在的永久资源的函数。这是新增的数据结构,在则是。这使得引擎做一些例如资源类型,注册变量等的一次初始化。也就是说该函数的返回值为指针。会将返回值的设为,设为。资源的删除资源删除传入需要被删除的资源即可。 PHP 扩展开发的文章,我均已更新至《TIPI》 在阅读下面的内容之前,我们假定你已经对 PHP 7 基本的数据结构 都有大致的了解了,这是下面内容阅读的前提。...

    caozhijian 评论0 收藏0
  • PHP7 使用资源包裹三方扩展的实现及其源码解读

    摘要:也就是说该函数的返回值为指针。会将返回值的设为,设为。而这里的则是该函数的返回值,所以后面我们在解析该类型变量时,都需要将带上。 在阅读下面的内容之前,我们假定你已经对 PHP 7 基本的数据结构都有大致的了解了,这是下面内容阅读的前提。 我们分为两大块: 首先实现一个自定义的文件打开、读取、写入、关闭的文件操作扩展; 然后分析各个操作背后的实现原理,其中某些部分的实现我会和 PHP ...

    williamwen1986 评论0 收藏0
  • PHP 进阶之路 - 亿级 pv 网站架构实战之性能压榨

    摘要:业务和架构不分家,架构是建立在对业务的理解之上的。主键最好保持顺序递增,随机主键会导致聚簇索引树频繁分裂,随机增多,数据离散,性能下降。没有索引的更新,可能会导致全表数据都被锁住。 本博客并非全部原创,其实是一个知识的归纳和汇总,里面我引用了很多网上、书上的内容。也给出了相关的链接。 本文涉及的知识点比较多,大家可以根据关键字去搜索相关的内容和购买相应的书籍进行系统的学习。不对的地方...

    SnaiLiu 评论0 收藏0
  • PHP 进阶之路 - 揭开 PHP 线程安全的神秘面纱

    摘要:如果现有子进程中的线程总数不能满足负载,控制进程将派生新的子进程。为解决线程的并发问题,引入了线程安全资源管理器。的全拼,用来存放各个线程的链表。 PHP 进阶之路 - 零基础构建自己的服务治理框架(上) PHP 进阶之路 - 零基础构建自己的服务治理框架(下) PHP 进阶之路 - 亿级 pv 网站架构的技术细节与套路 PHP 进阶之路 - 亿级 pv 网站架构实战之性能压榨 注...

    pepperwang 评论0 收藏0
  • PHP小知识点

    摘要:那些琐碎的知识点作者记录的的很奇特很难记的知识点。易错知识点整理注意和的区别中和都是输出的作用,但是两者之间还是有细微的差别。今天手头不忙,总结一下,分享过程中掌握的知识点。 深入理解 PHP 之:Nginx 与 FPM 的工作机制 这篇文章从 Nginx 与 FPM 的工作机制出发,探讨配置背后的原理,让我们真正理解 Nginx 与 PHP 是如何协同工作的。 PHP 那些琐碎的知识...

    hover_lew 评论0 收藏0

发表评论

0条评论

ctriptech

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<