您好,欢迎来到三六零分类信息网!老站,搜索引擎当天收录,欢迎发信息
免费发信息
三六零分类信息网 > 德宏分类信息网,免费分类信息发布

变量在 PHP7 内部的实现(二),_PHP教程

2024/4/14 13:36:45发布43次查看
变量在 php7 内部的实现(二),在上篇文章给大家介绍了变量在 php7 内部的实现(一),本篇继续给大家介绍php7内部实现相关知识,感兴趣的朋友通过本篇文章一起学习吧。
本文第一部分和第二均翻译自nikita popov(nikic,php 官方开发组成员,柏林科技大学的学生) 的 博客 。为了更符合汉语的阅读习惯,文中并不会逐字逐句的翻译。
要理解本文,你应该对 php5 中变量的实现有了一些了解,本文重点在于解释 php7 中 zval 的变化。
第一部分讲了 php5 和 php7 中关于变量最基础的实现和变化。这里再重复一下,主要的变化就是 zval 不再单独分配内存,不自己存储引用计数。整型浮点型等简单类型直接存储在 zval 中。复杂类型则通过指针指向一个独立的结构体。
复杂的 zval 数据值有一个共同的头,其结构由 zend_refcounted 定义:
struct _zend_refcounted { uint32_t refcount; union { struct { zend_endian_lohi_3( zend_uchar type, zend_uchar flags, uint16_t gc_info) } v; uint32_t type_info; } u;};
这个头存储有 refcount (引用计数),值的类型 type 和循环回收的相关信息 gc_info 以及类型标志位 flags 。
接下来会对每种复杂类型的实现单独进行分析并和 php5 的实现进行比较。引用虽然也属于复杂类型,但是上一部分已经介绍过了,这里就不再赘述。另外这里也不会讲到资源类型(因为作者觉得资源类型没什么好讲的)。
字符串
php7 中定义了一个新的结构体 zend_string 用于存储字符串变量:
struct _zend_string { zend_refcounted gc; zend_ulong h; /* hash value */ size_t len; char val[1];};
除了引用计数的头以外,字符串还包含哈希缓存 h ,字符串长度 len 以及字符串的值 val 。哈希缓存的存在是为了防止使用字符串做为 hashtable 的 key 在查找时需要重复计算其哈希值,所以这个在使用之前就对其进行初始化。
如果你对 c 语言了解的不是很深入的话,可能会觉得 val 的定义有些奇怪:这个声明只有一个元素,但是显然我们想存储的字符串偿付肯定大于一个字符的长度。这里其实使用的是结构体的一个『黑』方法:在声明数组时只定义一个元素,但是实际创建 zend_string 时再分配足够的内存来存储整个字符串。这样我们还是可以通过 val 访问完整的字符串。
当然这属于非常规的实现手段,因为我们实际的读和写的内容都超过了单字符数组的边界。但是 c 语言编译器却不知道你是这么做的。虽然 c99 也曾明确规定过支持『柔性数组』,但是感谢我们的好朋友微软,没人能在不同的平台上保证 c99 的一致性(所以这种手段是为了解决 windows 平台下柔性数组的支持问题)。
新的字符串类型的结构比原生的 c 字符串更方便使用:第一是因为直接存储了字符串的长度,这样就不用每次使用时都去计算。第二是字符串也有引用计数的头,这样也就可以在不同的地方共享字符串本身而无需使用 zval。一个经常使用的地方就是共享 hashtable 的 key。
但是新的字符串类型也有一个很不好的地方:虽然可以很方便的从 zend_string 中取出 c 字符串(使用 str->val 即可),但反过来,如果将 c 字符串变成 zend_string 就需要先分配 zend_string 需要的内存,再将字符串复制到 zend_string 中。这在实际使用的过程中并不是很方便。
字符串也有一些特有的标志(存储在 gc 的标志位中的):
#define is_str_persistent (1<<0) /* allocated using malloc */#define is_str_interned (1<<1) /* interned string */#define is_str_permanent (1<<2) /* interned string surviving request boundary */
持久化的字符串需要的内存直接从系统本身分配而不是 zend 内存管理器(zmm),这样它就可以一直存在而不是只在单次请求中有效。给这种特殊的分配打上标记便于 zval 使用持久化字符串。在 php5 中并不是这样处理的,是在使用前复制一份到 zmm 中。
保留字符(interned strings)有点特殊,它会一直存在直到请求结束时才销毁,所以也就无需进行引用计数。保留字符串也不可重复(duplicate),所以在创建新的保留字符时也会先检查是否有同样字符的已经存在。所有 php 源码中不可变的字符串都是保留字符(包括字符串常量、变量名函数名等)。持久化字符串也是请求开始之前已经创建好的保留字符。但普通的保留字符在请求结束后会销毁,持久化字符串却始终存在。
如果使用了 opcache 的话保留字符会被存储在共享内存(shm)中这样就可以在所有 php 进程质检共享。这种情况下持久化字符串也就没有存在的意义了,因为保留字符也是不会被销毁的。
数组
因为 之前的文章 有讲过新的数组实现,所以这里就不再详细描述了。虽然最近有些变化导致之前的描述不是十分准确了,但是基本的概念还是一致的。
这里要说的是之前的文章中没有提到的数组相关的概念:不可变数组。其本质上和保留字符类似:没有引用计数且在请求结束之前一直存在(也可能在请求结束之后还存在)。
因为某些内存管理方便的原因,不可变数组只会在开启 opcache 时会使用到。我们来看看实际使用的例子,先看以下的脚本:
some zval| +---> cv_ptr[1] --> some zval+-----> cv_ptr[2] --> some zval
当需要使用符号表时存储 zval* 的中间表其实是没有用到的而 zval** 指针会被更新到 hashtable buckets 的响应位置中。我们假定有 $a 、 $b 和 $c 三个变量,下面是简单的示意图:
cv_ptr_ptr[0] --> symboltable[a].pdataptr --> some zvalcv_ptr_ptr[1] --> symboltable[b].pdataptr --> some zvalcv_ptr_ptr[2] --> symboltable[c].pdataptr --> some zval
但是 php7 的用法中已经没有这个问题了,因为 php7 中的 hashtable 大小发生变化时 hashtable bucket 就失效了。所以 php7 用了一个相反的策略:为了访问 cv 表中存储的变量,符号表中存储 indirect 来指向 cv 表。cv 表在符号表的生命周期内不会重新分配,所以也就不会存在有无效指针的问题了。
所以加入你有一个函数并且在 cv 表中有 $a 、 $b 和 $c ,同时还有一个动态分配的变量 $d ,符号表的结构看起来大概就是这个样子:
symboltable[a].value = indirect --> cv[0] = long 42symboltable[b].value = indirect --> cv[1] = double 42.0symboltable[c].value = indirect --> cv[2] = string --> zend_string(42)symboltable[d].value = array --> zend_array([4, 2])
间接 zval 也可以是一个指向 is_undef 类型 zval 的指针,当 hashtable 没有和它关联的 key 时就会出现这种情况。所以当使用 unset($a) 将 cv[0] 的类型标记为 undef 时就会判定符号表不存在键值为 a 的数据。
常量和 ast
还有两个需要说一下的在 php5 和 php7 中都存在的特殊类型 is_constant 和 is_constant_ast 。要了解他们我们还是先看以下的例子:
<?phpfunction test($a = answer, $b = answer * answer) { return $a + $b;}define('answer', 42);var_dump(test()); // int(42 + 42 * 42)·
test() 函数的两个参数的默认值都是由常量 answer 构成,但是函数声明时常量的值尚未定义。常量的具体值只有通过 define() 定义时才知道。
由于以上问题的存在,参数和属性的默认值、常量以及其他接受『静态表达式』的东西都支持『延时绑定』直到首次使用时。
常量(或者类的静态属性)这些需要『延时绑定』的数据就是最常需要用到 is_constant 类型 zval 的地方。如果这个值是表达式,就会使用 is_constant_ast 类型的 zval 指向表达式的抽象语法树(ast)。
到这里我们就结束了对 php7 中变量实现的分析。后面我可能还会写两篇文章来介绍一些虚拟机优化、新的命名约定以及一些编译器基础结构的优化的内容(这是作者原话)。
您可能感兴趣的文章:迁移php版本到php7php7.0版本备注在mac上编译安装php7的开发环境php7.0安装笔记整理浅谈php7的重大新特性深入浅析php7.0新特征(五大新特征)php7正式版测试,性能惊艳!变量在 php7 内部的实现(一)
http://www.bkjia.com/phpjc/1084582.htmlwww.bkjia.comtruehttp://www.bkjia.com/phpjc/1084582.htmltecharticle变量在 php7 内部的实现(二), 在上篇文章给大家介绍了变量在 php7 内部的实现(一),本篇继续给大家介绍php7内部实现相关知识,感兴...
德宏分类信息网,免费分类信息发布

VIP推荐

免费发布信息,免费发布B2B信息网站平台 - 三六零分类信息网 沪ICP备09012988号-2
企业名录