php数据内存泄露 php数据内存泄露的原因( 四 )


那PHP的命令入口是什么?流程又是什么?
主要函数流程如下:
入口main函数(sapi/cli/php_cli.c) ==》php_execute_script(main/main.c)==zend_execute_scripts(Zend/zend.c)==execute(Zend/zend_vm_execute.h)
调用GC的地方在execute里 。
简单描述下这个过程 , 
main 是入口,它的作用是根据我们传递的参数做不同的设置,最后会把我们的php脚本作为一个zend_file_handle指针传递给
php_execute_script函数,zend_file_handle其实就是把FILE*做了一下封装,保存了一些其他的文件信息 。
php_execute_script会做一些文件检查工作 , 把php脚本加到 哈希表included_files中 。
php_execute_scripts 会执行 zend_compile_file函数来解释我们写的PHP代码 , 最后执行execute 。
应该都知道 Zend把脚本解析完会生成op代码保存到 哈希表:active_op_array中,execute会逐个执行每个op , 
op基本上都对应一个ZEND_ASSIGN_*_HANDLER这样的一个宏 , 它就保存在active_op_array-opline-handlers中 。
在进入到 execute之后:
首先初始化execute_data,它保存了很多重要信息,上下文信息,然后调用 ZEND_VM_SET_OPCODE宏,
把execute_data-opline的指针指向active_op_array-opline-handlers 。
之后,execute会执行一个while循环,逐条执行opline:
123456789101112131415161718192021222324while (1) {int ret;#ifdef ZEND_WIN32if (EG(timed_out)) {zend_timeout(0);}#endifif ((ret = EX(opline)-handler(execute_data TSRMLS_CC))0) {switch (ret) {case 1:EG(in_execution) = original_in_execution;return;case 2:op_array = EG(active_op_array);goto zend_vm_enter;case 3:execute_data = https://www.04ip.com/post/EG(current_execute_data);default:break;}}}
每个handlers都会执行一个宏:ZEND_VM_NEXT_OPCODE(),它意思就是跳到下一个Opline,这样就能逐条执行了 。
最后跟踪 上面的PHP代码会执行 ZEND_ASSIGN_SPEC_CV_VAR_HANDLER这个宏 , 它是干嘛的?他就是 变量赋值
下面代码执行的操作:
1234class A{ }$a=new A();
这里就会执行 这个宏 。
在这个宏里有段代码:
12345678910111213141516171819202122232425262728293031static int ZEND_FASTCALLZEND_ASSIGN_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS){zend_op *opline = EX(opline);zend_free_op free_op2;zval *value = https://www.04ip.com/post/_get_zval_ptr_var(opline-op2, EX(Ts), free_op2 TSRMLS_CC);zval **variable_ptr_ptr = _get_zval_ptr_ptr_cv(opline-op1, EX(Ts), BP_VAR_W TSRMLS_CC);if (IS_CV == IS_VAR!variable_ptr_ptr) {if (zend_assign_to_string_offset(EX_T(opline-op1.u.var), value, IS_VAR TSRMLS_CC)) {if (!RETURN_VALUE_UNUSED(opline-result)) {EX_T(opline-result.u.var).var.ptr_ptr = EX_T(opline-result.u.var).var.ptr;ALLOC_ZVAL(EX_T(opline-result.u.var).var.ptr);INIT_PZVAL(EX_T(opline-result.u.var).var.ptr);ZVAL_STRINGL(EX_T(opline-result.u.var).var.ptr, Z_STRVAL_P(EX_T(opline-op1.u.var).str_offset.str)+EX_T(opline-op1.u.var).str_offset.offset, 1, 1);}} else if (!RETURN_VALUE_UNUSED(opline-result)) {AI_SET_PTR(EX_T(opline-result.u.var).var, EG(uninitialized_zval_ptr));PZVAL_LOCK(EG(uninitialized_zval_ptr));}} else {value = zend_assign_to_variable(variable_ptr_ptr, value, 0 TSRMLS_CC);if (!RETURN_VALUE_UNUSED(opline-result)) {AI_SET_PTR(EX_T(opline-result.u.var).var, value);PZVAL_LOCK(value);}}/* zend_assign_to_variable() always takes care of op2, never free it! */if (free_op2.var) {zval_ptr_dtor(free_op2.var);};ZEND_VM_NEXT_OPCODE();}
free_op2.var保存的是 new A的对象.
free_op2.var这个是哪儿来的呢?
在整个execute期间,维持一个 execute_data结构,里面有个 Ts指针
1union _temp_variable *Ts;
它用来保存一些临时的变量信息,比如 new A(),这个会保存到Ts链表里,
opline-op2.u.var这个里面保存了此临时变量所在的位置,然后Ts+这个值是一个zval*指针,它就保存了new A产生的对象.

推荐阅读