310 likes | 609 Views
Zend Engine изнутри. Дмитрий Стогов. Немного истории. Zend Engine была разработана в качестве ядра для PHP 4 Andi Gutmans и Zeev Suraski в 1999 PHP 5.0 основан на Zend Engine 2 с новой объектной моделью PHP 5.1 основан на Zend Engine 2.1 со специализированной VM
E N D
Zend Engine изнутри Дмитрий Стогов
Немного истории • Zend Engine была разработана в качестве ядра для PHP 4 Andi Gutmans и Zeev Suraski в 1999 • PHP 5.0 основан на Zend Engine 2 с новой объектной моделью • PHP 5.1 основан на Zend Engine 2.1 со специализированной VM • PHP 5.2 основан на Zend Engine 2.2 с новым менеджером памяти • PHP 5.3 основан на Zend Engine 2.3 которая включает большинство улучшений и нововведений из PHP6, за исключением Unicode, (namespace-ы, сборщик мусора, LSB, оператор goto, ленивая инициализация таблиц символов, новый сканнер основанный на re2c) • PHP 6 основан на Zend Engine 3 с поддержкой Unicode
Подсистемы ZE • Менеджер памяти • API для доступа к внутренним структурам данных • Компилятор PHP • Виртуальная машина PHP • API для ресурсов (файлы, DB коннекшены) • API для внешних расширений PHP • Набор внутренних функций • Сборщик мусора (5.3)
Thread Safe Resource Manager • non-ZTS-build (single-thread) • ZTS-build (thread-safe) • Каждый thread работает со своими глобальными данными • ZE использует compiler_globals (CG) и executor_globals (EG) • Любое расширение PHP может определить свои глобальные данные, которые должны быть уникальными для разных thread-ов
TSRM макросы • void some_function(void) {process(EG(symbol_table));// compilation error}
TSRM макросы • void some_function(void) { int some_local_variable;TSRMLS_FETCH();process(EG(symbol_table));} • void some_function(TSRMLS_D) {process(EG(symbol_table));} some_function(TSRMLS_C); • void some_function(int some_paremeterTSRMLS_DC) {process(EG(symbol_table));} some_function(0TSRMLS_CC);
Менеджер памяти emalloc() efree() erealloc() estrdup() estrndup() ecalloc() $ USE_ZEND_ALLOC=0 valgrind php test.php
typedef struct _zval_struct { zend_uchar type; zvalue_value value; zend_uchar is_ref; zebd_uint refcount; } zval; Значения (zval-коетейнер)
typedef struct _zval_struct { zend_uchar type; zvalue_value value; zend_uchar is_ref; zebd_uint refcount; } zval; IS_NULL IS_LONG IS_DOUBLE IS_BOOL IS_ARRAY IS_OBJECT IS_STRING IS_RESOURCE IS_CONSTANT IS_CONSTANT_ARRAY Значения (zval-коетейнер)
typedef struct _zval_struct { zend_uchar type; zvalue_value value; zend_uchar is_ref; zebd_uint refcount; } zval; typedef union _zvalue_value { long lval; double dval; struct { char *val; int len; } str; HashTable *ht; zend_object_value obj; } zvalue_value; Значения (zval-коетейнер)
<?php $a = 10; $b =& $b; Ссылки EG(symbol_table) HashTable $a zval type IS_LONG $b value 10 is_ref 1 … refcount 2
<?php $a = 1; $b = $a; $b = 2; $b =& $a; $b = 3; $b =& $c; // $a = {1, refcount=1, is_ref=0} // $a = {1, refcount=2, is_ref=0} // $b = {1, refcount=2, is_ref=0} // $a = {1, refcount=1, is_ref=0} // $b = {2, refcount=1, is_ref=0} // $a = {1, refcount=2, is_ref=1} // $b = {1, refcount=2, is_ref=1} // $a = {3, refcount=2, is_ref=1} // $b = {3, refcount=2, is_ref=1} // $a = {3, refcount=1, is_ref=0} // $b = {?, refcount=?, is_ref=1} Присваивание и копирование при записи
Компилятор • Основан на flex/bison based (основан на re2c/bison начиная с 5.3) • Однопроходная компиляция (на самом деле два прохода) • AST не создается • Прямая компиляция в байт-кодVM • Быстрая компиляция • Оптимизация практически не выполняется
Глобальные данные компилятора (CG) • struct _zend_compiler_globals { … HashTable *function_table; HashTable *class_table; zend_class_entry *active_class_entry; zend_op_array *active_op_array; … }; • CG(function_table)
typedef struct _zend_op_array { zend_uchar type; char *function_name; zend_class_entry *scope; zend_uint fn_flags; zend_op *opcodes; zend_compiled_variables *vars; zend_uint last, lat_var, T; HashTable *static_variables; … } zend_op_array; Функции PHP (op_array)
typedef struct _zend_op { zend_uchar opcode; ulong extended_value; znode op1; znode op2; znode result; uint lineno; opcode_handler_t handler; } zend_op; Инструкции VM (zend_op)
typedef struct _zend_op { zend_uchar opcode; ulong extended_value; znode op1; znode op2; znode result; uint lineno; opcode_handler_t handler; } zend_op; ZEND_NOP ZEND_ADD ZEND_SUB ZEND_IS_EQUAL ZEND_JMP ZEND_JMPZ ZEND_ASSIGN ZEND_DO_FCALL ZEND_RETURN ~150 opcodes in zend_vm_opcodes.h Инструкции VM (zend_op)
typedef struct _zend_op { zend_uchar opcode; ulong extended_value; znode op1; znode op2; znode result; uint lineno; opcode_handler_t handler; } zend_op; typedef struct _znode { int op_type; union { zval constant; zend_uint var; zend_uint opline_num; zend_op *jmp_addr; struct { zend_uint var; zend_uint type; } EA; } u; } znode; Инструкции VM (zend_op)
typedef struct _znode { int op_type; union { zval constant; zend_uint var; zend_uint opline_num; zend_op *jmp_addr; struct { zend_uint var; zend_uint type; } EA; } u; } znode; IS_CONST IS_CV IS_TMP_VAR IS_VAR IS_UNUSED Операнды (znode)
<?php $a = “Hello”; $b = “World”; echo $a .” “. $b; ?> // FETCH_WC(“a”) -> V(0) // ASSIGNV(0), C(“Hello”) // FETCH_WC(“b”) -> V(1) // ASSIGNV(1), C(“World”) // FETCH_RC(“a”) -> V(2) // CONCATV(2), C(“ “) -> T(3) // FETCH_RC(“b”) -> V(4) // CONCATT(3), V(4) -> T(5) // ECHOT(5) // RETURNC(NULL) Пример компиляции (5.0)
<?php $a = “Hello”; $b = “World”; echo $a .” “. $b; ?> // // ASSIGN CV(0)[“a”], C(“Hello”) // // ASSIGN CV(1)[“b”], C(“World”) // // CONCAT CV(0)[“a”], C(“ “) -> T(0) // CONCAT T(0), CV(1)[“b”] -> T(1) // ECHO T(1) // // // RETURN C(NULL) Пример компиляции (5.1)
Глобальные данные VM (EG) • struct _zend_executor_globals { … HashTable *active_symbol_table; HashTable symbol_table; // $GLOBALS[] HashTable *function_table; HashTable *class_table; HashTable *zend_constants; zval *This; zend_class_entry *scope; zend_op_array *active_op_array; zend_op **opline_ptr; struct _zend_execute_data *current_execute_data; … }; • EG(symbol_table)
Switch-threaded Executor (4.*) void execute(zend_op_array *op_arrayTSRMLS_DC) { zend_execute_data execute_data; // initialization EX(opline) = op_array->opcodes; while (1) { switch (EX(opline)->opcode) { … case ZEND_RETURN; … return; } } }
Call-threaded Executor (5.*) void execute(zend_op_array *op_arrayTSRMLS_DC) { zend_execute_data execute_data; // initialization EX(opline) = op_array->opcodes; while (1) { if (EX(opline)->handler(&execute_dataTSRMLS_CC)) { return; } } }
Call-threaded Executor (5.0) int zend_concat_handler(ZEND_OPCODE_HANDLER_ARGS) { zend_op *opline = EX(opline); concat_function(&EX_T(opline->result.u.var).tmp_var, get_zval_ptr(&opline->op1, EX(Ts), &EG(free_op1), BP_VAR_R), get_zval_ptr(&opline->op2, EX(Ts), &EG(free_op2), BP_VAR_R)); FREE_OP1(EX(Ts), &opline->op1, EG(free_op1)); FREE_OP2(EX(Ts), &opline->op2, EG(free_op2)); EX(opline)++; return 0; }
Специализация в VM (5.1) ZEND_VM_HANDLER(8, ZEND_CONCAT, CONST|TMP|VAR|CV, CONST|TMP|VAR|CV) { zend_op *opline = EX(opline); zend_free_op free_op1, free_op2; concat_function(&EX_T(opline->result.u.var).tmp_var, GET_OP1_ZVAL_PTR(BP_VAR_R), GET_OP2_ZVAL_PTR(BP_VAR_R)); FREE_OP1(); FREE_OP2(); EX(opline)++; return 0; }
Специализация в VM (5.1) int ZEND_CONCAT_SPEC_CV_CONST_HANDLER( ZEND_OPCODE_HANDLER_ARGS) { zend_op *opline = EX(opline); concat_function(&EX_T(opline->result.u.var).tmp_var, _get_zval_ptr_cv(&opline->op1, EX(Ts), BP_VAR_R), &opline->op2.u.constant); EX(opline)++; return 0; }
Классы typrdef struct _zend_class_entry { char type; char *name; zend_class_entry *parent; zend_uint ce_flags; HashTable function_table; HashTable default_properties; HashTable properties_info; HashTable *static_members; HashTable constants_table; zend_class_entry **interfaces; … } zend_class_entry;
Объекты typedef struct _zend_object_value { zend_uint handle; // Z_OBJ_HANDLE(zval) zend_object_handlers *handlers; // Z_OBJ_HT(zval) } zend_object_value; typedef struct _zend_object { zend_class_entry *ce; // Z_OBJCE(zval) HashTable *properties; // Z_OBJPROP(zval) } zend_object;