PHP kernel exploration: zend_execute specific execution process

Source: Internet
Author: User
Tags sapi zts zend

PHP kernel exploration: zend_execute specific execution process

The function of the interpreter engine to execute the OP is zend_execute, in fact Zend_execute is a function pointer, when the engine is initialized Zend_execute the default point to execute, which is defined in {Phpsrc}/zend /zend_vm_execute.h:

Zend_api void Execute (zend_op_array *op_array tsrmls_dc) {zend_execute_data *execute_data;      Zend_bool nested = 0;          Zend_bool original_in_execution = EG (in_execution);      if (EG (Exception)) {return;    } EG (in_execution) = 1; Zend_vm_enter:/* Initialize execute_data */execute_data = (Zend_execute_data *) Zend_vm_stack_alloc (Z End_mm_aligned_size (sizeof (zend_execute_data)) + zend_mm_aligned_size (sizeof (zval**) * Op_array->last_var * (E G (active_symbol_table)?        1:2)) + zend_mm_aligned_size (sizeof (temp_variable)) * op_array->t tsrmls_cc);      EX (CVs) = (zval***) ((char*) Execute_data + zend_mm_aligned_size (sizeof (Zend_execute_data)));      memset (EX (CVs), 0, sizeof (zval**) * Op_array->last_var); EX (Ts) = (temp_variable *) (((char*) ex (CVs)) + zend_mm_aligned_size (sizeof (zval**) * Op_array->last_var * (EG (active_ symbol_table)?      1:2)));      EX (FBC) = NULL;      EX (called_scope) = NULL; EX (object) = NULL;      EX (old_error_reporting) = NULL;      EX (Op_array) = Op_array;      EX (symbol_table) = EG (active_symbol_table);      EX (Prev_execute_data) = EG (Current_execute_data);      EG (current_execute_data) = Execute_data;      EX (nested) = nested;        nested = 1;      if (op_array->start_op) {zend_vm_set_opcode (OP_ARRAY->START_OP);      } else {Zend_vm_set_opcode (op_array->opcodes);          } if (Op_array->this_var! =-1 && eg (this)) {z_addref_p (eg));/* for $this pointer */ if (! EG (active_symbol_table)) {EX (CVS) [Op_array->this_var] = (zval**) EX (CVS) + (Op_array->last_var + Op_arr              Ay->this_var);          *ex (CVs) [Op_array->this_var] = EG (this); } else {if (Zend_hash_add (active_symbol_table), "This", sizeof ("the"), &eg (this), sizeof (Zval *), (v          oid**) &ex (CVs) [Op_array->this_var]) ==failure) {z_delref_p (EG (this));    }}}} EG (opline_ptr) = &ex (Opline);      EX (function_state). function = (zend_function *) Op_array;            EX (function_state). arguments = NULL;  while (1) {int ret;          #ifdef Zend_win32 if (EG (timed_out)) {zend_timeout (0);                  } #endif if (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 = EG (Current_execute_data);              Default:break;  }}} Zend_error_noreturn (E_error, "arrived at end of main loop which shouldn ' t happen"); }

The parameter of this function is Op_array, which is a pointer to Zend_op_array, Op_array is generated during the compilation process, it is necessary to introduce zend_op_array this type.

Zend_op_array about this type of definition in {phpsrc}/zend/zend_compile.h:

struct _zend_op_array {/* Common elements */Zend_uchar type;              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;      Zend_bool pass_rest_by_reference;      unsigned char return_reference;        /* END of common Elements */Zend_bool done_pass_two;        Zend_uint *refcount;      Zend_op *opcodes;        Zend_uint last, size;      Zend_compiled_variable *vars;        int Last_var, Size_var;        Zend_uint T;      Zend_brk_cont_element *brk_cont_array;      int Last_brk_cont;        int Current_brk_cont;      Zend_try_catch_element *try_catch_array;        int last_try_catch;        /* Static variables Support */HashTable *static_variables;      Zend_op *start_op;        int backpatch_count;        Zend_uint This_var;      Char *filename;      Zend_uint Line_start;      Zend_uint Line_end; ChAR *doc_comment;      Zend_uint Doc_comment_len; Zend_uint early_binding;  /* The linked list of delayed declarations */void *reserved[zend_max_reserved_resources];    }; typedef struct _ZEND_OP_ARRAY Zend_op_array;

This is a complex structure and we currently only introduce the most basic fields.


Op_array type, the first thing to note is that after a period of PHP code is compiled, although the return is a zend_op_array pointer, but actually generated more than one zend_op_array structure, through the structure of some of the fields, For example, Function_name, Num_args, etc. you may find that this zend_op_array structure seems to have a certain connection to the function, indeed, the user-defined function, and the user-defined class method, are a zend_op_array structure, These zend_op_array structures are saved in some places during compilation, for example, user-defined functions are saved in global_function_table, which is the global function symbol table, which can be retrieved from this table by the function name. So what is the Zend_op_array pointer that is returned after compilation, in fact, the Zend_op_array returned after compilation is an entry for execution, or it can be thought of as the outermost layer, which is not a op_array of global code in any function body. However, the global code, the user-defined function, and the user-defined method all have the same type value: 2, the type is the value of the macro defined as:

#define Zend_internal_function              1  #define Zend_user_function                  2  #define Zend_overloaded_function            3  #define ZEND_EVAL_CODE                      4  #define Zend_overloaded_function_temporary  5

Can see the global code, user functions, user methods are corresponding to the zend_user_function, this is the most common type, where Zend_eval_code corresponds to the EVAL function in the PHP code, so we can think of, The PHP code in the eval function parameter is also compiled into a separate zend_op_array.


If the Op_array is generated by a user-defined function or a method compilation, then this field corresponds to the name of the function, and if it is the code of the Global code or the eval part, then this field is controlled.


This field type is Zend_op *, so this is an array of zend_op, this array is saved by this compilation process generated by the OP, if you do not understand zend_op, you can look at the previous article opcode introduction, this field is the most important part, Zend_ Execute eventually executes the OP saved here.

Now that we have a basic understanding of the parameter op_array, we begin to get into execute.

Execution details the Execute function begins with a declaration of some underlying variables, where Zend_execute_data *execute_data is the data structure of the execution period, This variable will be passed to each OP's handler function as a parameter after a certain initialization, and the OP may change the contents of the Execute_data at any time during execution.

Line 14th Zend_vm_enter This jump tag is the entry that executes as a virtual machine, and when the OP involves a function call, it is possible to jump here to execute the function body.

Line 16th to 19th behavior Execute_data allocating space

Line 21st to 32nd is mainly to execute_data some initialization, and save the field work, to save the scene is because when entering the function call, you need to save some of the current running data, after the end of the function call to restore, you can imagine the operating system in the process scheduling, When the process is paged out, it needs to save the context of the Register, and then take it out again when the process is called in.

Lines 41st through 51st mainly include the $this variable in the current dynamic symbol table, which is necessary to invoke the object's method.

The while infinite loop starting at line 58th starts execution of the opcodes in Op_array and calls the handler of the currently executing op in line 66th:

EX (Opline)->handler (Execute_data tsrmls_cc))

Then, if the return value of handler is less than 0, the loop continues, and if greater than 0 enters a switch structure:

When the return value is 1 o'clock: The Execute function returns and execution ends.

When the return value is 2 o'clock: Op_array is reset and jumps to Zend_vm_enter, this is usually a function call or execute the code in the Eval function, will execute the correlation function in the new context Op_array

When the return value is 3 o'clock: The loop body continues to execute and, of course, before proceeding, EX (Opline) has moved back one (possibly multiple), that is, has pointed to the next new opline, so continue to execute the new Opline

When other values are returned: End loop, error, end should return, that is, 1

The return of a specific value in OP's handler is defined as a macro, for example, defined in {phpsrc}/zend/zend_execute.c:

#define ZEND_VM_NEXT_OPCODE ()/      check_symbol_tables ()/      EX (opline) + +;/      zend_vm_continue ()    #define Zend_vm_set_opcode (NEW_OP)/      check_symbol_tables ()/      EX (opline) = New_op    #define ZEND_VM_JMP (NEW_OP)/      check_symbol_tables ()/      if (expected (! EG (Exception))) {/          EX (opline) = New_op;/      }/      zend_vm_continue ()    #define ZEND_VM_INC_OPCODE ()/      EX (opline) + +

and defined in {phpsrc}/zend/zend_vm_execute.c:

#define ZEND_VM_CONTINUE ()   return 0  #define Zend_vm_return ()     return 1  #define Zend_vm_enter ()      Return 2  #define ZEND_VM_LEAVE ()      return 3  #define zend_vm_dispatch (opcode, opline) return zend_vm_get_ opcode_handler (opcode, opline) (Zend_opcode_handler_args_passthru);

Simple Introduction to Features

Zend_vm_next_opcode (): Move to the next op, return 0, do not enter switch, loop continues (this is the most commonly used)

Zend_vm_set_opcode (NEW_OP): Current Opline set to New_op

ZEND_VM_JMP (NEW_OP): Current opline set to New_op, return 0, do not enter switch, loop continues

Zend_vm_inc_opcode (): Move only to the next op

The switchover of the execution environment in the previous content has been mentioned, user-defined functions, class methods, eval code will be compiled into a separate op_array, then when the function calls and other operations, it is necessary to invoke the Op_array execution environment and the new function of the Op_array execution environment of the switch , we will call the user-defined function to describe how the entire switching process takes place.

Before you introduce this procedure, you must understand the relevant data structure of the execution environment, which involves two of the following:

1. Implementation-time global variable structure

The relevant definitions are in {phpsrc}/zend/zend_globals_macros.h:

/* Executor *  /#ifdef ZTS  # define EG (v) tsrmg (executor_globals_id, zend_executor_globals *, v)  #else  # Define EG (v) (EXECUTOR_GLOBALS.V)  extern zend_api zend_executor_globals executor_globals;  #endif

Here is a conditional compilation, zts means thread security enabled, in order to simplify, we are here in the case of non-thread safe mode, then the execution period of the global variable is executor_globals, its type is zend_executor_globals, Zend_ The definition of executor_globals in {phpsrc}/zend/zend_globals.h, the structure is very large, here contains the entire execution period need to use a variety of variables, regardless of which op_array in the execution, all share this global variable, in the course of execution , some members of this structure may change, such as the currently executing Op_array field Active_op_array, and the dynamic symbol table fields active_symbol_table may change according to different Op_array. The this pointer changes depending on the object environment.

An eg macro is also defined to take the value of the field in this variable, which is a package for thread-safe and non-thread safe mode.

2. Execution data for each op_array itself

For each op_array, there will be some data of their own execution period, at the beginning of the function execute we can see the zend_vm_enter jump tag will be the initial local variable execute_data, so we switch to the new OP_ each time Array, a Execute_data variable is created for the new Op_array, and the type of this variable is a pointer to Zend_execute_data, which is defined in {phpsrc}/zend/zend_compile.h:

struct _zend_execute_data {      struct _zend_op *opline;      Zend_function_state function_state;      Zend_function *FBC; /* Function Being called */      zend_class_entry *called_scope;      Zend_op_array *op_array;      Zval *object;      Union _temp_variable *ts;      Zval ***cvs;      HashTable *symbol_table;      struct _zend_execute_data *prev_execute_data;      Zval *old_error_reporting;      Zend_bool nested;      Zval **original_return_value;      Zend_class_entry *current_scope;      Zend_class_entry *current_called_scope;      Zval *current_this;      Zval *current_object;      struct _zend_op *call_opline;  };

You can use the EX macro to take the value: #define EX (Element) execute_data->element

Here are just two of these fields:

Opline: The OP currently being executed.

Prev_execute_data:op_array Environment switch, this field is used to save the pre-switch op_array, this field is very important, he can each Op_array execute_data in the order of the call to join a single linked list, Whenever a op_array execution ends to revert to the pre-call Op_array, the pre-call executor data is obtained through the Prev_execute_data field in the current execute_data.

The field Current_execute_data in Executor_globals is the execute_data that points to the currently executing op_array.

Before the formal introduction also need a simple introduction of user-defined functions of the call process, the detailed procedure later in the chapter of the function is specifically introduced, here is a simple explanation:

When calling a function, such as the test () function, the corresponding function body is searched according to test in the Global function symbol table, if the search is not the error function is not defined, after finding the function body of test, the op_array of the test function is obtained. Then jump to the Goto label in execute: Zend_vm_enter, then enter the execution environment of the test function.

Below we will introduce a simple code to perform the environment switching process, example code:

<?php  $a = 123;    Test ();    function test ()  {      return 1;  }  ? >

This piece of code is very simple, so that we can introduce the principle, complex code reader extrapolate. This code compiles two Op_array, one is the op_array of the global code, the other is the op_array of the test function, where the global code goes through a function call to the execution environment of the test function, and after execution, it returns to the global code and then the code ends.

Here we take a few stages to introduce the process of this code, and then you can know how to perform the environment switch.

1. Go to execute function and start executing Op_array, this op_array is the op_array of the global code, we call it op_array1 for the time being.

First, in execute, we set up a execute_data data for Op_array1, we temporarily named Execute_data1, and then we do the related initialization, which is more important:

EX (Op_array) = Op_array; Set the Op_array field to the currently executing Op_array, which is the Op_array1ex (prev_execute_data) = EG (current_execute_data) of the global Code, or the current op that is saved in the global execution data _array execution data is saved to Op_array1 's execute_data1 prev_execute_data field, because this is the first op_array to execute, so prev_execute_data is actually a null value, The current_execute_data of the execution period global variable is then set to execute_data1 and the current execution op of execute_data1 is set so that the current OP can be started.

2. When Op_array1 executes to the test function call, first finds the function body of test from the global function symbol table, saves the function body in the Execute_data1 function_state field, and then takes the Op_array from the function body to test , we use Op_array2 to represent and assign op_array2 to eg (Active_op_array):

EG (Active_op_array) = &ex (function_state) .function->op_array;

The dynamic Op_array field of the global variable for the execution period points to the Op_array of the function test and then calls Zend_vm_enter (); This time it will go back to the switch structure in the EXECUTE function and satisfy the following case

Case 2:     Op_array = EG (Active_op_array);     Goto Zend_vm_enter;

EG (Active_op_array) was previously set to the op_array2 of the test function, so in function execute, the Op_array variable points to the op_array2 of test and jumps to Zend_vm_enter.

3. Jump to Zend_vm_enter after actually returned to a similar 1 step, at this time for Test op_array2 set up its execution data execute_data, we use EXECUTE_DATA2 to express. Some of the differences with 1 are EX (prev_execute_data) = EG (current_execute_data); this time Current_execute_data = Execute_data1, which is the execution period data of the global Code And then eg (current_execute_data) = Execute_data, so current_execute_data is equal to test's execution period data execute_data2, and the Global Code Execute_ Data1 is saved in the Execute_data2 prev_execute_data field. This time the environment switch is complete and the test function is started.

4. After execution of the test function is returned to the execution environment before the call, that is, the global Code execution Environment, the most important operation of this phase is eg (current_execute_data) = EX (Prev_execute_data); In 3 the EX (PREV_EXECUTE_DATA) has been set to the global code of EXECUTE_DATA1, so the current execution of data becomes the global Code execution data, so that the successful from the function test execution environment returned to the Global Code execution Environment

In this way, the execution environment of the switchover process is complete, for deep-seated function calls, the principle, the execution of data execute_data composed of a single linked list will be longer.

Read the following list of topics in this article:

PHP Kernel Explorer: Starting with the SAPI interface
PHP kernel exploration: Start and end of a single request
PHP kernel exploration: One-time request life cycle
PHP Kernel Exploration: single-process SAPI life cycle
PHP kernel Exploration: SAPI lifecycle of multiple processes/threads
PHP Kernel Explorer: Zend Engine
PHP Kernel Explorer: Explore SAPI again
PHP kernel Discovery: Apache module Introduction
PHP Kernel Explorer: PHP support via MOD_PHP5
PHP kernel exploration: Apache Run with hook function
PHP Kernel Explorer: embedded PHP
PHP Kernel Explorer: PHP fastcgi
PHP kernel exploration: How to execute PHP scripts
PHP Kernel Explorer: PHP script execution details
PHP kernel exploration: opcode opcode
PHP kernel Explorer: PHP opcode
PHP kernel exploration: Interpreter execution process
PHP Kernel Exploration: Variables overview
PHP kernel exploration: variable storage and type
PHP Kernel Explorer: Hash table in PHP
PHP Kernel Exploration: Understanding the hash table in Zend
PHP kernel exploration: PHP hash Algorithm design
PHP kernel Exploration: translating an article hashtables
PHP Kernel exploration: What is a hash collision attack?
PHP Kernel exploration: implementation of constants
PHP kernel exploration: Storage of variables
PHP kernel exploration: Types of variables
PHP Kernel Explorer: Variable value operation
PHP Kernel exploration: Creation of variables
PHP kernel exploration: pre-defined variables
PHP Kernel Explorer: variable retrieval
PHP kernel Exploration: Variable type conversion
PHP Kernel exploration: implementation of weakly typed variables
PHP Kernel exploration: implementation of static variables
PHP Kernel Explorer: Variable type hints
PHP kernel exploration: The life cycle of a variable
PHP Kernel Exploration: variable assignment and destruction
PHP Kernel exploration: variable scope
PHP kernel Explorer: Weird variable names
PHP Kernel Explorer: variable value and type storage
PHP Kernel Explorer: global variables
PHP kernel Exploration: Conversion of variable types
PHP kernel Exploration: The memory management begins
PHP Kernel Explorer: Zend Memory manager
PHP Kernel Explorer: PHP's memory management
PHP Kernel Exploration: Application and destruction of memory
PHP Kernel Exploration: reference count vs. write-time replication
PHP kernel exploration: PHP5.3 garbage collection mechanism
PHP Kernel Explorer: Cache in memory management
PHP Kernel Exploration: write-time copy cow mechanism
PHP kernel Exploration: arrays and Linked lists
PHP kernel exploration: Using the Hash Table API
PHP kernel exploration: array manipulation
PHP kernel Exploration: Array source code Analysis
PHP Kernel Exploration: Classification of functions
PHP kernel Exploration: internal structure of functions
PHP Kernel exploration: function structure transformation
PHP Kernel Exploration: The process of defining a function
PHP kernel Exploration: Parameters for functions
PHP kernel exploration: zend_parse_parameters function
PHP Kernel exploration: function return value
PHP kernel exploration: formal parameter return value
PHP Kernel exploration: function invocation and execution
PHP kernel exploration: Referencing and function execution
PHP kernel exploration: anonymous functions and closures
PHP Kernel Exploration: object-oriented opening
PHP kernel Exploration: The structure and implementation of classes
PHP kernel exploration: member Variables for classes
PHP Kernel Exploration: Member Methods for classes
PHP Kernel Exploration: class prototype Zend_class_entry
PHP kernel exploration: Definition of class
PHP Kernel Explorer: Access control
PHP kernel exploration: inheritance, polymorphism and abstract classes
PHP Kernel Exploration: magic function and delay binding
PHP kernel Exploration: Preserving classes and special classes
PHP Kernel Explorer: objects
PHP kernel Exploration: Creating object instances
PHP Kernel Explorer: Object properties Read and write
PHP Kernel Exploration: namespaces
PHP kernel exploration: Defining interfaces
PHP kernel Exploration: Inheritance and Implementation interface
PHP Kernel Exploration: resource resource type
PHP Kernel Explorer: Zend virtual machine
PHP Kernel Exploration: Lexical parsing of virtual machines
PHP Kernel Explorer: virtual machine Syntax analysis
PHP kernel exploration: Execution of intermediate code opcode
PHP Kernel Exploration: Code encryption and decryption
PHP kernel exploration: zend_execute specific execution process
PHP kernel exploration: Reference and counting rules for variables
PHP kernel exploration: New garbage collection Mechanism description

Reference Source:
PHP kernel exploration: zend_execute specific execution process

PHP kernel exploration: zend_execute specific execution process

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.