php7底层源码-字符串写时复制-gdb调试过程

php7底层源码-字符串写时复制-gdb调试过程

ps: 青色的字,都是我自己在yy,   我在这个调试过程中的变量认为是这样的   
            $c zval  080;
            $a zval  090 ;  _zend_string 2d0;
            $b zval  0a0;   _zend_string 2d0 ;   //写时复制后的$b
            $b zval  0a0;   _zend_string 600;   //赋值为 hello 之后的$b
            当前gdb调试的 跟我 php输出的 不一致,请仔细往下看

    

一: 调试环境

    mac  virtualbox 安装 contos7
    php版本 7.1.0
    gdb版本 7.82 

  1.1:gdb快捷键说明

一些快捷命令

l – list
p – print print {variable}  //打印变量
c – continue           //继续执行
s – step          
b - break break line_number/break [file_name]:line_number/break [file_name]:func_name       //设置断点
r - run                    //执行文件


二: 调试的php代码, 

git地址:https://git.imooc.com/coding-312/php7internal/src/master/chapter3/string.php

<?php
//const string
$c = "hello world!";
echo $c;

$a = time()."string";
echo $a;

//copy on write
$b = $a;
echo $a;
echo $b;
$b = "hello";
echo $a;
echo $b;

三:正式调试

  3.1:目录说明:

[root@chenlei output]# pwd
/home/codes/php/php-7.1.0/output
[root@chenlei output]# ls
bin demo etc include lib php sbin var
[root@chenlei output]# tree ./demo
./demo
├── string.php


3.2:开始调试

gdb ./bin/php
(gdb) b ZEND_ECHO_SPEC_CV_HANDLER(gdb) r ./demo/string.php


调试
$c = "hello world!";
echo $c;
(gdb) n
34641 z = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
(gdb) p z
$1 = (zval *) 0x7ffff5e13080
(gdb) p *z
$2 = {value = {lval = 140737318491232, dval = 6.9533474154335748e-310, counted = 0x7ffff5e01460, str = 0x7ffff5e01460,
arr = 0x7ffff5e01460, obj = 0x7ffff5e01460, res = 0x7ffff5e01460, ref = 0x7ffff5e01460, ast = 0x7ffff5e01460,
zv = 0x7ffff5e01460, ptr = 0x7ffff5e01460, ce = 0x7ffff5e01460, func = 0x7ffff5e01460, ww = {w1 = 4125103200, w2 = 32767}},
u1 = {v = {type = 6 '\006', type_flags = 0 '\000', const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 6}, u2 = {
next = 0, cache_slot = 0, lineno = 0, num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0, property_guard = 0}}
(gdb) p z.value.str
$3 = (zend_string *) 0x7ffff5e01460
(gdb) p *z.value.str
$4 = {gc = {refcount = 0, u = {v = {type = 6 '\006', flags = 2 '\002', gc_info = 0}, type_info = 518}},
h = 15212097803322581250, len = 12, val = "h"}
(gdb) p *z.value.str.val@12
$5 = "hello world!"

(gdb) c
Continuing.
hello world!

这里调试的 $c 变量, 底层是一个zval结构体, u1.type=6 是字符串   ,接着走


调试$a  一个变量字符串

$a = time()."string";
echo $a;



(gdb) n
34641 z = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
(gdb) p z
$6 = (zval *) 0x7ffff5e13090
(gdb) p *z
$7 = {value = {lval = 140737318859472, dval = 6.9533474336270481e-310, counted = 0x7ffff5e5b2d0, str = 0x7ffff5e5b2d0,
arr = 0x7ffff5e5b2d0, obj = 0x7ffff5e5b2d0, res = 0x7ffff5e5b2d0, ref = 0x7ffff5e5b2d0, ast = 0x7ffff5e5b2d0,
zv = 0x7ffff5e5b2d0, ptr = 0x7ffff5e5b2d0, ce = 0x7ffff5e5b2d0, func = 0x7ffff5e5b2d0, ww = {w1 = 4125471440, w2 = 32767}},
u1 = {v = {type = 6 '\006', type_flags = 20 '\024', const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 5126}, u2 = {
next = 0, cache_slot = 0, lineno = 0, num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0, property_guard = 0}}
(gdb) p z.value.str
$8 = (zend_string *) 0x7ffff5e5b2d0
(gdb) p *z.value.str
$9 = {gc = {refcount = 1, u = {v = {type = 6 '\006', flags = 0 '\000', gc_info = 0}, type_info = 6}}, h = 0, len = 16, val = "1"}
(gdb) p *z.value.str.val@16
$10 = "1553221045string"

(gdb) c
Continuing.
1553221045string


$a 是一个zval, 内存地址  090;u1.type=6 字符串; _zend_string 内存地址 2d0 ; _zend_string.val=时间戳+string

c命令输出结果 1553221045string,接着调试


//copy on write
$b = $a;
echo $a;

请注意,奇怪的事情发生了,注意,我的本意是输出$a,  仔细看我gdb调试的 跟 输出的是不是同一个变量

(gdb) n
34641 z = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
(gdb) p z
$11 = (zval *) 0x7ffff5e130a0
(gdb) p *z
$12 = {value = {lval = 140737318859472, dval = 6.9533474336270481e-310, counted = 0x7ffff5e5b2d0, str = 0x7ffff5e5b2d0,
arr = 0x7ffff5e5b2d0, obj = 0x7ffff5e5b2d0, res = 0x7ffff5e5b2d0, ref = 0x7ffff5e5b2d0, ast = 0x7ffff5e5b2d0,
zv = 0x7ffff5e5b2d0, ptr = 0x7ffff5e5b2d0, ce = 0x7ffff5e5b2d0, func = 0x7ffff5e5b2d0, ww = {w1 = 4125471440, w2 = 32767}},
u1 = {v = {type = 6 '\006', type_flags = 20 '\024', const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 5126}, u2 = {
next = 0, cache_slot = 0, lineno = 0, num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0, property_guard = 0}}
(gdb) p z.value.str
$13 = (zend_string *) 0x7ffff5e5b2d0
(gdb) p *z.value.str
$14 = {gc = {refcount = 2, u = {v = {type = 6 '\006', flags = 0 '\000', gc_info = 0}, type_info = 6}}, h = 0, len = 16, val = "1"}
(gdb) p *z.value.str.val@16
$15 = "1553221045string"

(gdb) c
Continuing.
1553221045string

输出 p *z.value.str.val@16 = '时间戳+string' 
c 命令之后 输出  ‘时间戳+string’ 

$a = 090
这个 zval 是 0a0 这是一个新的zval, 这个对应的是谁? $b ? 还是谁, 我本意要输出 $a变量的,怎么不是我想象中的 $a = 090  黑人问号.jpg  ?

虽然调试结果和 输出结果都是时间戳+string ,  但是对应的 变量是同一个吗?


echo $b;    记住我本意调试的是 $b
(gdb) n
34641 z = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
(gdb) p z
$16 = (zval *) 0x7ffff5e13090
(gdb) p *z
$17 = {value = {lval = 140737318859472, dval = 6.9533474336270481e-310, counted = 0x7ffff5e5b2d0, str = 0x7ffff5e5b2d0,
arr = 0x7ffff5e5b2d0, obj = 0x7ffff5e5b2d0, res = 0x7ffff5e5b2d0, ref = 0x7ffff5e5b2d0, ast = 0x7ffff5e5b2d0,
zv = 0x7ffff5e5b2d0, ptr = 0x7ffff5e5b2d0, ce = 0x7ffff5e5b2d0, func = 0x7ffff5e5b2d0, ww = {w1 = 4125471440, w2 = 32767}},
u1 = {v = {type = 6 '\006', type_flags = 20 '\024', const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 5126}, u2 = {
next = 0, cache_slot = 0, lineno = 0, num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0, property_guard = 0}}
(gdb) p z.value.str
$18 = (zend_string *) 0x7ffff5e5b2d0
(gdb) p *z.value.str
$19 = {gc = {refcount = 2, u = {v = {type = 6 '\006', flags = 0 '\000', gc_info = 0}, type_info = 6}}, h = 0, len = 16, val = "1"}
(gdb) p *z.value.str.val@16
$20 = "1553221045string"

(gdb) c
Continuing.
1553221045string

$a 的zval 地址是 090, 
这里我本意调试 $b , 怎么gdb输出的 zval = 090,   不理解  ?
虽然调试和输出的变量都是 时间戳+string , 但这是变量$a 还是 $b, 调试和输出的变量是同一个吗?


$b 变量改变后,在输出a, 本意调试 $a
$b = "hello";
echo $a;
重点的来了,注意看,我本意调试 $a

(gdb) p z
$21 = (zval *) 0x7ffff5e130a0
(gdb) p *z
$22 = {value = {lval = 140737318884864, dval = 6.9533474348815796e-310, counted = 0x7ffff5e61600, str = 0x7ffff5e61600,
arr = 0x7ffff5e61600, obj = 0x7ffff5e61600, res = 0x7ffff5e61600, ref = 0x7ffff5e61600, ast = 0x7ffff5e61600,
zv = 0x7ffff5e61600, ptr = 0x7ffff5e61600, ce = 0x7ffff5e61600, func = 0x7ffff5e61600, ww = {w1 = 4125496832, w2 = 32767}},
u1 = {v = {type = 6 '\006', type_flags = 0 '\000', const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 6}, u2 = {next = 0,
cache_slot = 0, lineno = 0, num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0, property_guard = 0}}
(gdb) p z.value.str
$23 = (zend_string *) 0x7ffff5e61600
(gdb) p *z.value.str
$24 = {gc = {refcount = 0, u = {v = {type = 6 '\006', flags = 2 '\002', gc_info = 0}, type_info = 518}}, h = 9223372247569412249,
len = 5, val = "h"}
(gdb) p *z.value.str.val@5
$25 = "hello"

(gdb) c
Continuing.
1553221045string

zval 内存地址 0a0, 比 $a 080 ,  $b 090不一样;说明是新的 zval,
我本意是 调试$a 090 的, 难道gdb真正调试的是 改变后的 $b? 
zval.u1.type = 6;  _zend_string  内存地址  1600;跟$a,$b 的 _zend_string 对应的 2d0 ,不是同一个,
_zend_string.gc.refcount = 0 , _zend_string.u.v.type = 6; _zend_string.u.v.flags=2 说明是 常量字符串

p *z.value.str.val@5 = 'hello'   输出的 改变之后 $b 的结果
c 命令出的是  ‘时间戳+string’ 

gdb 调试结果 和 输出结果  不一致,黑人问号?? 
?

我们的最后一行php代码,本意输出 变化后 $b;
echo $b,看调结果
(gdb) n
34641 z = _get_zval_ptr_cv_undef(execute_data, opline->op1.var);
(gdb) p z
$26 = (zval *) 0x7ffff5e13090
(gdb) p *z
$27 = {value = {lval = 140737318859472, dval = 6.9533474336270481e-310, counted = 0x7ffff5e5b2d0, str = 0x7ffff5e5b2d0,
arr = 0x7ffff5e5b2d0, obj = 0x7ffff5e5b2d0, res = 0x7ffff5e5b2d0, ref = 0x7ffff5e5b2d0, ast = 0x7ffff5e5b2d0,
zv = 0x7ffff5e5b2d0, ptr = 0x7ffff5e5b2d0, ce = 0x7ffff5e5b2d0, func = 0x7ffff5e5b2d0, ww = {w1 = 4125471440, w2 = 32767}},
u1 = {v = {type = 6 '\006', type_flags = 20 '\024', const_flags = 0 '\000', reserved = 0 '\000'}, type_info = 5126}, u2 = {
next = 0, cache_slot = 0, lineno = 0, num_args = 0, fe_pos = 0, fe_iter_idx = 0, access_flags = 0, property_guard = 0}}
(gdb) p z.value.str
$28 = (zend_string *) 0x7ffff5e5b2d0
(gdb) p *z.value.str
$29 = {gc = {refcount = 1, u = {v = {type = 6 '\006', flags = 0 '\000', gc_info = 0}, type_info = 6}}, h = 0, len = 16, val = "1"}
(gdb) p *z.value.str.val@16
$30 = "1553221045string"

(gdb) c
Continuing.
hello
[Inferior 1 (process 1898) exited normally]

我们本意是调试改变后的$b。也就是 $b='hello',
而gdb调试的 zval 我们看到是 090, 也就是$a,
gdb调试的输出的也是 $a 的值,
c 命令后输出的 hello

gdb调试 和 c命令输出 结果不一致, 为什么会不一致,不理解

再次 n 命令后,
(gdb) n
The program is not being run. 调试程序结束, q退出
(gdb) q
[root@chenlei output]# pwd
/home/codes/php/php-7.1.0/output


tags: PHP,php源码分析