Skip to content

修改别字 #28

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
````

####REFERENCE
上一章说过引用(REFERENCE)在PHP5的时候是一个标志位, 而在PHP7以后我们把它变成了一种新的类型:`IS_REFERNCE`. 然而引用是一种很常见的应用, 所以这个变化带来了很多的变化, 也给我们在做PHP7开发的时候, 因为有的时候疏忽忘了处理这个类型, 而带来不少的bug.
上一章说过引用(REFERENCE)在PHP5的时候是一个标志位, 而在PHP7以后我们把它变成了一种新的类型:`IS_REFERENCE`. 然而引用是一种很常见的应用, 所以这个变化带来了很多的变化, 也给我们在做PHP7开发的时候, 因为有的时候疏忽忘了处理这个类型, 而带来不少的bug.

最简单的情况, 就是在处理各种类型的时候, 从此以后我们要多考虑这种新的类型, 比如在PHP7中, 这样的代码形式就变得很常见了:
````c
try_again:
swtich (Z_TYPE_P(zv)) {
case IS_TRING:
case IS_STRING:
break;
case IS_ARRAY:
break;
Expand All @@ -31,15 +31,15 @@ swtich (Z_TYPE_P(zv)) {

一句话来说, 就是我们不得不这么做. -_#

前面说到, Hashtable直接存储的是zval, 这样在符号表中, 俩个zval如何共用一个数值呢? 对于字符串等复杂类型来说还好, 我们貌似可以在`zend_refcounted`结构中加入一个标志位来表明是引用来解决, 然而这个也会遇到Change On Write带来的复制, 但是我们知道在PHP7中, 一些类型是直接存储在zval中的, 比如`IS_LONG`, 但是引用类型是需要引用计数的, 那么对于一个是`IS_LONG`并且又是`IS_REFERNCE`的zval该如何表示呢?
前面说到, Hashtable直接存储的是zval, 这样在符号表中, 俩个zval如何共用一个数值呢? 对于字符串等复杂类型来说还好, 我们貌似可以在`zend_refcounted`结构中加入一个标志位来表明是引用来解决, 然而这个也会遇到Change On Write带来的复制, 但是我们知道在PHP7中, 一些类型是直接存储在zval中的, 比如`IS_LONG`, 但是引用类型是需要引用计数的, 那么对于一个是`IS_LONG`并且又是`IS_REFERENCE`的zval该如何表示呢?

为此, 我们创造了这个新的类型:

![IS_REFERNCE](/img/reference.png)
![IS_REFERENCE](/img/reference.png)

如图所示, 引用是一种新的类型:`zend_reference`, 对于`IS_REFERNCE`类型的zval, `zval.value.ref`是一个指向`zend_reference`的指针, 它包含了引用计数和一个zval, 具体的zval的值是存在`zval.value.ref->val`中的.
如图所示, 引用是一种新的类型:`zend_reference`, 对于`IS_REFERENCE`类型的zval, `zval.value.ref`是一个指向`zend_reference`的指针, 它包含了引用计数和一个zval, 具体的zval的值是存在`zval.value.ref->val`中的.

所以对于`IS_LONG`的引用来说, 就用一个类型是`IS_REFERNCE`的zval, 它指向一个`zend_reference`, 而这个`zend_reference->val`中是一个类型为`IS_LONG`的zval.
所以对于`IS_LONG`的引用来说, 就用一个类型是`IS_REFERENCE`的zval, 它指向一个`zend_reference`, 而这个`zend_reference->val`中是一个类型为`IS_LONG`的zval.

####Change On Write
PHP采用引用计数来做简单的垃圾回收, 考虑如下的代码:
Expand All @@ -52,7 +52,7 @@ swtich (Z_TYPE_P(zv)) {
````
`$ref`和`$val`是指向同一个zval的引用, 在PHP5的时候, 我们是通过一个引用计数为2, 并且引用标志位为1来表示这种情况, 当把`$val`复制给`$copy`(line 3)的时候, 我们发现$val是一个计数大于1的引用, 所以要产生Change on write, 也就是分离. 所以我们需要复制这个zval.

而在PHP7中, 情况就变得简单了很多, 首先在引用赋值给`$ref`(line 2)的时候, 生成一个`IS_REFERNCE`类型, 然后因为此时有俩个变量引用它所以`zend_reference`这个结构的引用计数`zval.value.ref->gc.refcount`为2.
而在PHP7中, 情况就变得简单了很多, 首先在引用赋值给`$ref`(line 2)的时候, 生成一个`IS_REFERENCE`类型, 然后因为此时有俩个变量引用它所以`zend_reference`这个结构的引用计数`zval.value.ref->gc.refcount`为2.

再随后的赋值给`$copy`(line 3)的时候, 发现`$val`是一个引用, 于是让`$copy`指向的是`zval.value.ref->val`, 也就是字符串值为`laruence`的zval, 然后把zval的引用计数+1, 也就是`zval.value.ref->val.value.str.gc.refcount`为2. 并没有产生复制.

Expand Down
8 changes: 4 additions & 4 deletions zval.md
Original file line number Diff line number Diff line change
Expand Up @@ -422,15 +422,15 @@ IS_OBJ_HAS_GUARDS //是否有魔术方法递归保护标志
这个标记就会一直随着这个字符串的生存而存在的, 省掉了我之前的很多tricky的做法.

####ZVAL预先分配
前面我们说过, PHP5的zval分配采用的是堆上分配内存, 也就是在PHP预案代码中随处可见的MAKE_STD_ZVAL和ALLOC_ZVAL宏. 我们也知道了本来一个zval只需要24个字节, 但是算上gc_info, 其实分配了32个字节, 再加上PHP自己的内存管理在分配内存的时候都会在内存前面保留一部分信息:
前面我们说过, PHP5的zval分配采用的是堆上分配内存, 也就是在PHP源代码中随处可见的MAKE_STD_ZVAL和ALLOC_ZVAL宏. 我们也知道了本来一个zval只需要24个字节, 但是算上gc_info, 其实分配了32个字节, 再加上PHP自己的内存管理在分配内存的时候都会在内存前面保留一部分信息:
````c
typedef struct _zend_mm_block {
zend_mm_block_info info;
#if ZEND_DEBUG
unsigned int magic;
# ifdef ZTS
#ifdef ZTS
THREAD_T thread_id;
# endif
#endif
zend_mm_debug_info debug;
#elif ZEND_MM_HEAP_PROTECTION
zend_mm_debug_info debug;
Expand All @@ -447,7 +447,7 @@ typedef struct _zend_mm_block {

而这个也很容易证明, PHP脚本中使用的zval, 要么存在于符号表, 要么就以临时变量(`IS_TMP_VAR`)或者编译变量(`IS_CV`)的形式存在. 前者存在于一个Hashtable中, 而在PHP7中Hashtable默认保存的就是zval, 这部分的zval完全可以在Hashtable分配的时候一次性分配出来, 后面的存在于execute_data之后, 数量也在编译时刻确定好了, 也可以随着execute_data一次性分配, 所以我们确实不再需要单独在堆上申请zval了.

所以, 在PHP7开始, 我们移除了MAKE_STD_ZVAL/ALLOC_ZVAL宏, 不再支持存堆内存上申请zval. 函数内部使用的zval要么来自外面输入, 要么使用在栈上分配的临时zval.
所以, 在PHP7开始, 我们移除了MAKE_STD_ZVAL/ALLOC_ZVAL宏, 不再支持从堆内存上申请zval. 函数内部使用的zval要么来自外面输入, 要么使用在栈上分配的临时zval.

在后来的实践中, 总结出来的可能对于开发者来说最大的变化就是, 之前的一些内部函数, 通过一些操作获得一些信息, 然后分配一个zval, 返回给调用者的情况:
````c
Expand Down