iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > PHP编程 >PHP底层内核源码之变量zend_string的示例分析
  • 557
分享到

PHP底层内核源码之变量zend_string的示例分析

2023-06-15 11:06:18 557人浏览 泡泡鱼
摘要

这篇文章主要介绍PHP底层内核源码之变量zend_string的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!我们主要通读了_zval_struct  来深入了解 php7以上版本的 变量实现和内存

这篇文章主要介绍PHP底层内核源码之变量zend_string的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!

我们主要通读了_zval_struct  来深入了解 php7以上版本的 变量实现和内存占用

struct _zval_struct {zend_value        value; u1; u2;};

其中 zend_value 结构体的核心代码如下

typedef uNIOn _zend_value {zend_long         lval;          //整型        double            dval;          //浮点型        zend_refcounted  *counted;     //获取不同类型结构的GC头部的指针        zend_string      *str;        //string字符串 的指针        zend_array       *arr;        //数组指针        zend_object      *obj;        //object 对象指针        zend_resource    *res;         ///资源类型指针        zend_reference   *ref;       //引用类型指针   比如你通过&$c  定义的        zend_ast_ref     *ast;     // ast 指针  线程安全 相关的 内核使用的          zval             *zv;   // 指向另外一个zval的指针  内核使用的        void             *ptr;   //指针  ,通用类型  内核使用的        zend_class_entry *ce;    //类 ,内核使用的        zend_function    *func;   // 函数 ,内核使用的        struct {         uint32_t w1;//自己定义的。 无符号的32位整数         uint32_t w2;//同上         } ww; } zend_value;

可以看出常用的 zend_value包含 上面几种 会不会有个疑问   怎么没有布尔型呢?

其实这里这里的 zend_value 只是负责存储 内容   同样你也会发现 也没有null类型

再次回去打开 zend_types.h

[root@2890cf458ee2 Zend]# vim zend_types.h#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#define IS_CONSTANT_AST11#define IS_INDIRECT             13#define IS_PTR14#define IS_ALIAS_PTR15#define _IS_ERROR15#define _IS_BOOL16#define IS_CALLABLE17#define IS_ITERABLE18#define IS_VOID19#define _IS_NUMBER20

可以看到 在代码里 定义了 20种类型  其中前11种 是常用类型 后面的类型包含ast和 internal 等 不常用  后面到内存管理 会依次展开 ast和 internal的使用

言归正传   在PHP中 管理字符串会使用zend_string。每次 PHP 需要使用字符串时,都会使用zend_string结构,   PHP没有用原生C语言的 char 而是封装了个结构体

[root@2890cf458ee2 Zend]# vim zend_types.h
  82 typedef struct _zend_object_handlers zend_object_handlers;  83 typedef struct _zend_class_entry     zend_class_entry;  84 typedef union  _zend_function        zend_function;  85 typedef struct _zend_execute_data    zend_execute_data;  86  87 typedef struct _zval_struct     zval;  88  89 typedef struct _zend_refcounted zend_refcounted;  90 typedef struct _zend_string     zend_string;  91 typedef struct _zend_array      zend_array;  92 typedef struct _zend_object     zend_object;  93 typedef struct _zend_resource   zend_resource;  94 typedef struct _zend_reference  zend_reference;  95 typedef struct _zend_ast_ref    zend_ast_ref;  96 typedef struct _zend_ast        zend_ast;

在第90行看到 zend_string实际上是_zend_string的别名

别名是c语言特有的一种 形式

继续跟到第235行 看到了 _zend_string是一个结构体    

struct _zend_string {zend_refcounted_h gc;zend_ulong        h;                size_t            len;char              val[1];};

这个结构体包含 4个部分  

其中 有gc  (这显然又是一个自定义类型 )  h(也是一个自定义类型) len (整型)   val[1](字符串类型,但是这个名字怎么怪怪的)。

我们继续跟gc 这个类型

typedef struct _zend_refcounted_h {uint32_t         refcount;union {uint32_t type_info;} u;} zend_refcounted_h;

可以看到 zend_refcounted_h 是 _zend_refcounted_h结构体的别名

这个结构体 包括 一个 32位纯数字的 refcount   和一个联合体u  联合体u里面包括一个 type_info       zend_refcounted_h 占用8字节  ,refount英文翻译成中文是引用的意思  显然 这个 zend_refcounted_h是为了引用计数和字符串类别存储用的。

引用计数存放在refcount字段、字符串所属的变量类别则存储在type字段。zend_string结构体中因为加入了gc字段,使得其和数组、对象一样可被多个zval引用 这非常巧妙了。

[root@2890cf458ee2 Zend]# vim zend_types.h[root@2890cf458ee2 Zend]# php -vPHP 7.4.15 (cli) (built: Feb 22 2021 08:46:50) ( NTS )Copyright (c) The PHP GroupZend Engine v3.4.0, Copyright (c) Zend Technologies****************************************我的版本为 7.4.15 你如果看过其他大佬做的源码文章会发现跟我这个版本的_zend_refcounted_h结构体有所不同 ,比如 陈雷大佬的书中 的_zend_refcounted_h结构体会包含一个联合体 联合体里面又有用于垃圾回收颜色用的 gc_info 等 *************************************

个人认为是因为 zend_zval 的u1 已经包含了 type_flags type 等字段  所以在PHP7.4版本里zend_refcounted_h   就弃用了这些值

在 zend_string结构体 第二个值      h  指向了zend_ulong

通过追踪代码  发现 zendulong    在  zend_long.h 中

PHP底层内核源码之变量zend_string的示例分析

h是typedef uint64_t zend_ulong类型的一个变量,保存字符串对应的哈希值,其后续会用在数组里面。他占用8个字节

我们把 zend_string 加上注释

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

len和val[1]用于标识字符串,c语言中字符串的表示形式可以以\0结尾,通过遍历得到字符串长度,但是其非二进制安全,如字符串中本身就包含\0,那么该字符串\0后面的字符串会被截断,这里len用于保存字符串的长度, val是一个柔性数组。实现的字符串是二进制安全的。

关于\0 可以看以下 c语言代码

main(){ char a[] = "aa\0"; char b[] = "aa\0aaaaaaaaaaaaaaaaaa";        printf(strlen(a));    printf(strlen(b)); }

运行结果为  2    2

也就是说C语言认为a和b这两个字符串是相等的,而且ab的长度为都为2

但是在PHP中因为有了zend_string的存在 可以做到二进制安全

例如,字符串 “foo” 在zend_string中存储为 “foo\0”,且它的长度为3。另外,字符串 “foo\0bar” 将存储为 “foo\0bar\0”,且其长度为7。

至于什么是柔性数组  参考Goole搜的介绍

1、什么是柔性数组?柔性数组既数组大小待定的数组, C语言中结构体的最后一个元素可以是大小未知的数组,也就是所谓的0长度,所以我们可以用结构体来创建柔性数组。2、柔性数组有什么用途 ?它的主要用途是为了满足需要变长度的结构体,为了解决使用数组时内存的冗余和数组的越界问题。3、用法 :在一个结构体的最后 ,申明一个长度为空的数组,就可以使得这个结构体是可变长的。对于编译器来说,此时长度为0的数组并不占用空间,因为数组名本身不占空间,它只是一个偏移量, 数组名这个符号本身代 表了一个不可修改的地址常量 (注意:数组名永远都不会是指针! ),但对于这个数组的大小,我们可以进行动态分配,对于编译器而言,数组名仅仅是一个符号,它不会占用任何空间,它在结构体中,只是代表了一个偏移量,代表一个不可修改的地址常量!对于柔性数组的这个特点,很容易构造出变成结构体,如缓冲区,数据包等等

用柔性数组的好处很明显,读写字符串值时可以省一次内存读写

那为什么不用val[0] 或者var[] 而是var[1] 呢 因为 为了兼容c99的标准 c99里不允许变长数组的定义,但是支持var[1] 你可以理解为 为了兼容不同版本的c编译器即可。

len字段是记录 字符串的长度 跟上面的柔性数组一配合就知道 字符串的真实长度了 读取的数据长度以自身结构体len值为准。同时这也是典型的空间换时间算法 也节省了还要去计算字符串的长度的消耗。

所以 zend_string 结构体整体占用 25个字节 但是因为内存对齐 所以占用32个字节

以上你已经掌握了 字符串 结构体的 基础知识

在PHP中 封装了很多 操作字符串的基础宏  一般在 zend_string.h 中

下面这行代码 php是怎么实现的?

其实整个过程是

PHP底层内核源码之变量zend_string的示例分析

(先不要考虑 词法分析 语法分析 AST 等过程)

<?php  $str = 'PHP';  printf("字符串内容为".$str);  printf("字符串长度为".strlen($str));?>

其实对应的 ‘伪代码’如下

zend_string *s;zend_string_init(s,"PHP", strlen("PHP"), 0) // 其中 zend_string_init 为初始化一个普通字符串 s// 存储字符串到s 到变量 zval a 中 ZVAL_STR(&a, s);php_printf("子字符串内容为", Z_STRVAL(a));php_printf("字符串长度为", Z_STRLEN(a));zend_string_release(a);

zend_string_init()函数(实际上是宏)计算完整的char *字符串和它的长度。最后一个参数的类型为 int 值为 0 或 1。如果传0,则通过 Zend 内存管理使用请求绑定的堆分配。这种分配在当前请求结束后时销毁。如果不销毁,内存就会泄漏。如果传1,则要求了所谓的“持久”分配,将使用传统的 C语言的malloc()调用。

说人话就是zend_string_init函数把一个普通字符串初始化成zend_string

在zend_string.h 中 第152行 可以找到

 //上述我们传进来  zend_string_init("PHP", 3, 0);static zend_always_inline zend_string *zend_string_init(const char *str, size_t len, int persistent){        //分配内存及初始化 初始化内存的值zend_string *ret = zend_string_alloc(len, persistent);       //拷贝 str 到 zend_string 中的val中 memcpy(ZSTR_VAL(ret), str, len);      //把字符串末尾加上\0 毕竟要依赖c语言 所以最最底层要按照人家规则走ZSTR_VAL(ret)[len] = '\0';return ret;}

zend_string_init 第一步 又调用了 zend_string_alloc   然后进行 memcpy   执行ZSTR_VAL

最后返回一个 字符串变量

下面是zend_string_alloc的代码

static zend_always_inline zend_string *zend_string_alloc(size_t len, int persistent){zend_string *ret = (zend_string *)pemalloc(ZEND_MM_ALIGNED_SIZE(_ZSTR_STRUCT_SIZE(len)), persistent);GC_SET_REFCOUNT(ret, 1);GC_TYPE_INFO(ret) = IS_STRING | ((persistent ? IS_STR_PERSISTENT : 0) << GC_FLAGS_SHIFT);ZSTR_H(ret) = 0;ZSTR_LEN(ret) = len;return ret;}

这个宏代码主要是申请一块连续的内存,内存的大小的计算公式为:实际申请大小= 结构体的大小(24) + 字符串的长度(len)+1,实际申请大小是按照8字节对齐的,不一定等于实际计算的结果。  len = string.len + new_str_len + string_struct_len + 1

这个+1就是为了追加 \0 使用的

并且还做了初始化 zend_string 工作

//这是个宏  设置 zend_string 中的 h值    还记得h值是干嘛的吗?        ZSTRH(ret) = 0;  //这是个宏 设置 zend_string 中的len的值  ZSTR_LEN(ret) = len;

然后进行memcpy 函数

C 库函数 中的memcpy()void *memcpy(void *str1, const void *str2, size_t n)参数str1 -- 指向用于存储复制内容的目标数组,类型强制转换为 void* 指针。str2 -- 指向要复制的数据源,类型强制转换为 void* 指针。n -- 要被复制的字节数。返回值该函数返回一个指向目标存储区 str1 的指针

memcpy主要用于拷贝数据 里面包含了一个宏 ZSTR_VAL

这个宏是设置zend_string的val中数据

通过阅读源码我们可以发现以ZSTR_***(s)开头的每个宏都会作用到 zend_string。ZSTR_VAL()   访问字符数组 ZSTR_LEN()  访问长度信息 ZSTR_HASH() 访问哈希值…以 Z_STR**(z) 开头的宏都会作用于到 zval 中的 zend_string 。Z_STRVAL() Z_STRLEN()Z_STRHASH()…

这样就开辟了一个字符串 值为 "PHP"

下一步又是一个宏  zend_string_release

static zend_always_inline void zend_string_release(zend_string *s){if (!ZSTR_IS_INTERNED(s)) {if (GC_DELREF(s) == 0) {pefree(s, GC_FLAGS(s) & IS_STR_PERSISTENT);}}}

显然是用于释放内存的

关于zend_string 的宏 可以参考以下注释 (慢慢会依次展开讲解)

PHP底层内核源码之变量zend_string的示例分析

接下来的小节我们将继续 分析zend_string 的写时赋值 和 内存管理 以及字符串的各种操作的实现。所以你务必吸收上面的内容 并且打开源码进行查看。

以上是“PHP底层内核源码之变量zend_string的示例分析”这篇文章的所有内容,感谢各位的阅读!希望分享的内容对大家有帮助,更多相关知识,欢迎关注编程网PHP编程频道!

--结束END--

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

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

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

本篇文章演示代码以及资料文档资料下载

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

下载Word文档
猜你喜欢
  • PHP底层内核源码之变量zend_string的示例分析
    这篇文章主要介绍PHP底层内核源码之变量zend_string的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!我们主要通读了_zval_struct  来深入了解 PHP7以上版本的 变量实现和内存...
    99+
    2023-06-15
  • PHP底层内核源码之变量的示例分析
    小编给大家分享一下PHP底层内核源码之变量的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!PHP变量的四个基本特征:1.变量命名变量命名上,PHP继承了P...
    99+
    2023-06-15
  • PHP底层内核源码之变量zend_zval结构体的示例分析
    小编给大家分享一下PHP底层内核源码之变量zend_zval结构体的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!zend_string的 结构体 的源码...
    99+
    2023-06-15
  • iOS底层allocinitnew源码流程示例分析
    目录alloc&init 的源码流程图Init 源码探索new 的源码探索alloc&init 的源码流程图 首先创建Person 类, 在main函数创建Per...
    99+
    2022-12-25
    iOS 底层alloc init new分析 iOS alloc init
  • PHP常量和变量之变量引用的示例分析
    小编给大家分享一下PHP常量和变量之变量引用的示例分析,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!关于PHP常量和变量之变量引用分别写两段代码,如下所示:<php$fo =8;//$fo的值为8,将8赋值...
    99+
    2023-06-15
  • Java SpringBoot核心源码的示例分析
    本篇文章给大家分享的是有关Java SpringBoot核心源码的示例分析,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。SpringBoot源码主线分析我们要分析一个...
    99+
    2023-06-22
  • Java源码解析之ConcurrentHashMap的示例分析
    小编给大家分享一下Java源码解析之ConcurrentHashMap的示例分析,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!早期 ConcurrentHashMap,其实现是基于:分离锁,也就是将内部进行分段(Segme...
    99+
    2023-06-15
  • python源码剖析之PyObject的示例分析
    这篇文章主要介绍python源码剖析之PyObject的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!一、Python中的对象Python中一切皆是对象。————Guido van Rossum(1989)这...
    99+
    2023-06-15
  • PHP gPRC 源码分析:深入了解 gPRC 的底层原理
    gRPC 简介 gRPC 是一种现代 RPC 框架,它建立在 HTTP/2 和 Protocol Buffers 之上。它提供了高性能、低延迟的 RPC 服务,广泛应用于各种系统中。 PHP gPRC 源码分析 PHP gPRC 源码托管...
    99+
    2024-02-19
    PHP gRPC 源码分析 底层原理
  • Vue源码分析之虚拟DOM的示例分析
    小编给大家分享一下Vue源码分析之虚拟DOM的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!为什么需要虚拟dom?虚拟DOM就是为了解决浏览器性能问题而被...
    99+
    2023-06-15
  • PHP语法和PHP变量的示例分析
    这篇文章给大家分享的是有关PHP语法和PHP变量的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。PHP 语法PHP 脚本在服务器上执行,然后将纯 HTML 结果发送回浏览器。。。。。。。。。。。。。。。基...
    99+
    2023-06-04
  • Linux五大模块内核源码以及内核整体架构设计的示例分析
    小编给大家分享一下Linux五大模块内核源码以及内核整体架构设计的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!一、前言本文是“Linux内核源码分析”系...
    99+
    2023-06-29
  • Android框架之Volley源码的示例分析
    这篇文章主要介绍Android框架之Volley源码的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!Volley简单使用我这里是以依赖架包的形式 ,大家也可以以gradle的形式进行依赖。好了,接下来上代码了...
    99+
    2023-06-15
  • webpack源码之loader机制的示例分析
    这篇文章主要介绍webpack源码之loader机制的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!loader概念loader是用来加载处理各种形式的资源,本质上是一个函数...
    99+
    2024-04-02
  • Android框架之OkHttp3源码的示例分析
    这篇文章将为大家详细讲解有关Android框架之OkHttp3源码的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。OkHttp流程图OkHttp基本使用gradle依赖implementation...
    99+
    2023-06-15
  • Java源码解析之接口Collection的示例分析
    小编给大家分享一下Java源码解析之接口Collection的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!一、图示二、方法定义我们先想一想,公司如果要我...
    99+
    2023-06-15
  • Vue.js源码分析之自定义指令的示例分析
    这篇文章给大家分享的是有关Vue.js源码分析之自定义指令的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。前言除了核心功能默认内置的指令 (v-model 和 v-show),Vue 也允许注册自定义指令...
    99+
    2023-06-14
  • JS中内存与变量存储的示例分析
    这篇文章将为大家详细讲解有关JS中内存与变量存储的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。JS神奇的Number案例一:金额的计算与传递18.9 * 100=1889.9...
    99+
    2023-06-20
  • Java并发编程之ConcurrentLinkedQueue源码的示例分析
    这篇文章给大家分享的是有关Java并发编程之ConcurrentLinkedQueue源码的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。一、ConcurrentLinkedQueue介绍并编程中,一般需...
    99+
    2023-06-15
  • Java基础之面向对象机制底层实现的示例分析
    这篇文章主要介绍Java基础之面向对象机制底层实现的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!Java的特点有哪些Java的特点有哪些1.Java语言作为静态面向对象编程语言的代表,实现了面向对象理论,允...
    99+
    2023-06-14
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作