GDB调试PHP源码分析写时复制

2次阅读

共计 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 结构体,如下图:


以上就是一个简单的写时复制的实例, 这样来理解写时复制是不是会容易很多!


更多内容见个人网站

正文完
 0