免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 5515 | 回复: 5
打印 上一主题 下一主题

深入理解PHP--zend最终调用函数分析 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-05-17 20:43 |只看该作者 |倒序浏览
PHP源代码分析-zend最终调用函数分析
原文出处:http://jackywdx.cn/2009/05/notes_of_zend_kernel


在追踪zend 内核的时候发现最终调用的函数都在zend_vm_execute.h文件里面定义了,也就是说基本每一个PHP的语句,比如定义一个函数、echo一个表达式等任何一个语句最后的实现都是在zend_vm_execute.h里面定义了,
在zend.c文件zend_startup()函数里面调用了zend_init_opcodes_handlers(),初始化了labels数组,这个数组的类型是opcode_handler_t,这个类型在zend_compile.h文件里面定义了:
typedef int (*opcode_handler_t) (ZEND_OPCODE_HANDLER_ARGS);
//在zend_vm_execute.h引用zend_opcode_handlers,

//zend_opcode_handlers = (opcode_handler_t*)labels;

extern ZEND_API opcode_handler_t *zend_opcode_handlers;


opcode_handler_t是函数指针,指定一个用来处理每一个opcode的函数的入口地址,这些地址都保存在labels数组里面了。

static const opcode_handler_t labels[] = {
        ZEND_NOP_SPEC_HANDLER,
      ZEND_NOP_SPEC_HANDLER,
        //中间省略。。。。。

        ZEND_NOP_SPEC_HANDLER,
      ZEND_ADD_SPEC_CONST_CONST_HANDLER,
      ZEND_ADD_SPEC_CONST_TMP_HANDLER,
      ZEND_ADD_SPEC_CONST_VAR_HANDLER,
        。。。。。。。
}


这个结构体包含了近3776个成员,这些成员都是函数名称,每次执行一个opcode的时候都要到这个labels里面找对应的handler处理函数。
在zend_vm_execute.h文件最后的有两个函数:

static opcode_handler_t zend_vm_get_opcode_handler(zend_uchar opcode, zend_op* op)
{
        static const int zend_vm_decode[] = {
            _UNUSED_CODE, /* 0              */
            _CONST_CODE,  /* 1 = IS_CONST   */
            _TMP_CODE,    /* 2 = IS_TMP_VAR */
            _UNUSED_CODE, /* 3              */
            _VAR_CODE,    /* 4 = IS_VAR     */
            _UNUSED_CODE, /* 5              */
            _UNUSED_CODE, /* 6              */
            _UNUSED_CODE, /* 7              */
            _UNUSED_CODE, /* 8 = IS_UNUSED  */
            _UNUSED_CODE, /* 9              */
            _UNUSED_CODE, /* 10             */
            _UNUSED_CODE, /* 11             */
            _UNUSED_CODE, /* 12             */
            _UNUSED_CODE, /* 13             */
            _UNUSED_CODE, /* 14             */
            _UNUSED_CODE, /* 15             */
            _CV_CODE      /* 16 = IS_CV     */
        };
        //这句很关键,就是返回处理当前opcode的handler,

                //zend_opcode_handlers等于上面定义的lables

        return zend_opcode_handlers[opcode * 25 + zend_vm_decode[op->op1.op_type] * 5 + zend_vm_decode[op->op2.op_type]];
}

//设置zend_op里面的handler,指定处理的函数

ZEND_API void zend_vm_set_opcode_handler(zend_op* op)
{
    op->handler = zend_vm_get_opcode_handler(zend_user_opcodes[op->opcode], op);
}


如果想知道每个opcode是交给哪个handler处理,可以在
return zend_opcode_handlers[opcode * 25 + zend_vm_decode[op->op1.op_type] * 5 + zend_vm_decode[op->op2.op_type]];
这条语句前面打印出索引值:

int index;
index = opcode * 25 + zend_vm_decode[op->op1.op_type] * 5 + zend_vm_decode[op->op2.op_type];
printf("zend_opcode_handlers_index:%d\n", index);


然后再重新 make && make install
编写一个test.php文件:
<?php
$a = "test";
?>
再运行/usr/local/php5.2.9/bin/php  test.php
输出的结果如下:
zend_opcode_handlers_index:970
zend_opcode_handlers_index:1553
zend_opcode_handlers_index:3743
简单的一个赋值语句,最后执行了三个函数,这三个数字代表在labels里面的偏移量,找到labels里面第970个值:
ZEND_ASSIGN_SPEC_CV_CONST_HANDLER
再找到这个函数:

static int ZEND_ASSIGN_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
    zend_op *opline = EX(opline);

    zval *value = &opline->op2.u.constant;

     zend_assign_to_variable(&opline->result, &opline->op1, &opline->op2, value, (0?IS_TMP_VAR:IS_CONST), EX(Ts) TSRMLS_CC);
    /* zend_assign_to_variable() always takes care of op2, never free it! */

    ZEND_VM_NEXT_OPCODE();
}


这个函数主要是对变量进行赋值,把值存进opline->result里面。后面1553和3743偏移值分别是
ZEND_RETURN_SPEC_CONST_HANDLER
ZEND_HANDLE_EXCEPTION_SPEC_HANDLER
分别是用来处理函数返回和异常,每个PHP文件执行到最后都会自动执行这两个函数。

另外想要快速查找每个偏移量对应的是哪个函数,可以先把  static const opcode_handler_t labels[] 里面的3776个成员保存在一个文件opcode_handler.txt,然后写个函数来智能处理:


#include<stdio.h>
#include<stdlib.h>
int main(int c, char *v[]){
   //在labels的偏移量

    int offset = atoi(v[1]);
    FILE *fp;
    fp = fopen("/home/dexin/op_code_handler.txt","r");
    char handler[200];
    int i = 0;
    while(!feof(fp)){
        fgets(handler,1024,fp);
        if(i != offset){
            i++;
        }else{
            printf("opcode handler:%s\n", handler);
            break;
        }
    
    }
    return 1;
}


编译:gcc -g -o gethandler gethandler.c
然后把gethandler 放入/bin/gethandler就可以了,比如我想查询偏移量为970的handler:
gethandler 970
结果:opcode handler:         ZEND_ASSIGN_SPEC_CV_CONST_HANDLER,
再去找到这个函数的实现就好了。
写完之后发现用shell写更加快:
#gethandler_awk
#!/bin/bash
awk 'NR == offset {print "opcode handler:"$0} offset=$1 /home/dexin/op_code_handler.txt

chmod+x gethandler_awk
放入/bin/gethandler_awk
gethandler_awk 970
同样可以得出结果
opcode handler:         ZEND_ASSIGN_SPEC_CV_CONST_HANDLER,

论坛徽章:
0
2 [报告]
发表于 2009-05-19 08:37 |只看该作者
好文,顶!

论坛徽章:
0
3 [报告]
发表于 2009-05-19 21:24 |只看该作者
:wink:
自己再顶。。

论坛徽章:
0
4 [报告]
发表于 2009-05-27 18:13 |只看该作者

回复 #1 jackywdx 的帖子

认真学习一下

论坛徽章:
0
5 [报告]
发表于 2009-05-27 18:21 |只看该作者
嗯,不顶不行!

论坛徽章:
0
6 [报告]
发表于 2009-05-30 21:09 |只看该作者
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP