跟厂长学PHP7内核(五):深入分析生命周期的模块初始化阶段

上篇我们讲到了模块初始化阶段,并得知它是由php_module_startup函数来实现的。该阶段的主要作用是初始化变量、常量;注册各种函数,比如工具、词法、语法函数等;解析配置文件;加载扩展;当然最重要的是计算出PHP二进制程序的路径,现在让我们来研究一下该函数都做了哪些工作。 1、sapi_initialize_request_empty函数 // main/SAPI.c SAPI_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宏中的成员变量进行初始化。 2、sapi_activate函数 // main/SAPI.c SAPI_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; SG(request_info).current_user = NULL; SG(request_info).current_user_length = 0; SG(request_info).no_headers = 0; SG(request_info).post_entry = NULL; SG(request_info).proto_num = 1000; /* Default to HTTP 1.0 */ SG(global_request_time) = 0; SG(post_read) = 0; /* It's possible to override this general case in the activate() callback, if necessary. */ if (SG(request_info).request_method && !strcmp(SG(request_info).request_method, "HEAD")) { SG(request_info).headers_only = 1; } else { SG(request_info).headers_only = 0; } SG(rfc1867_uploaded_files) = NULL; /* Handle request method */ if (SG(server_context)) { if (PG(enable_post_data_reading) && SG(request_info).content_type && SG(request_info).request_method && !strcmp(SG(request_info).request_method, "POST")) { /* HTTP POST may contain form data to be processed into variables * depending on given content type */ sapi_read_post_data(); } else { SG(request_info).content_type_dup = NULL; } /* Cookies */ SG(request_info).cookie_data = sapi_module.read_cookies(); 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 */ 3、php_output_startup函数 //main/output.c PHPAPI 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.c static 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.h ZEND_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 flags; ZEND_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.c static size_t php_output_stdout(const char *str, size_t str_len) { fwrite(str, 1, str_len, stdout); return str_len; } 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_globals,core_globals又对应_php_core_globals,代码如下 extern ZEND_API struct _php_core_globals core_globals; php_core_globals顾名思义,就是php核心的全局变量,定义很多PHP相关的参数,比如内存上限、是否显示错误信息、上传文件大小限制、输入输出编码、禁用的函数等等,这里不再赘述,感兴趣的同学可以去看一下源码。 //main/php_globals.h struct _php_core_globals { zend_bool implicit_flush; zend_long output_buffering; zend_bool sql_safe_mode; zend_bool enable_dl; char *output_handler; char *unserialize_callback_func; zend_long serialize_precision; zend_long memory_limit; zend_long max_input_time; zend_bool track_errors; zend_bool display_errors; zend_bool display_startup_errors; zend_bool log_errors; zend_long log_errors_max_len; zend_bool ignore_repeated_errors; zend_bool ignore_repeated_source; zend_bool report_memleaks; char *error_log; char *doc_root; char *user_dir; char *include_path; char *open_basedir; char *extension_dir; char *php_binary; char *sys_temp_dir; char *upload_tmp_dir; zend_long upload_max_filesize; char *error_append_string; char *error_prepend_string; char *auto_prepend_file; char *auto_append_file; char *input_encoding; char *internal_encoding; char *output_encoding; arg_separators arg_separator; char *variables_order; HashTable rfc1867_protected_variables; short connection_status; short ignore_user_abort; unsigned char header_is_being_sent; zend_llist tick_functions; zval http_globals[6]; zend_bool expose_php; zend_bool register_argc_argv; zend_bool auto_globals_jit; char *docref_root; char *docref_ext; zend_bool html_errors; zend_bool xmlrpc_errors; zend_long xmlrpc_error_number; zend_bool activated_auto_globals[8]; zend_bool modules_activated; zend_bool file_uploads; zend_bool during_request_startup; zend_bool allow_url_fopen; zend_bool enable_post_data_reading; zend_bool report_zend_debug; int last_error_type; char *last_error_message; char *last_error_file; int last_error_lineno; char *php_sys_temp_dir; char *disable_functions; char *disable_classes; zend_bool allow_url_include; zend_bool exit_on_timeout; #ifdef PHP_WIN32 zend_bool com_initialized; #endif zend_long max_input_nesting_level; zend_long max_input_vars; zend_bool in_user_include; char *user_ini_filename; zend_long user_ini_cache_ttl; char *request_order; zend_bool mail_x_header; char *mail_log; zend_bool in_error_log; #ifdef PHP_WIN32 zend_bool windows_show_crt_warning; #endif }; 而php_startup_ticks函数就是对PG宏的成员变量tick_functions进行初始化。 5、gc_globals_ctor函数 ZEND_API void gc_globals_ctor(void) { gc_globals_ctor_ex(&gc_globals); } 这里又出现了一个gc_globals,PHP的全局变量就是多啊,在这里我们只要先记住它是PHP垃圾回收相关的结构体即可,后面我们做详细介绍。这段代码是对gc_globals进行初始化。 //Zend/zend_gc.c typedef struct _zend_gc_globals { zend_bool gc_enabled; zend_bool gc_active; zend_bool gc_full; gc_root_buffer *buf; /* preallocated arrays of buffers */ gc_root_buffer roots; /* list of possible roots of cycles */ gc_root_buffer *unused; /* list of unused buffers */ gc_root_buffer *first_unused; /* pointer to first unused buffer */ gc_root_buffer *last_unused; /* pointer to last unused buffer */ gc_root_buffer to_free; /* list to free */ gc_root_buffer *next_to_free; uint32_t gc_runs; uint32_t collected; #if GC_BENCH uint32_t root_buf_length; uint32_t root_buf_peak; uint32_t zval_possible_root; uint32_t zval_buffered; uint32_t zval_remove_from_buffer; uint32_t zval_marked_grey; #endif } zend_gc_globals; 6、zend_startup函数 6.1、start_memory_manager 初始化内存管理器,对结构体alloc_globals进行初始化。 //Zend/zend_alloc.c static void alloc_globals_ctor(zend_alloc_globals *alloc_globals) { #if ZEND_MM_CUSTOM char *tmp = getenv("USE_ZEND_ALLOC"); if (tmp && !zend_atoi(tmp, 0)) { alloc_globals->mm_heap = malloc(sizeof(zend_mm_heap)); memset(alloc_globals->mm_heap, 0, sizeof(zend_mm_heap)); alloc_globals->mm_heap->use_custom_heap = ZEND_MM_CUSTOM_HEAP_STD; alloc_globals->mm_heap->custom_heap.std._malloc = __zend_malloc; alloc_globals->mm_heap->custom_heap.std._free = free; alloc_globals->mm_heap->custom_heap.std._realloc = __zend_realloc; return; } #endif #ifdef MAP_HUGETLB tmp = getenv("USE_ZEND_ALLOC_HUGE_PAGES"); if (tmp && zend_atoi(tmp, 0)) { zend_mm_use_huge_pages = 1; } #endif ZEND_TSRMLS_CACHE_UPDATE(); alloc_globals->mm_heap = zend_mm_init(); } 6.2、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.c typedef 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; 6.3、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; } 6.4、提供编译与执行入口 zend_compile_file = compile_file; zend_execute_ex = execute_ex; 6.5、zend_init_opcodes_handlers 初始化Zend虚拟机的handler 6.6、初始化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)); 6.7、ini_scanner_globals_ctor 初始化ini_scanner_globals 6.8、php_scanner_globals_ctor 初始化language_scanner_globals 6.9、zend_set_default_compile_time_values 设置了编译相关的配置 //Zend/zend.c static void zend_set_default_compile_time_values(void) /* {{{ */ { /* default compile-time values */ CG(short_tags) = short_tags_default; CG(compiler_options) = compiler_options_default; } 6.10、EG(error_reporting) = E_ALL & ~E_NOTICE; EG宏就是executor_globals,Zend执行器相关的全局变量,在这里对我们熟知的error_reporting进行配置。 //Zend/zend_globals.h struct _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; ...... } 6.11、zend_interned_strings_init 初始化内部字符串 //Zend/zend_string.c void zend_interned_strings_init(void) { #ifndef ZTS zend_string *str; zend_hash_init(&CG(interned_strings), 1024, NULL, _str_dtor, 1); ...... } 6.12、zend_startup_builtin_functions 初始化内部函数 //Zend/zend_builtin_functions.c int 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; } 6.13、zend_register_standard_constants 注册常量,比如E_ERROR、E_WARNING、E_NOTICE、E_CORE_ERROR等。 //Zend/zend_constants.c void 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); ...... } 6.14、zend_register_auto_global 将GLOBALS加入CG(auto_globals) //Zend/zend.c zend_register_auto_global(zend_string_init("GLOBALS", sizeof("GLOBALS") - 1, 1), 1, php_auto_globals_create_globals); 6.15、zend_init_rsrc_plist 初始化持久化符号表 int zend_init_rsrc_plist(void) {
50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信