跟厂长学PHP7内核(五):系统分析生命周期

发布日期:2019-01-10

上篇文章讲述了模块初始化阶段之前的准备工作,本篇我来详细介绍PHP生命周期的五个阶段。

一、模块初始化阶段

我们先来看一下该阶段的每个函数的作用。

1.1、sapi_initialize_request_empty函数

// main/SAPI.cSAPI_API void sapi_initialize_empty_request(void){ SG(server_context) = NULL SG(request_info).request_method = NULL SG(request_info).auth_digest = SG(request_info).auth_user = SG(request_info).auth_password = NULL SG(request_info).content_type_dup = NULL}

这个函数主要为前面定义的SG宏中的成员变量进行初始化。

1.2、sapi_activate函数

// main/SAPI.cSAPI_API void sapi_activate(void){ zend_llist_init(&SG(sapi_headers).headers sizeof(sapi_header_struct) (void (*)(void *)) sapi_free_header 0) SG(sapi_headers).send_default_content_type = 1 SG(sapi_headers).http_status_line = NULL SG(sapi_headers).mimetype = NULL SG(headers_sent) = 0 ...... /* Handle request method */ if (SG(server_context)) { ...... if (sapi_module.activate) { sapi_module.activate() } } if (sapi_module.input_filter_init) { sapi_module.input_filter_init() }}

函数的前半部分主要还是对SG宏的成员变量进行初始化。后半部分先是调用了sapi_module_struct内部实现的activate函数,又调用了input_filter_init函数,但是在CLI模式并没有实现这两个函数,只是返回了NULL。代码如下:

NULL /* activate */

1.3、php_output_startup函数

//main/output.cPHPAPI void php_output_startup(void){ ZEND_INIT_MODULE_GLOBALS(output php_output_init_globals NULL) zend_hash_init(&php_output_handler_aliases 8 NULL NULL 1) zend_hash_init(&php_output_handler_conflicts 8 NULL NULL 1) zend_hash_init(&php_output_handler_reverse_conflicts 8 NULL reverse_conflict_dtor 1) php_output_direct = php_output_stdout}

我们先来看ZEND_INIT_MODULE_GLOBALS宏做了什么事情:

#define ZEND_INIT_MODULE_GLOBALS(module_name globals_ctor globals_dtor) globals_ctor(&module_name##_globals)

由代码得知,该宏只是做了一层替换,替换后的内容为:

php_output_init_globals(&output_globals)

php_output_init_globals函数又做了什么呢?

//main/output.cstatic inline void php_output_init_globals(zend_output_globals *G){ ZEND_TSRMLS_CACHE_UPDATE() memset(G 0 sizeof(*G))}

该函数通过memset函数对output_globals进行了内存相关的初始化,我们可以在main/php_output.h中的155行找到它的宏定义OG。

//main/php_output.h# define OG(v) (output_globals.v)

OG对应的结构体是php_output_init_globals的入参zend_output_globals,在这里花了些时间,因为没找到定义在哪里,最后发现它也是通过宏定义替换得来的,代码如下:

//main/php_output.hZEND_BEGIN_MODULE_GLOBALS(output) zend_stack handlers php_output_handler *active php_output_handler *running const char *output_start_filename int output_start_lineno int flagsZEND_END_MODULE_GLOBALS(output)

看似是定义了一个结构体,但是代码中又出现了两个宏,我们再来瞅瞅这两个宏是干嘛的:

//Zend/zend_API.h#define ZEND_BEGIN_MODULE_GLOBALS(module_name) typedef struct _zend_##module_name##_globals {#define ZEND_END_MODULE_GLOBALS(module_name) } zend_##module_name##_globals

原来只是做了个替换而已,替换后的代码如下:

//这个是意淫出来的代码typedef struct _zend_output_globals { zend_stack handlers php_output_handler *active php_output_handler *running const char *output_start_filename int output_start_lineno int flags} zend_output_globals

这才是zend_output_globals最纯粹的定义,写的很是骚气,差点看断片。这样看来我们的OG宏对应的就是这个结构体了,姑且认为它是PHP输出相关的结构体。我们继续往下看:

zend_hash_init(&php_output_handler_aliases 8 NULL NULL 1)zend_hash_init(&php_output_handler_conflicts 8 NULL NULL 1)zend_hash_init(&php_output_handler_reverse_conflicts 8 NULL reverse_conflict_dtor 1)php_output_direct = php_output_stdout

接下来又对三个HashTable进行了初始化,初始化完成后,将php_output_direct指针指向了php_output_stdout函数。php_output_stdout函数的作用是调用fwrite函数,输出字符串到stdout中。代码如下:

//main/output.cstatic size_t php_output_stdout(const char *str size_t str_len){ fwrite(str 1 str_len stdout) return str_len}

1.4、php_startup_ticks函数

int php_startup_ticks(void){ zend_llist_init(&PG(tick_functions) sizeof(struct st_tick_function) NULL 1) return SUCCESS}

这里又出现了一个PG宏,来看下它的定义

# define PG(v) (core_globals.v)

PG对应的结构体是core_globalscore_globals又对应_php_core_globals,代码如下

extern ZEND_API struct _php_core_globals core_globals

php_core_globals顾名思义,就是php核心的全局变量,定义很多PHP相关的参数,比如内存上限、是否显示错误信息、上传文件大小限制、输入输出编码、禁用的函数等等,这里不再赘述,感兴趣的同学可以去看一下源码。

//main/php_globals.hstruct _php_core_globals { zend_bool implicit_flush zend_long output_buffering zend_bool sql_safe_mode zend_bool enable_dl ......}

php_startup_ticks函数就是对PG宏的成员变量tick_functions进行初始化。

1.5、gc_globals_ctor函数

ZEND_API void gc_globals_ctor(void){ gc_globals_ctor_ex(&gc_globals)}

这里又出现了一个gc_globals,它是与垃圾回收相关的结构体,这段代码是对gc_globals进行初始化。

//Zend/zend_gc.ctypedef struct _zend_gc_globals { zend_bool gc_enabled zend_bool gc_active zend_bool gc_full gc_root_buffer *buf /* preallocated arrays of buffers */ ......} zend_gc_globals

1.6、zend_startup函数

++start_memory_manager++:初始化内存管理器,对结构体alloc_globals进行初始化。

++virtual_cwd_startup++:virtual_cwd_startup初始化了cwd_globals,根据源码可以看出成员变量都与realpath_cache有关,realpath_cache是什么呢?我们平时在写代码的时候,经常会使用include、include_once、require、require_once等语句导入文件,如果每次使用这些语句都要去对应的目录中寻找目标文件,势必会降低性能,所以官方加入了缓存,以便PHP再次使用时不必到include_path中查找,加快PHP的执行速度。

//Zend/zend_virtual_cwd.ctypedef struct _virtual_cwd_globals { cwd_state cwd zend_long realpath_cache_size zend_long realpath_cache_size_limit zend_long realpath_cache_ttl realpath_cache_bucket *realpath_cache[1024]} virtual_cwd_globals

++zend_startup_extensions_mechanism++。启动扩展机制,初始化zend_extensions结构体。

int zend_startup_extensions_mechanism(){ /* Startup extensions mechanism */ zend_llist_init(&zend_extensions sizeof(zend_extension) (void (*)(void *)) zend_extension_dtor 1) last_resource_number = 0 return SUCCESS}

++提供编译与执行入口++

zend_compile_file = compile_filezend_execute_ex = execute_ex

++zend_init_opcodes_handlers++。初始化Zend虚拟机的handler

++初始化CG、EG++。初始化CG(function_table)、CG(class_table)、CG(auto_globals)、EG(zend_constants)。

GLOBAL_FUNCTION_TABLE = (HashTable *) malloc(sizeof(HashTable))GLOBAL_CLASS_TABLE = (HashTable *) malloc(sizeof(HashTable))GLOBAL_AUTO_GLOBALS_TABLE = (HashTable *) malloc(sizeof(HashTable))GLOBAL_CONSTANTS_TABLE = (HashTable *) malloc(sizeof(HashTable))

++ini_scanner_globals_ctor++。初始化ini_scanner_globals

++php_scanner_globals_ctor++。初始化language_scanner_globals

++zend_set_default_compile_time_values++。设置了编译相关的配置。

//Zend/zend.cstatic void zend_set_default_compile_time_values(void) /* {{{ */{ /* default compile-time values */ CG(short_tags) = short_tags_default CG(compiler_options) = compiler_options_default}

++EG(error_reporting)++。EG宏就是executor_globals,Zend执行器相关的全局变量,在这里对我们熟知的error_reporting进行配置。

//Zend/zend_globals.hstruct _zend_executor_globals { zval uninitialized_zval zval error_zval /* symbol table cache */ zend_array *symtable_cache[SYMTABLE_CACHE_SIZE] zend_array **symtable_cache_limit zend_array **symtable_cache_ptr ......}

++zend_interned_strings_init++。初始化内部字符串。

//Zend/zend_string.cvoid zend_interned_strings_init(void){#ifndef ZTS zend_string *str zend_hash_init(&CG(interned_strings) 1024 NULL _str_dtor 1) ......}

++zend_startup_builtin_functions++。初始化内部函数。

//Zend/zend_builtin_functions.cint zend_startup_builtin_functions(void) /* {{{ */{ zend_builtin_module.module_number = 0 zend_builtin_module.type = MODULE_PERSISTENT return (EG(current_module) = zend_register_module_ex(&zend_builtin_module)) == NULL ? FAILURE : SUCCESS}

++zend_register_standard_constants++。注册常量,比如E_ERROR、E_WARNING、E_NOTICE、E_CORE_ERROR等。

//Zend/zend_constants.cvoid zend_register_standard_constants(void){ REGISTER_MAIN_LONG_CONSTANT("E_ERROR" E_ERROR CONST_PERSISTENT | CONST_CS) REGISTER_MAIN_LONG_CONSTANT("E_RECOVERABLE_ERROR" E_RECOVERABLE_ERROR CONST_PERSISTENT | CONST_CS) REGISTER_MAIN_LONG_CONSTANT("E_WARNING" E_WARNING CONST_PERSISTENT | CONST_CS) ......}

++zend_register_auto_global++。将GLOBALS加入CG(auto_globals)。

//Zend/zend.czend_register_auto_global(zend_string_init("GLOBALS" sizeof("GLOBALS") - 1 1) 1 php_auto_globals_create_globals)

++zend_init_rsrc_plist++。初始化持久化符号表。

int zend_init_rsrc_plist(void){ zend_hash_init_ex(&EG(persistent_list) 8 NULL plist_entry_destructor 1 0) return SUCCESS}

++zend_init_exception_op++。初始化EG(exception_op)。

++zend_init_call_trampoline_op++。初始化EG(call_trampoline_op)。

++zend_ini_startup++。初始化与php.ini解析相关的变量。

//Zend/zend_ini.cZEND_API int zend_ini_startup(void) /* {{{ */{ registered_zend_ini_directives = (HashTable *) malloc(sizeof(HashTable)) EG(ini_directives) = registered_zend_ini_directives EG(modified_ini_directives) = NULL EG(error_reporting_ini_entry) = NULL zend_hash_init_ex(registered_zend_ini_directives 128 NULL free_ini_entry 1 0) return SUCCESS}

1.7、zend_register_list_destructors_ex函数

初始化析构函数

//Zend/zend_list.cZEND_API int zend_register_list_destructors_ex(rsrc_dtor_func_t ld rsrc_dtor_func_t pld const char *type_name int module_number){ zend_rsrc_list_dtors_entry *lde zval zv lde = malloc(sizeof(zend_rsrc_list_dtors_entry)) lde->list_dtor_ex = ld lde->plist_dtor_ex = pld lde->module_number = module_number lde->resource_id = list_destructors.nNextFreeElement lde->type_name = type_name ZVAL_PTR(&zv lde) if (zend_hash_next_index_insert(&list_destructors &zv) == NULL) { return FAILURE } return list_destructors.nNextFreeElement-1}

1.8、php_binary_init函数

获取PHP执行的二进制路径

static void php_binary_init(void){ char *binary_location#ifdef PHP_WIN32 binary_location = (char *)malloc(MAXPATHLEN) if (GetModuleFileName(0 binary_location MAXPATHLEN) == 0) { free(binary_location) PG(php_binary) = NULL }#else if (sapi_module.executable_location) { binary_location = (char *)malloc(MAXPATHLEN) if (!strchr(sapi_module.executable_location "/")) { char *envpath *path int found = 0 ......}

1.9、php_output_register_constants函数

初始化输出相关的预定义常量

//main/output.cPHPAPI void php_output_register_constants(void){ REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_START" PHP_OUTPUT_HANDLER_START CONST_CS | CONST_PERSISTENT) REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_WRITE" PHP_OUTPUT_HANDLER_WRITE CONST_CS | CONST_PERSISTENT) REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_FLUSH" PHP_OUTPUT_HANDLER_FLUSH CONST_CS | CONST_PERSISTENT) REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_CLEAN" PHP_OUTPUT_HANDLER_CLEAN CONST_CS | CONST_PERSISTENT) REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_FINAL" PHP_OUTPUT_HANDLER_FINAL CONST_CS | CONST_PERSISTENT) REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_CONT" PHP_OUTPUT_HANDLER_WRITE CONST_CS | CONST_PERSISTENT) REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_END" PHP_OUTPUT_HANDLER_FINAL CONST_CS | CONST_PERSISTENT) REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_CLEANABLE" PHP_OUTPUT_HANDLER_CLEANABLE CONST_CS | CONST_PERSISTENT) REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_FLUSHABLE" PHP_OUTPUT_HANDLER_FLUSHABLE CONST_CS | CONST_PERSISTENT) REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_REMOVABLE" PHP_OUTPUT_HANDLER_REMOVABLE CONST_CS | CONST_PERSISTENT) REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_STDFLAGS" PHP_OUTPUT_HANDLER_STDFLAGS CONST_CS | CONST_PERSISTENT) REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_STARTED" PHP_OUTPUT_HANDLER_STARTED CONST_CS | CONST_PERSISTENT) REGISTER_MAIN_LONG_CONSTANT("PHP_OUTPUT_HANDLER_DISABLED" PHP_OUTPUT_HANDLER_DISABLED CONST_CS | CONST_PERSISTENT)}

1.10、php_rfc1867_register_constants函数

注册文件上传相关的预定义常量

//main/rfc1867.cvoid php_rfc1867_register_constants(void) /* {{{ */{ REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_OK" UPLOAD_ERROR_OK CONST_CS | CONST_PERSISTENT) REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_INI_SIZE" UPLOAD_ERROR_A CONST_CS | CONST_PERSISTENT) REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_FORM_SIZE" UPLOAD_ERROR_B CONST_CS | CONST_PERSISTENT) REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_PARTIAL" UPLOAD_ERROR_C CONST_CS | CONST_PERSISTENT) REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_NO_FILE" UPLOAD_ERROR_D CONST_CS | CONST_PERSISTENT) REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_NO_TMP_DIR" UPLOAD_ERROR_E CONST_CS | CONST_PERSISTENT) REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_CANT_WRITE" UPLOAD_ERROR_F CONST_CS | CONST_PERSISTENT) REGISTER_MAIN_LONG_CONSTANT("UPLOAD_ERR_EXTENSION" UPLOAD_ERROR_X CONST_CS | CONST_PERSISTENT)}

1.11、php_init_config函数

初始化配置文件php.ini,并通过zend_parse_ini_file解析。

1.12、zend_register_standard_ini_entries函数

初始化ini相关的变量

void zend_register_standard_ini_entries(void) /* {{{ */{ int module_number = 0 REGISTER_INI_ENTRIES()}

1.13、php_startup_auto_globals函数

注册我们熟知的全局变量$_GET、$_POST、$_COOKIE等等

//main/php_variables.cvoid php_startup_auto_globals(void){ zend_register_auto_global(zend_string_init("_GET" sizeof("_GET")-1 1) 0 php_auto_globals_create_get) zend_register_auto_global(zend_string_init("_POST" sizeof("_POST")-1 1) 0 php_auto_globals_create_post) zend_register_auto_global(zend_string_init("_COOKIE" sizeof("_COOKIE")-1 1) 0 php_auto_globals_create_cookie) zend_register_auto_global(zend_string_init("_SERVER" sizeof("_SERVER")-1 1) PG(auto_globals_jit) php_auto_globals_create_server) zend_register_auto_global(zend_string_init("_ENV" sizeof("_ENV")-1 1) PG(auto_globals_jit) php_auto_globals_create_env) zend_register_auto_global(zend_string_init("_REQUEST" sizeof("_REQUEST")-1 1) PG(auto_globals_jit) php_auto_globals_create_request) zend_register_auto_global(zend_string_init("_FILES" sizeof("_FILES")-1 1) 0 php_auto_globals_create_files)}

1.14、php_startup_sapi_content_types函数

初始化针对不同内容类型的处理函数

//main/php_content_types.cint php_startup_sapi_content_types(void){ sapi_register_default_post_reader(php_default_post_reader) sapi_register_treat_data(php_default_treat_data) sapi_register_input_filter(php_default_input_filter NULL) return SUCCESS}

二、请求初始化阶段

函数说明
php_output_activate()重置输出全局变量,初始化输出相关堆栈
zend_activate()初始化Zend引擎
sapi_activate()初始化SG宏,调各sapi钩子函数activate
zend_signal_activate()信号处理
zend_set_timeout()设置超时时间
php_hash_environment()初始化PHP请求的全局变量
zend_activate_modules()调用各扩展定义的request_startup钩子函数

2.1、php_output_activate

重新为output_globals分配内存,初始化与输出处理程序相关的堆栈,并将OG宏的flags设置为激活状态。

//main/output.cPHPAPI int php_output_activate(void){#ifdef ZTS memset((*((void ***) ZEND_TSRMLS_CACHE))[TSRM_UNSHUFFLE_RSRC_ID(output_globals_id)] 0 sizeof(zend_output_globals))#else memset(&output_globals 0 sizeof(zend_output_globals))#endif zend_stack_init(&OG(handlers) sizeof(php_output_handler *)) OG(flags) |= PHP_OUTPUT_ACTIVATED return SUCCESS}

2.2、zend_activate

zend引擎的初始化,主要作用为重置垃圾回收、初始化编译器、初始化执行器、初始化扫描器。

函数说明
gc_reset()重置垃圾回收
init_compiler()初始化编译器
init_executor()初始化执行器
startup_scanner()初始化扫描器

2.3、sapi_activate

对SG宏内的一些变量进行初始化,并调用当前sapi_module_struct中定义的钩子函数activate()以及input_filter_init(),但是在cli模式下,这两个钩子函数都没有实现,返回了null。

//main/SAPI.cSAPI_API void sapi_activate(void){ zend_llist_init(&SG(sapi_headers).headers sizeof(sapi_header_struct) (void (*)(void *)) sapi_free_header 0) SG(sapi_headers).send_default_content_type = 1 /* SG(sapi_headers).http_response_code = 200 */ SG(sapi_headers).http_status_line = NULL SG(sapi_headers).mimetype = NULL SG(headers_sent) = 0 ZVAL_UNDEF(&SG(callback_func)) SG(read_post_bytes) = 0 SG(request_info).request_body = NULL ......}

2.4、php_hash_environment

为http_globals分配内存,初始化auto_globals,解析请求参数并存放到全局变量中。

PHPAPI int php_hash_environment(void){ memset(PG(http_globals) 0 sizeof(PG(http_globals))) zend_activate_auto_globals() if (PG(register_argc_argv)) { php_build_argv(SG(request_info).query_string &PG(http_globals)[TRACK_VARS_SERVER]) } return SUCCESS}

2.5、zend_activate_modules

该函数通过遍历注册在module_registry的所有模块,调用每个模块的钩子函数request_startup()进行初始化。

ZEND_API void zend_activate_modules(void) /* {{{ */{ zend_module_entry **p = module_request_startup_handlers while (*p) { zend_module_entry *module = *p if (module->request_startup_func(module->type module->module_number)==FAILURE) { zend_error(E_WARNING "request_startup() for %s module failed" module->name) exit(1) } p++ }}

三、执行脚本阶段

函数说明
compile_file()-
open_file_for_scanning()读取PHP代码内容
zend_parse()词法语法分析生成AST
init_op_array()初始化op_array
zend_compile_top_stmt()将AST转换为op_array
pass_two()设置op_array中对应zend虚拟机的handler
zend_execute()执行op_array

执行阶段的入口是php_execute_script函数,该函数又调用了zend_execute_scripts函数,看了这段代码的同学可能会找不到上图表中所提到的compile_file()函数,其实它在模块初始化阶段就已经将compile_file赋值给了zend_compile_file。

//Zend/zend.czend_compile_file = compile_file

执行阶段调用示意图:

四、请求关闭阶段

请求关闭阶段主要内容是调用各模块的关闭函数和析构函数、输出缓冲区内容、调用各扩展的钩子函数RSHUTDOWN、关闭编译器和执行器以及还原PHP配置等。

五、模块关闭阶段

虽然请求关闭和模块关闭看起来是两个截然不同的阶段,但阅读完源码后发现并看不出两个阶段的区别。它的主要作用是调用模块的flush函数、清理符号表、销毁全局变量、关闭内存管理和垃圾回收、关闭输出等。

1 0 9)