ホームページ > バックエンド開発 > PHPチュートリアル > PHP オペコード カーネルの実装

PHP オペコード カーネルの実装

WBOY
リリース: 2016-06-23 13:17:56
オリジナル
912 人が閲覧しました

catalogue

1. Opcode简介2. PHP中的Opcode3. opcode翻译执行(即时解释执行)
ログイン後にコピー

1. オペコードの概要

オペコードは、実行される演算を指定するコンピュータ命令の一部であり、命令の形式と仕様はプロセッサの命令仕様によって指定されます。 命令自体に加えて、通常は命令に必要なオペランドがあります。一部の命令は明示的なオペランドを必要としない場合があります。 これらのオペランドは、レジスタ内の値、スタック内の値、特定のメモリ内の値、または IO ポート内の値などです。

通常、オペコードにはバイト コードという別の名前があります。 たとえば、Java 仮想マシン (JVM)、.NET の共通中間言語 (CIL: Common Intermediate Language) などです。

PHP のオペコードは、前の紹介の後半に属します。PHP は Zend 仮想マシン ( Zend VM)。 PHP のオペコードは、Zend 仮想マシン内の命令です (Zend の中間コードに基づく)

関連リンク:

http://www.luocong.com/learningopcode/doc/1._%E4%BB%80%E4%B9%88%E6%98%AFOpCode%EF%BC%9F.htm
ログイン後にコピー

2. PHP のオペコード

0x1: データ構造

PHP 実装内部では、オペコードは次の構造を持っています。 body は php-5.6.17Zendzend_compile.h を表します

struct _zend_op {    opcode_handler_t handler;    // 执行该opcode时调用的处理函数    znode_op op1;                // opcode所操作的操作数    znode_op op2;                // opcode所操作的操作数    znode_op result;    ulong extended_value;    uint lineno;    zend_uchar opcode;            // opcode代码    zend_uchar op1_type;    zend_uchar op2_type;    zend_uchar result_type;};
ログイン後にコピー

の命令を示す op がありますcode フィールドと、これによって演算されるオペランドopcode では、PHP はアセンブリほど低レベルではありません。スクリプトが実際に実行される場合、この種の情報はさらに多くの情報が必要になる場合があります。たとえば、命令の実行後には result フィールドに結果が格納されます。以下のコード コンパイラーが print ステートメントに遭遇したときにコンパイルする関数です

php-5.6.17Zendzend_compile.c

void zend_do_print(znode *result, const znode *arg TSRMLS_DC) /* {{{ */{        //新创建一条zend_op     zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);    //将新建的zend_op的返回值类型设置为临时变量(IS_TMP_VAR),因为print中的内存仅仅为了临时输出,并不需要保存    opline->result_type = IS_TMP_VAR;    //为临时变量申请空间    opline->result.var = get_temporary_variable(CG(active_op_array));    //指定opcode为ZEND_PRINT    opline->opcode = ZEND_PRINT;    //将传递进来的参数赋值给这条opcode的第一个操作数    SET_NODE(opline->op1, arg);    SET_UNUSED(opline->op2);    GET_NODE(result, opline->result);}
ログイン後にコピー

0x2: opcode type: zend_op->zend_uchar opcode

アセンブリ言語の概念をそれぞれ比較してくださいopcode すべては型に対応し、opcpde の「操作命令」を示します。opcode の型は zend_uchar で、実際には unsigned char です。このフィールドに保存される整数値は、さまざまな op 型を区別するために使用されます。オペコード 利用可能な値は、macros/Zend/zend_vm_opcodes.h として定義されます

#define ZEND_NOP                               0#define ZEND_ADD                               1#define ZEND_SUB                               2#define ZEND_MUL                               3#define ZEND_DIV                               4#define ZEND_MOD                               5#define ZEND_SL                                6#define ZEND_SR                                7#define ZEND_CONCAT                            8#define ZEND_BW_OR                             9#define ZEND_BW_AND                           10#define ZEND_BW_XOR                           11#define ZEND_BW_NOT                           12#define ZEND_BOOL_NOT                         13#define ZEND_BOOL_XOR                         14#define ZEND_IS_IDENTICAL                     15#define ZEND_IS_NOT_IDENTICAL                 16#define ZEND_IS_EQUAL                         17#define ZEND_IS_NOT_EQUAL                     18#define ZEND_IS_SMALLER                       19#define ZEND_IS_SMALLER_OR_EQUAL              20#define ZEND_CAST                             21#define ZEND_QM_ASSIGN                        22#define ZEND_ASSIGN_ADD                       23#define ZEND_ASSIGN_SUB                       24#define ZEND_ASSIGN_MUL                       25#define ZEND_ASSIGN_DIV                       26#define ZEND_ASSIGN_MOD                       27#define ZEND_ASSIGN_SL                        28#define ZEND_ASSIGN_SR                        29#define ZEND_ASSIGN_CONCAT                    30#define ZEND_ASSIGN_BW_OR                     31#define ZEND_ASSIGN_BW_AND                    32#define ZEND_ASSIGN_BW_XOR                    33#define ZEND_PRE_INC                          34#define ZEND_PRE_DEC                          35#define ZEND_POST_INC                         36#define ZEND_POST_DEC                         37#define ZEND_ASSIGN                           38#define ZEND_ASSIGN_REF                       39#define ZEND_ECHO                             40#define ZEND_PRINT                            41#define ZEND_JMP                              42#define ZEND_JMPZ                             43#define ZEND_JMPNZ                            44#define ZEND_JMPZNZ                           45#define ZEND_JMPZ_EX                          46#define ZEND_JMPNZ_EX                         47#define ZEND_CASE                             48#define ZEND_SWITCH_FREE                      49#define ZEND_BRK                              50#define ZEND_CONT                             51#define ZEND_BOOL                             52#define ZEND_INIT_STRING                      53#define ZEND_ADD_CHAR                         54#define ZEND_ADD_STRING                       55#define ZEND_ADD_VAR                          56#define ZEND_BEGIN_SILENCE                    57#define ZEND_END_SILENCE                      58#define ZEND_INIT_FCALL_BY_NAME               59#define ZEND_DO_FCALL                         60#define ZEND_DO_FCALL_BY_NAME                 61#define ZEND_RETURN                           62#define ZEND_RECV                             63#define ZEND_RECV_INIT                        64#define ZEND_SEND_VAL                         65#define ZEND_SEND_VAR                         66#define ZEND_SEND_REF                         67#define ZEND_NEW                              68#define ZEND_INIT_NS_FCALL_BY_NAME            69#define ZEND_FREE                             70#define ZEND_INIT_ARRAY                       71#define ZEND_ADD_ARRAY_ELEMENT                72#define ZEND_INCLUDE_OR_EVAL                  73#define ZEND_UNSET_VAR                        74#define ZEND_UNSET_DIM                        75#define ZEND_UNSET_OBJ                        76#define ZEND_FE_RESET                         77#define ZEND_FE_FETCH                         78#define ZEND_EXIT                             79#define ZEND_FETCH_R                          80#define ZEND_FETCH_DIM_R                      81#define ZEND_FETCH_OBJ_R                      82#define ZEND_FETCH_W                          83#define ZEND_FETCH_DIM_W                      84#define ZEND_FETCH_OBJ_W                      85#define ZEND_FETCH_RW                         86#define ZEND_FETCH_DIM_RW                     87#define ZEND_FETCH_OBJ_RW                     88#define ZEND_FETCH_IS                         89#define ZEND_FETCH_DIM_IS                     90#define ZEND_FETCH_OBJ_IS                     91#define ZEND_FETCH_FUNC_ARG                   92#define ZEND_FETCH_DIM_FUNC_ARG               93#define ZEND_FETCH_OBJ_FUNC_ARG               94#define ZEND_FETCH_UNSET                      95#define ZEND_FETCH_DIM_UNSET                  96#define ZEND_FETCH_OBJ_UNSET                  97#define ZEND_FETCH_DIM_TMP_VAR                98#define ZEND_FETCH_CONSTANT                   99#define ZEND_GOTO                            100#define ZEND_EXT_STMT                        101#define ZEND_EXT_FCALL_BEGIN                 102#define ZEND_EXT_FCALL_END                   103#define ZEND_EXT_NOP                         104#define ZEND_TICKS                           105#define ZEND_SEND_VAR_NO_REF                 106#define ZEND_CATCH                           107#define ZEND_THROW                           108#define ZEND_FETCH_CLASS                     109#define ZEND_CLONE                           110#define ZEND_RETURN_BY_REF                   111#define ZEND_INIT_METHOD_CALL                112#define ZEND_INIT_STATIC_METHOD_CALL         113#define ZEND_ISSET_ISEMPTY_VAR               114#define ZEND_ISSET_ISEMPTY_DIM_OBJ           115#define ZEND_PRE_INC_OBJ                     132#define ZEND_PRE_DEC_OBJ                     133#define ZEND_POST_INC_OBJ                    134#define ZEND_POST_DEC_OBJ                    135#define ZEND_ASSIGN_OBJ                      136#define ZEND_INSTANCEOF                      138#define ZEND_DECLARE_CLASS                   139#define ZEND_DECLARE_INHERITED_CLASS         140#define ZEND_DECLARE_FUNCTION                141#define ZEND_RAISE_ABSTRACT_ERROR            142#define ZEND_DECLARE_CONST                   143#define ZEND_ADD_INTERFACE                   144#define ZEND_DECLARE_INHERITED_CLASS_DELAYED 145#define ZEND_VERIFY_ABSTRACT_CLASS           146#define ZEND_ASSIGN_DIM                      147#define ZEND_ISSET_ISEMPTY_PROP_OBJ          148#define ZEND_HANDLE_EXCEPTION                149#define ZEND_USER_OPCODE                     150#define ZEND_JMP_SET                         152#define ZEND_DECLARE_LAMBDA_FUNCTION         153#define ZEND_ADD_TRAIT                       154#define ZEND_BIND_TRAITS                     155#define ZEND_SEPARATE                        156#define ZEND_QM_ASSIGN_VAR                   157#define ZEND_JMP_SET_VAR                     158#define ZEND_DISCARD_EXCEPTION               159#define ZEND_YIELD                           160#define ZEND_GENERATOR_RETURN                161#define ZEND_FAST_CALL                       162#define ZEND_FAST_RET                        163#define ZEND_RECV_VARIADIC                   164#define ZEND_SEND_UNPACK                     165#define ZEND_POW                             166#define ZEND_ASSIGN_POW                      167
ログイン後にコピー

0x3: オペコード実行ハンドル: zend_op->handler

op の実行ハンドル、その型は opcode_handler_t です

typedef int (ZEND_FASTCALL *opcode_handler_t) (ZEND_OPCODE_HANDLER_ARGS);
ログイン後にコピー

この関数ポインタは定義されていますop 実行メソッドの場合、各 opcode フィールドはハンドラーのタイプに対応します。たとえば、$a = 1; の場合、そのコードによって生成される op にはオペランド const と cv があり、最終的にハンドラーは関数 ZEND_ASSIGN_SPEC_CV_CONST_HANDLER/ であると判断できます。 Zend/zend_vm_execute.h

void zend_init_opcodes_handlers(void){  static const opcode_handler_t labels[] = {    ..    ZEND_ASSIGN_SPEC_CV_CONST_HANDLER,    ..    }}
ログイン後にコピー

0x4: opcpde オペランド znode

operand フィールドは _zend_op 型の重要な部分です。3 つのオペランド op1、op2、および result は znode 型 php-5.6.17Zendzend_compile.h

typedef struct _znode { /* used only during compilation */    /*    这个int类型的字段定义znode操作数的类型    #define IS_CONST    (1<<0)    //表示常量,例如$a = 123; $b = "hello";这些代码生成OP后,123和"hello"都是以常量类型操作数存在    #define IS_TMP_VAR    (1<<1)    //表示临时变量,临时变量一般在前面加~来表示,这是一些OP执行过程中需要用到的中间变量,例如初始化一个数组的时候,就需要一个临时变量来暂时存储数组zval,然后将数组赋值给变量    #define IS_VAR        (1<<2)    //一般意义上的变量,以$开发表示    #define IS_UNUSED    (1<<3)    // Unused variable      #define IS_CV        (1<<4)    // Compiled variable,这种类型的操作数比较重要,此类型是在PHP后来的版本中(大概5.1)中才出现,CV的意思是compiled variable,即编译后的变量,变量都是保存在一个符号表中,这个符号表是一个哈希表,如果每次读写变量的时候都需要到哈希表中去检索,会对效率有一定的影响,因此在执行上下文环境中,会将一些编译期间生成的变量缓存起来。此类型操作数一般以!开头表示,比如变量$a=123;$b="hello"这段代码,$a和$b对应的操作数可能就是!0和!1, 0和1相当于一个索引号,通过索引号从缓存中取得相应的值    */    int op_type;        /*    此字段为一个联合体,根据op_type的不同,u取不同的值    1. op_type=IS_CONST的时候,u中的constant保存的就是操作数对应的zval结构    2. 例如$a=123时,123这个操作数中,u中的constant是一个IS_LONG类型的zval,其值lval为123      */    union {        znode_op op;        zval constant; /* replaced by literal/zv */        zend_op_array *op_array;        zend_ast *ast;    } u;    zend_uint EA;      /* extended attributes */} znode;
ログイン後にコピー
として定義されます。

0x5 : オペコードがコンパイルされた配列 op_array

zend_do_print 関数の最初の行で、次のコード行に気づきました

zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);
ログイン後にコピー

PHP スクリプト コードがコンパイルされた後に生成されたオペコードは op_array に保存され、その内部ストレージ構造は次のとおりですphp-5.6 .17Zendzend_compile.h に従います

struct _zend_op_array {    /* Common elements */    zend_uchar type;    const char *function_name;        // 如果是用户定义的函数则,这里将保存函数的名字    zend_class_entry *scope;    zend_uint fn_flags;    union _zend_function *prototype;    zend_uint num_args;    zend_uint required_num_args;    zend_arg_info *arg_info;    /* END of common elements */    zend_uint *refcount;    zend_op *opcodes;                // opcode数组    zend_uint last;    zend_compiled_variable *vars;    int last_var;    zend_uint T;    zend_uint nested_calls;    zend_uint used_stack;    zend_brk_cont_element *brk_cont_array;    int last_brk_cont;    zend_try_catch_element *try_catch_array;    int last_try_catch;    zend_bool has_finally_block;    /* static variables support */    HashTable *static_variables;    zend_uint this_var;    const char *filename;    zend_uint line_start;    zend_uint line_end;    const char *doc_comment;    zend_uint doc_comment_len;    zend_uint early_binding; /* the linked list of delayed declarations */    zend_literal *literals;    int last_literal;    void **run_time_cache;    int  last_cache_slot;    void *reserved[ZEND_MAX_RESERVED_RESOURCES];};
ログイン後にコピー

PHP スクリプト コード全体のコンパイルされたオペコードはここに保存され、実行中に次の実行関数によって実行されます

ZEND_API void execute(zend_op_array *op_array TSRMLS_DC){    // ... 循环执行op_array中的opcode或者执行其他op_array中的opcode}
ログイン後にコピー

各オペコードには、opcode_handler_t の関数ポインタ フィールドがあり、これが使用されますオペコードを実行するために、PHP にはオペコードを処理する 3 つの方法があります

1. CALL: PHP默认使用CALL的方式,也就是函数调用的方式2. SWITCH: 由于opcode执行是每个PHP程序频繁需要进行的操作,可以使用SWITCH或者GOTO的方式来分发3. GOTO: 通常GOTO的效率相对会高一些,不过效率是否提高依赖于不同的CPU
ログイン後にコピー
実際、Zend のオペコード変換は /zend/zend_ にあることがわかります。 language_parser.c 実行の説明これは、PHP がインタープリタ型言語と呼ばれる理由の中心的な原則であり、文法解析が生成物を生成するときに、call と switch を直接使用します。 . goto メソッドは、瞬時の解釈と実行のために zend API を呼び出します 関連リンク:
http://www.nowamagic.net/librarys/veda/detail/1325http://php.net/manual/zh/internals2.opcodes.list.phphttp://www.nowamagic.net/librarys/veda/detail/1543http://www.nowamagic.net/librarys/veda/detail/1324http://www.nowamagic.net/librarys/veda/detail/1543 http://www.laruence.com/2008/06/18/221.htmlhttp://www.php-internals.com/book/?p=chapt02/02-03-02-opcode
ログイン後にコピー

3. オペコード変換の実行 (瞬時の解釈と実行)

関連リンク:

http://www.php-internals.com/book/?p=chapt02/02-03-03-from-opcode-to-handler
ログイン後にコピー
Copyright (c) 2016 Little5ann All Rights Reserved

ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート