共计 3902 个字符,预计需要花费 10 分钟才能阅读完成。
通过 GDB, 来简单明了的理解写时复制(copy to write):
操作环境:
PHP 7.3.7 | |
GNU gdb (GDB) 8.3 |
通过如下实例来操作一遍:
<?php | |
$a = 'hello'; | |
echo $a; | |
$b = $a; | |
echo $a; | |
echo $b; | |
$b = 'world'; | |
echo $a; | |
echo $b; | |
/* | |
result: | |
root@MacBook-Pro-3:/home/code/php-demo# php73 string.php | |
hellohellohellohelloworld | |
*/ |
该结果没什么异议,下面我们来分三部分 $a = 'hello';
$b = $a;
$b = 'world';
来看下底层变量的结构变化:
第一部分:
$a = 'hello'; | |
/* | |
(gdb) p z | |
$1 = (zval *) 0x102022080 | |
(gdb) p *z | |
$2 = {value = {lval = 4328543936, dval = 2.1385848552920513e-314, counted = 0x1020056c0, | |
str = 0x1020056c0, arr = 0x1020056c0, obj = 0x1020056c0, res = 0x1020056c0, ref = 0x1020056c0, | |
ast = 0x1020056c0, zv = 0x1020056c0, ptr = 0x1020056c0, ce = 0x1020056c0, func = 0x1020056c0, ww = {w1 = 33576640, w2 = 1}}, u1 = {v = {type = 6 '\006', type_flags = 0 '\000', u = {call_info = 0, | |
extra = 0}}, type_info = 6}, 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}} | |
(gdb) p $2.value.str | |
$3 = (zend_string *) 0x1020056c0 | |
(gdb) p *$2.value.str | |
$4 = {gc = {refcount = 1, u = {type_info = 70}}, h = 9223372247569412249, len = 5, val = "h"} | |
(gdb) p *$2.value.str.val@5 | |
$5 = "hello" | |
*/ | |
echo $a; |
变量 a 初始结构示意图:
第二部分:
$b = $a; | |
/* | |
(gdb) p z | |
$6 = (zval *) 0x102022080 | |
(gdb) p *z | |
$7 = {value = {lval = 4328543936, dval = 2.1385848552920513e-314, counted = 0x1020056c0, | |
str = 0x1020056c0, arr = 0x1020056c0, obj = 0x1020056c0, res = 0x1020056c0, ref = 0x1020056c0, | |
ast = 0x1020056c0, zv = 0x1020056c0, ptr = 0x1020056c0, ce = 0x1020056c0, func = 0x1020056c0, ww = {w1 = 33576640, w2 = 1}}, u1 = {v = {type = 6 '\006', type_flags = 0 '\000', u = {call_info = 0, | |
extra = 0}}, type_info = 6}, 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}} | |
(gdb) p $7.value.str | |
$8 = (zend_string *) 0x1020056c0 | |
(gdb) p *$7.value.str | |
$9 = {gc = {refcount = 1, u = {type_info = 70}}, h = 9223372247569412249, len = 5, val = "h"} | |
(gdb) p *$7.value.str.val@5 | |
$10 = "hello" | |
*/ | |
echo $a; | |
/* | |
(gdb) p z | |
$11 = (zval *) 0x102022090 | |
(gdb) p *z | |
$12 = {value = {lval = 4328543936, dval = 2.1385848552920513e-314, counted = 0x1020056c0, | |
str = 0x1020056c0, arr = 0x1020056c0, obj = 0x1020056c0, res = 0x1020056c0, ref = 0x1020056c0, | |
ast = 0x1020056c0, zv = 0x1020056c0, ptr = 0x1020056c0, ce = 0x1020056c0, func = 0x1020056c0, ww = {w1 = 33576640, w2 = 1}}, u1 = {v = {type = 6 '\006', type_flags = 0 '\000', u = {call_info = 0, | |
extra = 0}}, type_info = 6}, 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}} | |
(gdb) p *$12.value.str | |
$13 = {gc = {refcount = 1, u = {type_info = 70}}, h = 9223372247569412249, len = 5, val = "h"} | |
(gdb) p *$12.value.str.val@5 | |
$14 = "hello" | |
*/ | |
echo $b; |
变量 a 赋值给变量 b 时,两者共用一个_zend_string 结构体,如下图:
第三部分:
$b = 'world'; | |
/* | |
(gdb) p z | |
$17 = (zval *) 0x102022080 | |
(gdb) p *z | |
$18 = {value = {lval = 4328543936, dval = 2.1385848552920513e-314, counted = 0x1020056c0, | |
str = 0x1020056c0, arr = 0x1020056c0, obj = 0x1020056c0, res = 0x1020056c0, ref = 0x1020056c0, | |
ast = 0x1020056c0, zv = 0x1020056c0, ptr = 0x1020056c0, ce = 0x1020056c0, func = 0x1020056c0, ww = {w1 = 33576640, w2 = 1}}, u1 = {v = {type = 6 '\006', type_flags = 0 '\000', u = {call_info = 0, | |
extra = 0}}, type_info = 6}, 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}} | |
(gdb) p $18.value.str | |
$19 = (zend_string *) 0x1020056c0 | |
(gdb) p *$18.value.str | |
$20 = {gc = {refcount = 1, u = {type_info = 70}}, h = 9223372247569412249, len = 5, val = "h"} | |
(gdb) p *$18.value.str.val@5 | |
$21 = "hello" | |
*/ | |
echo $a; | |
/* | |
(gdb) p z | |
$22 = (zval *) 0x102022090 | |
(gdb) p *z | |
$23 = {value = {lval = 4328544000, dval = 2.1385848869122527e-314, counted = 0x102005700, | |
str = 0x102005700, arr = 0x102005700, obj = 0x102005700, res = 0x102005700, ref = 0x102005700, | |
ast = 0x102005700, zv = 0x102005700, ptr = 0x102005700, ce = 0x102005700, func = 0x102005700, ww = {w1 = 33576704, w2 = 1}}, u1 = {v = {type = 6 '\006', type_flags = 0 '\000', u = {call_info = 0, | |
extra = 0}}, type_info = 6}, 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}} | |
(gdb) p $23.value.str | |
$24 = (zend_string *) 0x102005700 | |
(gdb) p *$23.value.str | |
$25 = {gc = {refcount = 1, u = {type_info = 70}}, h = 9223372247587566957, len = 5, val = "w"} | |
(gdb) p *$23.value.str.val@5 | |
$26 = "world" | |
*/ | |
echo $b; |
当变量 b 重新赋值时,会启用一个新的_zend_string 结构体,如下图:
以上就是一个简单的写时复制的实例, 这样来理解写时复制是不是会容易很多!
更多内容见个人网站
正文完