返回顶部
首页 > 资讯 > 后端开发 > PHP编程 >PHP底层内核源码之变量zend_zval结构体的示例分析
  • 774
分享到

PHP底层内核源码之变量zend_zval结构体的示例分析

2023-06-15 11:06:45 774人浏览 薄情痞子
摘要

小编给大家分享一下PHP底层内核源码之变量zend_zval结构体的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!zend_string的 结构体 的源码

小编给大家分享一下PHP底层内核源码之变量zend_zval结构体的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!

zend_string的 结构体 的源码。

struct _zend_string {zend_refcounted_h GC; //占用8个字节 用于gc的计数和字符串类型的记录zend_ulong        h;        // 占用8个字节 用于记录 字符串的哈希值size_t            len;       //占用8个字节    字符串的长度char              val[1];   //占用1个字节    字符串的值存储位置};

其中 len 变量 使得 zend_string 具备了 二进制安全 的特性

gc 也就是zend_refcounted_h 结构体的加持 可以实现 写时复制 (写时拷贝 copy-on-write) 的功能

typedef struct _zend_refcounted_h {uint32_t         refcount;//引用数uNIOn {uint32_t type_info;   //字符串所属的变量类别} u;} zend_refcounted_h;

copy-on-write 技术在redislinux内核里广泛应用

比如 Redis需要创建当前服务器进程的子进程,而大多数操作系统都采用写时复制(copy-on-write)来优化子进程的使用效率,所以在子进程存在期间,服务器会提高负载因子的阈值,从而避免在子进程存在期间进行哈希表扩展操作,避免不必要的内存写入操作,最大限度地节约内存。

php 7也采用了写时复制从而在进行赋值操作时比较节省内存,当字符串在赋值时并不直接拷贝一份数据,而是把zend_string结构体里的 _zend_refcounted_h中的 refcount 做+1 运算,字符串销毁时再把zend_string结构体里的 _zend_refcounted_h中的 refcount 做-1 运算。

如果您看过 陈雷大佬写的 《PHP底层源码设计与实现》 一书 可以会发现 稍微不一样 因为 我的版本是PHP7.4 书中版本 与我本地安装的不同 ,猜测可能是为了统一进行内存管理。

zend_string结构体里面的gc.u.flags字段,gc.u.flags总共有8位,每个类别占一位,可以重复打标签,理论上最多打8种标签。目前PHP 7源码主要涉及以下几种:1)对于临时的普通字符串,flags字段被标识为0。2)对于内部字符串,用于存储PHP代码中的字面量、标识符等,flags字段被标识成IS_STR_PERSISTENT |IS_STR_INTERNED。3)对于PHP已知字符串,flags字段会被标识成IS_STR_PERSISTENT|IS_STR_INTERNED|IS_STR_PERMANENT。

--------摘自 《PHP底层源码设计与实现》

在 PHP7.4源码底层会给 变量进行分类 方便内存的管理 其依赖于 zend_zval结构体里的u1.v.type_flags字段

struct _zval_struct { 197         zend_value        value;         //变量 198         union { 199                 struct { 200                         ZEND_ENDIAN_LOHI_3( 201                                 zend_uchar    type,  //变量类型            202                                 zend_uchar    type_flags,//可以用于变量的分类 203                                 union { 204                                         uint16_t  extra;         205                                 } u) 206                 } v; 207                 uint32_t type_info;//变量类型 208         } u1; 209           u2; 222 };

在555行有如下代码

#define IS_TYPE_REFCOUNTED(1<<0) //REFCOUNTED 可以计数的#define IS_TYPE_COLLECTABLE(1<<1) // TYPE_COLLECTABLE可收集的#if 1# define Z_TYPE_INFO_REFCOUNTED(t)(((t) & Z_TYPE_FLAGS_MASK) != 0)#else# define Z_TYPE_INFO_REFCOUNTED(t)(((t) & (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT)) != 0)#endif

所以PHP7.4版本中 zval.u1.v.type_flags 只有两种类型 0或者 1 同时我也看了下最新的PHP8版本代码 也是如此

为了更好的深入了解源码 也将 前面两节内容穿起来 我们安装gdb 来调试下PHP

GDB(GNU symbolic debugger)简单地说就是一个调试工具。它是一个受通用公共许可证即GPL保护的自由软件。像所有的调试器一样,GDB可以让你调试一个程序,包括让程序在你希望的地方停下,此时你可以查看变量、寄存器、内存及堆栈。更进一步你可以修改变量及内存值。GDB是一个功能很强大的调试器,它可以调试多种语言。在此我们仅涉及 C 和 c++ 的调试,而不包括其它语言。还有一点要说明的是,GDB是一个调试器,而不像 VC 是一个集成环境。你可以使用一些前端工具如XXGDB、DDD等。他们都有图形化界面,因此使用更方便,但它们仅是GDB的一层外壳。因此,你仍应熟悉GDB命令。事实上,当你使用这些图形化界面时间较长时,你才会发现熟悉GDB命令的重要性。

-----摘自oschina

[root@a3D3f47671d9 /]# php -vPHP 7.4.15 (cli) (built: Feb 21 2021 09:07:07) ( NTS )Copyright (c) The PHP GroupZend Engine v3.4.0, Copyright (c) Zend Technologies[root@a3d3f47671d9 /]# gbv    bash: gbv: command not found[root@a3d3f47671d9 /]# gdbbash: gdb: command not found[root@a3d3f47671d9 /]# yum install gdb

.........

新建一个 PHP 文件

[root@a3d3f47671d9 cui]# vim php7-4-test-zval.php php7-4-test-zval.php                                                                              Buffers   <?php   $a="abcdefg";   echo $a;   $b=88;   echo $b;   $c = $a;   echo $c;   echo $a;   $c ="abc";   echo $c;   echo $a;

用 gdb 运行 PHP

[root@a3d3f47671d9 cui]# gdb phpGNU gdb (GDB) Red Hat Enterprise Linux 8.2-12.el8Copyright (C) 2018 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later <Http://gnu.org/licenses/gpl.html>This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law.Type "show copying" and "show warranty" for details.This GDB was configured as "x86_64-redhat-linux-gnu".Type "show configuration" for configuration details.For bug reporting instructions, please see:<http://www.gnu.org/software/gdb/bugs/>.Find the GDB manual and other documentation resources online at:    <http://www.gnu.org/software/gdb/documentation/>.For help, type "help".Type "apropos Word" to search for commands related to "word"...Reading symbols from php...done.(gdb) b ZEND_ECHO_SPEC_CV_HANDLER   # b 命令意思是打断点Breakpoint 1 at 0x6dfe80: file /cui/php-7.4.15/Zend/zend_vm_execute.h, line 36987.(gdb) r php7-4-test-zval.phpStarting program: /usr/local/bin/php php7-4-test-zval.phpwarning: Error disabling address space randomization: Operation not permittedMissing separate debuginfos, use: yum debuginfo-install glibc-2.28-127.el8.x86_64warning: Loadable section ".note.gnu.property" outside of ELF segmentswarning: Loadable section ".note.gnu.property" outside of ELF segmentswarning: Loadable section ".note.gnu.property" outside of ELF segments[Thread debugging using libthread_db enabled]Using host libthread_db library "/lib64/libthread_db.so.1".warning: Loadable section ".note.gnu.property" outside of ELF segmentswarning: Loadable section ".note.gnu.property" outside of ELF segmentsBreakpoint 1, ZEND_ECHO_SPEC_CV_HANDLER () at /cui/php-7.4.15/Zend/zend_vm_execute.h:3698736987SAVE_OPLINE();Missing separate debuginfos, use: yum debuginfo-install libxcrypt-4.1.1-4.el8.x86_64 libxml2-2.9.7-8.el8.x86_64 sqlite-libs-3.26.0-11.el8.x86_64 xz-libs-5.2.4-3.el8.x86_64 zlib-1.2.11-16.el8_2.x86_64

可以看到 我的报错了 因为我是在Docker里跑的 Centos镜像 查了一些资料解决方法如下

编辑   /etc/yum.repos.d/CentOS-Debuginfo.repo 文件

修改enable=1

然后  yum install yum-utils

然后 dnf install glibc-langpack-en

yum debuginfo-install libxcrypt-4.1.1-4.el8.x86_64 libxml2-2.9.7-8.el8.x86_64 sqlite-libs-3.26.0-11.el8.x86_64 xz-libs-5.2.4-3.el8.x86_64 zlib-1.2.11-16.el8_2.x86_64

yum debuginfo-install glibc-2.28-127.el8.x86_64

让我们再次运行一下 gdb

[root@a3d3f47671d9 cui]# vim php7-4-test-zval.php[root@a3d3f47671d9 cui]# gdb phpGNU gdb (GDB) Red Hat Enterprise Linux 8.2-12.el8Copyright (C) 2018 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law.Type "show copying" and "show warranty" for details.This GDB was configured as "x86_64-redhat-linux-gnu".Type "show configuration" for configuration details.For bug reporting instructions, please see:<http://www.gnu.org/software/gdb/bugs/>.Find the GDB manual and other documentation resources online at:    <http://www.gnu.org/software/gdb/documentation/>.For help, type "help".Type "apropos word" to search for commands related to "word"...Reading symbols from php...done.(gdb)

在gdb模式 命令b 可以设置断点 你可以理解为PHP的 xdebug

还记得我们的 php7-4-test-zval.php 文件内容吗

<?php   $a="abcdefg";   echo $a;   $b=88;   echo $b;   $c = $a;   echo $c;   echo $a;   $c ="abc";   echo $c;   echo $a;

这个echo 语言结构 是为了我们调试使用 这里是个小技巧

(ps 我这里说的语言结构 可没说echo是函数 有一道面试题 php 中 echo()和var_dump()的主要区别?)

这个echo 其实是为了我们设置 断点ZEND_ECHO_SPEC_CV_HANDLER

ZEND_ECHO_SPEC_CV_HANDLER其实是个宏 以后在词法解析 语法分析 execute时候会详细展开讲解 如图

PHP底层内核源码之变量zend_zval结构体的示例分析

我们设置这个断点的意义是为了让程序在拼接echo 的时候暂停代码 以便我们分析

(gdb) b ZEND_ECHO_SPEC_CV_HANDLERBreakpoint 1 at 0x6dfe80: file /cui/php-7.4.15/Zend/zend_vm_execute.h, line 36987.

在gdb中 使用 r 运行文件

(gdb) r php7-4-test-zval.php Starting program: /usr/local/bin/php php7-4-test-zval.phpwarning: Error disabling address space randomization: Operation not permitted[Thread debugging using libthread_db enabled]Using host libthread_db library "/lib64/libthread_db.so.1".Breakpoint 1, ZEND_ECHO_SPEC_CV_HANDLER () at /cui/php-7.4.15/Zend/zend_vm_execute.h:3698736987SAVE_OPLINE();

在gdb中 用 n 可以执行下一步操作

(gdb) n36988z = EX_VAR(opline->op1.var);

这里我们暂且忽略继续往下走

ZEND_ECHO_SPEC_CV_HANDLER的完整代码如下(我贴出来只是想告诉你代码里有这行代码 让你知道为什么往下走,你现阶段不需要理解代码,慢慢来 )

static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ECHO_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS){USE_OPLINEzval *z;SAVE_OPLINE();/                                  } u)                  } v;                  uint32_t type_info;//变量类型          } u1;            u2;  };

gdb中变量$2 中 u1.v.type=6 我们拿出第二节的 类型定义源码部分对比下

#define IS_UNDEF0#define IS_NULL1#define IS_FALSE2#define IS_TRUE3#define IS_LONG4#define IS_DOUBLE5#define IS_STRING6#define IS_ARRAY7#define IS_OBJECT8#define IS_RESOURCE9#define IS_REFERENCE10.....//其实有20种  剩下的不是常用类型 代码就不全部粘出来了u1.v.type=6 类型是 IS_STRING

再看下 zval种 value 对应的 zend_value联合体中的代码

ypedef union _zend_value {zend_long         lval;double            dval;zend_refcounted  *counted;zend_string      *str;zend_array       *arr;zend_object      *obj;zend_resource    *res;zend_reference   *ref;zend_ast_ref     *ast;zval             *zv;void             *ptr;zend_class_entry *ce;zend_function    *func;struct {uint32_t w1;uint32_t w2;} ww;} zend_value;

还记得联合体的特性吗 ? 所有值公用一个内存空间

上面的gdb中变量$2 的v.type=6 所以 在value中 值被str占用了 同时str 前面有个*

*星号 在C语言里代表指针 指向另外一个值的地址 所以指向 zend_string结构体

关于C语言指针您可以参考 菜鸟学院-指针

所以 接下来我们可以通过获取value中的str来获取 查看值

(gdb) p *z.value .str $4 = {gc = {refcount = 1, u = {type_info = 70}}, h = 9223601495925209889, len = 7, val = "a"}

对比下 zend_string 源码

struct _zend_string {zend_refcounted_h gc;//引用计数zend_ulong        h;                size_t            len;//字符串长度char              val[1];};

* 你可能有疑问 val为啥 是val=“a” 我们不是定义$a="abcdefg"; 吗 ? 还记得柔性数组吗?:)

接下来继续往下走

gdb中 用c 来执行到下一个断点处

(gdb) cContinuing.Breakpoint 1, ZEND_ECHO_SPEC_CV_HANDLER () at /cui/php-7.4.15/Zend/zend_vm_execute.h:3698736987SAVE_OPLINE();(gdb) n36988z = EX_VAR(opline->op1.var);(gdb) n441return pz->u1.v.type;(gdb) n36997zend_string *str = zval_get_string_func(z);(gdb) p *z$6 = {  value = {lval = 88, dval = 4.3477776834029696e-322, counted = 0x58, str = 0x58, arr = 0x58, obj = 0x58,     res = 0x58, ref = 0x58, ast = 0x58, zv = 0x58, ptr = 0x58, ce = 0x58, func = 0x58, ww = {w1 = 88, w2 = 0}},   u1 = {v = {type = 4 '\004', type_flags = 0 '\000', u = {extra = 0}}, type_info = 4},   u2 = {next = 0,     cache_slot = 0, opline_num = 0, lineno = 0, num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0,     property_guard = 0, constant_flags = 0, extra = 0}}

u1.v.type=4 对应的是IS_LONG 代表整型 所以 在value中 值被lval占用了

可以看到值就是88 (lval不是指针 无需再跟进去查看了)

以上是“PHP底层内核源码之变量zend_zval结构体的示例分析”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注编程网PHP编程频道!

--结束END--

本文标题: PHP底层内核源码之变量zend_zval结构体的示例分析

本文链接: https://lsjlt.com/news/279910.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • PHP底层内核源码之变量zend_zval结构体的示例分析
    小编给大家分享一下PHP底层内核源码之变量zend_zval结构体的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!zend_string的 结构体 的源码...
    99+
    2023-06-15
  • PHP底层内核源码之变量的示例分析
    小编给大家分享一下PHP底层内核源码之变量的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!PHP变量的四个基本特征:1.变量命名变量命名上,PHP继承了P...
    99+
    2023-06-15
  • PHP底层内核源码之变量zend_string的示例分析
    这篇文章主要介绍PHP底层内核源码之变量zend_string的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!我们主要通读了_zval_struct  来深入了解 PHP7以上版本的 变量实现和内存...
    99+
    2023-06-15
  • Linux五大模块内核源码以及内核整体架构设计的示例分析
    小编给大家分享一下Linux五大模块内核源码以及内核整体架构设计的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!一、前言本文是“Linux内核源码分析”系...
    99+
    2023-06-29
  • Hadoop体系结构之HDFS的示例分析
    这篇文章将为大家详细讲解有关Hadoop体系结构之HDFS的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。    HDFS采用主从(Master/Slave)结构模型,一个HD...
    99+
    2023-06-03
  • PHP常量和变量之变量引用的示例分析
    小编给大家分享一下PHP常量和变量之变量引用的示例分析,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!关于PHP常量和变量之变量引用分别写两段代码,如下所示:<php$fo =8;//$fo的值为8,将8赋值...
    99+
    2023-06-15
  • PHP数据结构之图存储结构的示例分析
    这篇文章主要介绍PHP数据结构之图存储结构的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!图的存储结构图的概念介绍得差不多了,大家可以消化消化再继续学习后面的内容。如果没有什么问题的话,我们就继续学习接下来的...
    99+
    2023-06-20
  • java之JVM字节码结构的示例分析
    这篇文章主要介绍了java之JVM字节码结构的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。常用的java框架有哪些1.SpringMVC,Spring Web MV...
    99+
    2023-06-14
  • C语言汇编分析传递结构体指针比传递结构体变量高效的深层原因
    目录前言传递结构体变量传递结构体指针总结前言 先声明下观点:当有少量结构体成员时,传递结构体指针和结构体变量的差距不大;当有大量结构体成员时,随着成员越来越多,传递指针的效率也越来越...
    99+
    2022-11-13
    C语言汇编分析 C语言传递结构体指针 C语言传递结构体变量
  • Spring源码解析之推断构造方法的示例分析
    小编给大家分享一下Spring源码解析之推断构造方法的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!Spring推断构造方法贴个测试代码直接开干,这只是个...
    99+
    2023-06-15
  • MySql优化之体系结构及存储引擎的示例分析
    这篇文章给大家分享的是有关MySql优化之体系结构及存储引擎的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。一、MySQL结构体系总体上, 我们可以把 MySQL 分成三...
    99+
    2024-04-02
  • C语言中使用结构体计算内存占用的示例分析
    小编给大家分享一下C语言中使用结构体计算内存占用的示例分析,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!    c语言中结构体使用是非常广泛的,但是结构体有一个问题,就是如果开头的字段属性是字符类型(c...
    99+
    2023-06-20
  • redis内部数据结构之SDS简单动态字符串的示例分析
    小编给大家分享一下redis内部数据结构之SDS简单动态字符串的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!前言rei...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作