php 启动过程 - sapi MINIT 过程
sapi 概念
- sapi 是 php 的应用编程接口, server 端接收请求通过 sapi 接口层交给 php 处理
- 不同的 server 端底层实现不同, 相应的数据结构已经方法也有所不同, 但是对于 php 层面来说是一样的, 就是因为 sapi 层的存在
- sapi 层对不同的 server 端进行了封装, 让 php 在处理时, 采用统一的处理方法, 并不感知底层到底是什么 server 端
sapi 生命周期前的启动过程 (以 apache2 为例)
-
apache 加载 php 模块
-
httpd.conf 配置
-
LoadModule php5_module modules/mod_php5.so -
php5_module 是模块名称
-
apache 在启动加载模块时会根据模块名查找并加载模块, apache 模块文件必须是 "mod_" 开头的文件, 对于 php 来说, 则是 mod_php5.c
-
apache 的每个模块均为 module 结构体
-
AP_MODULE_DECLARE_DATA module php5_module = { STANDARD20_MODULE_STUFF, create_php_config, /* create per-directory config structure */ merge_php_config, /* merge per-directory config structures */ NULL, /* create per-server config structure */ NULL, /* merge per-server config structures */ // 模块指令集合 php_dir_cmds, /* command apr_table_t */ // php 模块注册钩子, 服务启动时注册 php_ap2_register_hook /* register hooks */ }; -
php_dir_cmds 为模块指令集, 当 apache 接收到指令后, 会遍历每个模块的指令集, 查看哪个模块能处理该指令, 进而调用相应处理函数
-
php_ap2_register_hook 为模块注册钩子, 当 apache 启动加载模块时调用
-
void php_ap2_register_hook(apr_pool_t *p) { ap_hook_pre_config(php_pre_config, NULL, NULL, APR_HOOK_MIDDLE); // apache2 的注册钩子 ap_hook_post_config(php_apache_server_startup, NULL, NULL, APR_HOOK_MIDDLE); // apache2 处理请求时的钩子 ap_hook_handler(php_handler, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_child_init(php_apache_child_init, NULL, NULL, APR_HOOK_MIDDLE); } -
定义位置在 sapi/apache2handler/sapi_apache2.c
-
ap_hook_pre_config, ap_hook_post_config, ap_hook_child_init 这三个钩子均在 apache 启动时调用, ap_hook_handler 钩子在每次处理请求时均会调用, 在 ap_hook_post_config 中启动 php
-
调用 php_apache_server_startup
-
static int php_apache_server_startup(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) { // 省略 if (apache2_php_ini_path_override) { apache2_sapi_module.php_ini_path_override = apache2_php_ini_path_override; } #ifdef ZTS // 启动线程安全资源管理 tsrm_startup(1, 1, 0, NULL); #endif // sapi 启动 sapi_startup(&apache2_sapi_module); // apache2_sapi_module.startup(&apache2_sapi_module); apr_pool_cleanup_register(pconf, NULL, php_apache_server_shutdown, apr_pool_cleanup_null); php_apache_add_version(pconf); return OK; }
-
-
-
-
-
-
sapi 启动
-
sapi 模块结构体
static sapi_module_struct apache2_sapi_module = { "apache2handler", "Apache 2.0 Handler", php_apache2_startup, /* startup */ php_module_shutdown_wrapper, /* shutdown */ NULL, /* activate */ NULL, /* deactivate */ php_apache_sapi_ub_write, /* unbuffered write */ php_apache_sapi_flush, /* flush */ php_apache_sapi_get_stat, /* get uid */ php_apache_sapi_getenv, /* getenv */ php_error, /* error handler */ php_apache_sapi_header_handler, /* header handler */ php_apache_sapi_send_headers, /* send headers handler */ NULL, /* send header handler */ php_apache_sapi_read_post, /* read POST data */ php_apache_sapi_read_cookies, /* read Cookies */ php_apache_sapi_register_variables, php_apache_sapi_log_message, /* Log message */ php_apache_sapi_get_request_time, /* Request Time */ NULL, /* Child Terminate */ STANDARD_SAPI_MODULE_PROPERTIES }; -
调用 tsrm_startup (启动线程安全资源管理, 此处不做进一步展开, 在线程安全时单独展开)
-
调用 sapi_startup (main/SAPI.c)
SAPI_API void sapi_startup(sapi_module_struct *sf) { #ifdef ZEND_SIGNALS zend_signal_startup(); #endif sf->ini_entries = NULL; sapi_module = *sf; #ifdef ZTS ts_allocate_id(&sapi_globals_id, sizeof(sapi_globals_struct), (ts_allocate_ctor) sapi_globals_ctor, (ts_allocate_dtor) sapi_globals_dtor); # ifdef PHP_WIN32 _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); # endif #else sapi_globals_ctor(&sapi_globals); #endif virtual_cwd_startup(); /* Could use shutdown to free the main cwd but it would just slow it down for CGI */ #ifdef PHP_WIN32 tsrm_win32_startup(); #endif reentrancy_startup(); }-
zend_signal_startup: 信号系统
-
sapi_globals_ctor: 创建 sapi 全局结构
-
static void sapi_globals_ctor(sapi_globals_struct *sapi_globals TSRMLS_DC) { memset(sapi_globals, 0, sizeof(*sapi_globals)); zend_hash_init_ex(&sapi_globals->known_post_content_types, 5, NULL, NULL, 1, 0); php_setup_sapi_content_types(TSRMLS_C); } -
typedef struct _sapi_globals_struct { // server 环境上下文 void *server_context; // 请求信息 sapi_request_info request_info; // 头信息 sapi_headers_struct sapi_headers; // post 字节数 int read_post_bytes; // 是否发送头 unsigned char headers_sent; struct stat global_stat; // 默认 mime 类型 char *default_mimetype; // 默认字符集 char *default_charset; // 上传文件 HashTable *rfc1867_uploaded_files; // post 请求最大大小 long post_max_size; int options; // sapi 是否已启动 zend_bool sapi_started; // 请求时间 double global_request_time; // 已知的 post 的 content_type HashTable known_post_content_types; zval *callback_func; zend_fcall_info_cache fci_cache; zend_bool callback_run; } sapi_globals_struct; -
调用 php_setup_sapi_contents_types, 设置 post 请求相关的参数和处理方法
int php_setup_sapi_content_types(TSRMLS_D) { sapi_register_post_entries(php_post_entries TSRMLS_CC); return SUCCESS; }static sapi_post_entry php_post_entries[] = { { DEFAULT_POST_CONTENT_TYPE, sizeof(DEFAULT_POST_CONTENT_TYPE)-1, sapi_read_standard_form_data, php_std_post_handler }, { MULTIPART_CONTENT_TYPE, sizeof(MULTIPART_CONTENT_TYPE)-1, NULL, rfc1867_post_handler }, { NULL, 0, NULL, NULL } };struct _sapi_post_entry { char *content_type; uint content_type_len; void (*post_reader)(TSRMLS_D); void (*post_handler)(char *content_type_dup, void *arg TSRMLS_DC); };-
调用 sapi_register_post_entries
-
SAPI_API int sapi_register_post_entries(sapi_post_entry *post_entries TSRMLS_DC) { sapi_post_entry *p=post_entries; while (p->content_type) { if (sapi_register_post_entry(p TSRMLS_CC) == FAILURE) { return FAILURE; } p++; } return SUCCESS; } -
SAPI_API int sapi_register_post_entry(sapi_post_entry *post_entry TSRMLS_DC) { // 若 sapi 已启动并且正在执行当中, 不允许注册 post 结构体 if (SG(sapi_started) && EG(in_execution)) { return FAILURE; } return zend_hash_add(&SG(known_post_content_types), post_entry->content_type, post_entry->content_type_len+1, (void *) post_entry, sizeof(sapi_post_entry), NULL); }
-
-
-
-
-
调用 apache2_sapi_module.startup
static int php_apache2_startup(sapi_module_struct *sapi_module) { if (php_module_startup(sapi_module, &php_apache_module, 1)==FAILURE) { return FAILURE; } return SUCCESS; }-
php_apache_module 参数是 zend 引擎封装的模块结构
-
zend_module_entry php_apache_module = { STANDARD_MODULE_HEADER, "apache2handler", apache_functions, PHP_MINIT(apache), // 扩展为 zm_startup_##module PHP_MSHUTDOWN(apache), // 扩展为 zm_shutdown_##module NULL, NULL, PHP_MINFO(apache), // 扩展为 zm_info_##module NULL, STANDARD_MODULE_PROPERTIES }; -
struct _zend_module_entry { unsigned short size; unsigned int zend_api; unsigned char zend_debug; unsigned char zts; const struct _zend_ini_entry *ini_entry; const struct _zend_module_dep *deps; // 模块名称 const char *name; const struct _zend_function_entry *functions; // 模块启动时调用的函数 int (*module_startup_func)(INIT_FUNC_ARGS); // 模块关闭时调用的函数 int (*module_shutdown_func)(SHUTDOWN_FUNC_ARGS); // 请求开始时调用的函数 int (*request_startup_func)(INIT_FUNC_ARGS); // 请求结束时调用的函数 int (*request_shutdown_func)(SHUTDOWN_FUNC_ARGS); // 模块信息函数 void (*info_func)(ZEND_MODULE_INFO_FUNC_ARGS); // 版本 const char *version; size_t globals_size; #ifdef ZTS ts_rsrc_id* globals_id_ptr; #else void* globals_ptr; #endif void (*globals_ctor)(void *global TSRMLS_DC); void (*globals_dtor)(void *global TSRMLS_DC); int (*post_deactivate_func)(void); int module_started; unsigned char type; void *handle; int module_number; const char *build_id; };
-
-
调用 php_module_startup (main/main.c)
-
int php_module_startup(sapi_module_struct *sf, zend_module_entry *additional_modules, uint num_additional_modules) { zend_utility_functions zuf; zend_utility_values zuv; int retval = SUCCESS, module_number=0; /* for REGISTER_INI_ENTRIES() */ char *php_os; zend_module_entry *module; // 省略 ... module_shutdown = 0; module_startup = 1; // 初始化空请求 sapi_initialize_empty_request(TSRMLS_C); // sapi 激活 sapi_activate(TSRMLS_C); if (module_initialized) { return SUCCESS; } sapi_module = *sf; // php 输出功能启动 php_output_startup(); // 设置 zend 引擎使用的函数 zuf.error_function = php_error_cb; zuf.printf_function = php_printf; zuf.write_function = php_output_wrapper; zuf.fopen_function = php_fopen_wrapper_for_zend; zuf.message_handler = php_message_handler_for_zend; zuf.block_interruptions = sapi_module.block_interruptions; zuf.unblock_interruptions = sapi_module.unblock_interruptions; zuf.get_configuration_directive = php_get_configuration_directive_for_zend; zuf.ticks_function = php_run_ticks; zuf.on_timeout = php_on_timeout; zuf.stream_open_function = php_stream_open_for_zend; zuf.vspprintf_function = vspprintf; zuf.getenv_function = sapi_getenv; zuf.resolve_path_function = php_resolve_path_for_zend; // zend 引擎启动 zend_startup(&zuf, NULL TSRMLS_CC); // 省略 ... // 创建垃圾回收机制全局结构体 gc_globals_ctor(TSRMLS_C); // 省略 ... // 设置全局结构体的一些值 EG(bailout) = NULL; EG(error_reporting) = E_ALL & ~E_NOTICE; EG(active_symbol_table) = NULL; PG(header_is_being_sent) = 0; SG(request_info).headers_only = 0; SG(request_info).argv0 = NULL; SG(request_info).argc=0; SG(request_info).argv=(char **)NULL; PG(connection_status) = PHP_CONNECTION_NORMAL; PG(during_request_startup) = 0; PG(last_error_message) = NULL; PG(last_error_file) = NULL; PG(last_error_lineno) = 0; EG(error_handling) = EH_NORMAL; EG(exception_class) = NULL; PG(disable_functions) = NULL; PG(disable_classes) = NULL; EG(exception) = NULL; EG(objects_store).object_buckets = NULL; // 省略 ... // 注册一些常量 REGISTER_MAIN_STRINGL_CONSTANT("PHP_VERSION", PHP_VERSION, sizeof(PHP_VERSION)-1, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_LONG_CONSTANT("PHP_MAJOR_VERSION", PHP_MAJOR_VERSION, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_LONG_CONSTANT("PHP_MINOR_VERSION", PHP_MINOR_VERSION, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_LONG_CONSTANT("PHP_RELEASE_VERSION", PHP_RELEASE_VERSION, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_STRINGL_CONSTANT("PHP_EXTRA_VERSION", PHP_EXTRA_VERSION, sizeof(PHP_EXTRA_VERSION) - 1, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_LONG_CONSTANT("PHP_VERSION_ID", PHP_VERSION_ID, CONST_PERSISTENT | CONST_CS); #ifdef ZTS REGISTER_MAIN_LONG_CONSTANT("PHP_ZTS", 1, CONST_PERSISTENT | CONST_CS); #else REGISTER_MAIN_LONG_CONSTANT("PHP_ZTS", 0, CONST_PERSISTENT | CONST_CS); #endif REGISTER_MAIN_LONG_CONSTANT("PHP_DEBUG", PHP_DEBUG, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_STRINGL_CONSTANT("PHP_OS", php_os, strlen(php_os), CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_STRINGL_CONSTANT("PHP_SAPI", sapi_module.name, strlen(sapi_module.name), CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_STRINGL_CONSTANT("DEFAULT_INCLUDE_PATH", PHP_INCLUDE_PATH, sizeof(PHP_INCLUDE_PATH)-1, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_STRINGL_CONSTANT("PEAR_INSTALL_DIR", PEAR_INSTALLDIR, sizeof(PEAR_INSTALLDIR)-1, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_STRINGL_CONSTANT("PEAR_EXTENSION_DIR", PHP_EXTENSION_DIR, sizeof(PHP_EXTENSION_DIR)-1, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_STRINGL_CONSTANT("PHP_EXTENSION_DIR", PHP_EXTENSION_DIR, sizeof(PHP_EXTENSION_DIR)-1, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_STRINGL_CONSTANT("PHP_PREFIX", PHP_PREFIX, sizeof(PHP_PREFIX)-1, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_STRINGL_CONSTANT("PHP_BINDIR", PHP_BINDIR, sizeof(PHP_BINDIR)-1, CONST_PERSISTENT | CONST_CS); #ifndef PHP_WIN32 REGISTER_MAIN_STRINGL_CONSTANT("PHP_MANDIR", PHP_MANDIR, sizeof(PHP_MANDIR)-1, CONST_PERSISTENT | CONST_CS); #endif REGISTER_MAIN_STRINGL_CONSTANT("PHP_LIBDIR", PHP_LIBDIR, sizeof(PHP_LIBDIR)-1, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_STRINGL_CONSTANT("PHP_DATADIR", PHP_DATADIR, sizeof(PHP_DATADIR)-1, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_STRINGL_CONSTANT("PHP_SYSCONFDIR", PHP_SYSCONFDIR, sizeof(PHP_SYSCONFDIR)-1, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_STRINGL_CONSTANT("PHP_LOCALSTATEDIR", PHP_LOCALSTATEDIR, sizeof(PHP_LOCALSTATEDIR)-1, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_STRINGL_CONSTANT("PHP_CONFIG_FILE_PATH", PHP_CONFIG_FILE_PATH, strlen(PHP_CONFIG_FILE_PATH), CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_STRINGL_CONSTANT("PHP_CONFIG_FILE_SCAN_DIR", PHP_CONFIG_FILE_SCAN_DIR, sizeof(PHP_CONFIG_FILE_SCAN_DIR)-1, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_STRINGL_CONSTANT("PHP_SHLIB_SUFFIX", PHP_SHLIB_SUFFIX, sizeof(PHP_SHLIB_SUFFIX)-1, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_STRINGL_CONSTANT("PHP_EOL", PHP_EOL, sizeof(PHP_EOL)-1, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_LONG_CONSTANT("PHP_MAXPATHLEN", MAXPATHLEN, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_LONG_CONSTANT("PHP_INT_MAX", LONG_MAX, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_LONG_CONSTANT("PHP_INT_SIZE", sizeof(long), CONST_PERSISTENT | CONST_CS); #ifdef PHP_WIN32 REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_VERSION_MAJOR", EG(windows_version_info).dwMajorVersion, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_VERSION_MINOR", EG(windows_version_info).dwMinorVersion, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_VERSION_BUILD", EG(windows_version_info).dwBuildNumber, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_VERSION_PLATFORM", EG(windows_version_info).dwPlatformId, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_VERSION_SP_MAJOR", EG(windows_version_info).wServicePackMajor, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_VERSION_SP_MINOR", EG(windows_version_info).wServicePackMinor, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_VERSION_SUITEMASK", EG(windows_version_info).wSuiteMask, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_VERSION_PRODUCTTYPE", EG(windows_version_info).wProductType, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_NT_DOMAIN_CONTROLLER", VER_NT_DOMAIN_CONTROLLER, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_NT_SERVER", VER_NT_SERVER, CONST_PERSISTENT | CONST_CS); REGISTER_MAIN_LONG_CONSTANT("PHP_WINDOWS_NT_WORKSTATION", VER_NT_WORKSTATION, CONST_PERSISTENT | CONST_CS); #endif // php 命令初始化, 主要是设置 php 命令路径 php_binary_init(TSRMLS_C); if (PG(php_binary)) { REGISTER_MAIN_STRINGL_CONSTANT("PHP_BINARY", PG(php_binary), strlen(PG(php_binary)), CONST_PERSISTENT | CONST_CS); } else { REGISTER_MAIN_STRINGL_CONSTANT("PHP_BINARY", "", 0, CONST_PERSISTENT | CONST_CS); } // 注册 php 输出功能相关的一些常量 php_output_register_constants(TSRMLS_C); // 注册 php 上传功能相关的一些常量 php_rfc1867_register_constants(TSRMLS_C); // 读取 php.ini 配置 if (php_init_config(TSRMLS_C) == FAILURE) { return FAILURE; } // 将 php.ini 文件读取的配置注册成 ini_entry REGISTER_INI_ENTRIES(); // 注册 zend 引擎的 ini_entry zend_register_standard_ini_entries(TSRMLS_C); /* Disable realpath cache if an open_basedir is set */ if (PG(open_basedir) && *PG(open_basedir)) { CWDG(realpath_cache_size_limit) = 0; } // php 流的初始化 if (php_init_stream_wrappers(module_number TSRMLS_CC) == FAILURE) { php_printf("PHP: Unable to initialize stream url wrappers.\n"); return FAILURE; } zuv.html_errors = 1; zuv.import_use_extension = ".php"; // 注册超全局数组 $_GET, $_POST, $_COOKIE, ... php_startup_auto_globals(TSRMLS_C); zend_set_utility_values(&zuv); // 启动 sapi 的处理请求功能, 设置一些处理函数 php_startup_sapi_content_types(TSRMLS_C); // 注册 php 内置扩展 (module_registry) if (php_register_internal_extensions_func(TSRMLS_C) == FAILURE) { php_printf("Unable to start builtin modules\n"); return FAILURE; } // 注册附加扩展 (module_registry) php_register_extensions_bc(additional_modules, num_additional_modules TSRMLS_CC); // 注册 php.ini 文件设置的扩展 (zend_extensions) php_ini_register_extensions(TSRMLS_C); // 启动各模块 (module_registry), 调用各模块的 startup 方法 zend_startup_modules(TSRMLS_C); // 启动各扩展 (zend_extentions), 调用各扩展的 startup 方法 zend_startup_extensions(); // 统计各模块的启动情况 zend_collect_module_handlers(TSRMLS_C); /* register additional functions */ if (sapi_module.additional_functions) { if (zend_hash_find(&module_registry, "standard", sizeof("standard"), (void**)&module)==SUCCESS) { EG(current_module) = module; zend_register_functions(NULL, sapi_module.additional_functions, NULL, MODULE_PERSISTENT TSRMLS_CC); EG(current_module) = NULL; } } // 禁用函数 php_disable_functions(TSRMLS_C); // 禁用类 php_disable_classes(TSRMLS_C); // 省略 ... // sapi sapi_deactivate(TSRMLS_C); module_startup = 0; // 关闭内存管理 shutdown_memory_manager(1, 0 TSRMLS_CC); zend_interned_strings_snapshot(TSRMLS_C); /* we're done */ return retval; } -
初始化 zend 引擎使用的一些函数和值
-
初始化 sapi_global 的一些值 (通过初始化空请求, sapi 激活)
-
初始化全局结构体的一些值
-
zend 引擎启动
-
设置一些常量
-
加载 php.ini 配置
-
注册各子模块和扩展
-
启动各子模块和扩展
-
禁用函数和类
-
-
-
调用 apr_pool_cleanup_register
-
sapi 生命周期
-
MINIT
若不细致区分 sapi 启动前的准备工作与启动过程, 上面的部分均可以看成是 sapi MINIT 过程 若细致区分, sapi 启动前的准备工作是在注册 php 内置扩展之前的部分; 之后的部分为 MINIT 过程 -
RINIT
-
RSHUTDOWN
-
MSHUTDOWN
对于以上内容的更细致的点, 比如线程资源安全, zend 引擎启动, 全局结构体, php.ini 如何加载配置等等等等, 由于篇幅问题, 不在此赘述, 内容太多消化不了, 会在后续一个一个详细剖析, 本节仅涉及到 sapi 生命周期当中的 MINIT 过程
浙公网安备 33010602011771号