通过GDB,来简单明了的理解写时复制(copy to write):
操作环境:
PHP 7.3.7GNU 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.phphellohellohellohelloworld*/
该结果没什么异议,下面我们来分三部分$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结构体,如下图:
以上就是一个简单的写时复制的实例,这样来理解写时复制是不是会容易很多!
更多内容见个人网站