Zend Engine изнутри Дмитрий Стогов. Немного истории Zend Engine...

Preview:

Citation preview

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)

Стадии работы PHP

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_paremeter TSRMLS_DC) {process(EG(symbol_table));

}some_function(0 TSRMLS_CC);

Менеджер памяти

emalloc()efree()erealloc()estrdup()estrndup()ecalloc()

$ USE_ZEND_ALLOC=0 valgrind php test.php

Значения (zval-коетейнер)

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_NULLIS_LONGIS_DOUBLEIS_BOOLIS_ARRAYIS_OBJECTIS_STRINGIS_RESOURCEIS_CONSTANTIS_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;

Ссылки

<?php

$a = 10;

$b =& $b;$a

$b

EG(symbol_table)

type

value

is_ref

refcount

IS_LONG

10

1

2

zval

HashTable

Присваивание и копирование при записи

<?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)

Функции PHP (op_array)

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;

Инструкции 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;

Инструкции 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_NOPZEND_ADDZEND_SUBZEND_IS_EQUALZEND_JMPZEND_JMPZZEND_ASSIGNZEND_DO_FCALLZEND_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;

Операнды (znode)

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_CONSTIS_CVIS_TMP_VARIS_VARIS_UNUSED

Пример компиляции (5.0)

<?php$a = “Hello”;

$b = “World”;

echo $a .” “. $b;

?>

// FETCH_W C(“a”) -> V(0)// ASSIGN V(0), C(“Hello”)// FETCH_W C(“b”) -> V(1)// ASSIGN V(1), C(“World”)// FETCH_R C(“a”) -> V(2)// CONCAT V(2), C(“ “) -> T(3)// FETCH_R C(“b”) -> V(4)// CONCAT T(3), V(4) -> T(5)// ECHO T(5)// RETURN C(NULL)

Пример компиляции (5.1)

<?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)

Глобальные данные 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_array TSRMLS_DC) {zend_execute_data execute_data;

// initializationEX(opline) = op_array->opcodes;while (1) {

switch (EX(opline)->opcode) {…case ZEND_RETURN;

…return;

}}

}

Call-threaded Executor (5.*)

void execute(zend_op_array *op_array TSRMLS_DC) {zend_execute_data execute_data;

// initializationEX(opline) = op_array->opcodes;while (1) {

if (EX(opline)->handler(&execute_data TSRMLS_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;

Вопросы?