php 启动过程 - reqeust RSHUTDOWN 过程
php 启动过程 - reqeust RSHUTDOWN 过程
概述
request RSHUTDOWN 过程在请求结束后调用
调用触发
- 同 request RINIT 过程一样, 先是用 apache 注册的钩子函数 php_handler 处理请求
- 执行 request RINIT 过程
- 执行 request execute 过程
- 调用 php_apache_request_dtor 开始进行 request RSHUTDOWN 过程
调用过程
php_handler 处理完请求后, 调用 php_apache_request_dtor 函数进行 request RSHUTDOWN 过程
static int php_handler(request_rec *r)
{
// 省略 ...
if (!parent_req) {
// 销毁释放请求
php_apache_request_dtor(r TSRMLS_CC);
ctx->request_processed = 1;
bucket = apr_bucket_eos_create(r->connection->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(brigade, bucket);
rv = ap_pass_brigade(r->output_filters, brigade);
if (rv != APR_SUCCESS || r->connection->aborted) {
zend_first_try {
php_handle_aborted_connection();
} zend_end_try();
}
apr_brigade_cleanup(brigade);
apr_pool_cleanup_run(r->pool, (void *)&SG(server_context), php_server_context_cleanup);
} else {
ctx->r = parent_req;
}
return OK;
}
调用 php_apache_request_dtor
static void php_apache_request_dtor(request_rec *r TSRMLS_DC)
{
php_request_shutdown(NULL);
}
调用 php_request_shutdown 函数
void php_request_shutdown(void *dummy)
{
zend_bool report_memleaks;
TSRMLS_FETCH();
// 内存泄漏信息
report_memleaks = PG(report_memleaks);
// 设置 opcode 执行相关变量为空
EG(opline_ptr) = NULL;
EG(active_op_array) = NULL;
// 销毁 tick 相关
php_deactivate_ticks(TSRMLS_C);
// 调用 register_shutdown_function 注册的 shutdown 函数
if (PG(modules_activated)) zend_try {
php_call_shutdown_functions(TSRMLS_C);
} zend_end_try();
// 调用析构函数
zend_try {
zend_call_destructors(TSRMLS_C);
} zend_end_try();
// flush 输出缓冲区
zend_try {
zend_bool send_buffer = SG(request_info).headers_only ? 0 : 1;
if (CG(unclean_shutdown) && PG(last_error_type) == E_ERROR &&
(size_t)PG(memory_limit) < zend_memory_usage(1 TSRMLS_CC)
) {
send_buffer = 0;
}
if (!send_buffer) {
php_output_discard_all(TSRMLS_C);
} else {
php_output_end_all(TSRMLS_C);
}
} zend_end_try();
// 重置 max_execution_time
zend_try {
zend_unset_timeout(TSRMLS_C);
} zend_end_try();
// 调用各子模块的 RSHUTDOWN 函数
if (PG(modules_activated)) {
zend_deactivate_modules(TSRMLS_C);
php_free_shutdown_functions(TSRMLS_C);
}
// 关闭 output
zend_try {
php_output_deactivate(TSRMLS_C);
} zend_end_try();
// 销毁 PG(http_globals)
zend_try {
int i;
for (i=0; i<NUM_TRACK_VARS; i++) {
if (PG(http_globals)[i]) {
zval_ptr_dtor(&PG(http_globals)[i]);
}
}
} zend_end_try();
// 释放错误信息
if (PG(last_error_message)) {
free(PG(last_error_message));
PG(last_error_message) = NULL;
}
if (PG(last_error_file)) {
free(PG(last_error_file));
PG(last_error_file) = NULL;
}
// 关闭临时目录
php_shutdown_temporary_directory();
// 关闭 扫描器, 执行器, 编译器, 初始化 ini_entries
zend_deactivate(TSRMLS_C);
// 调用各子模块的 post_deactivate_func 函数
zend_try {
zend_post_deactivate_modules(TSRMLS_C);
} zend_end_try();
// 关闭 sapi
zend_try {
sapi_deactivate(TSRMLS_C);
} zend_end_try();
/* 10. Destroy stream hashes */
zend_try {
php_shutdown_stream_hashes(TSRMLS_C);
} zend_end_try();
// 关闭内存管理器
zend_try {
shutdown_memory_manager(CG(unclean_shutdown) || !report_memleaks, 0 TSRMLS_CC);
} zend_end_try();
zend_interned_strings_restore(TSRMLS_C);
// 重置 max_execution_time
zend_try {
zend_unset_timeout(TSRMLS_C);
} zend_end_try();
#ifdef PHP_WIN32
if (PG(com_initialized)) {
CoUninitialize();
PG(com_initialized) = 0;
}
#endif
#ifdef HAVE_DTRACE
DTRACE_REQUEST_SHUTDOWN(SAFE_FILENAME(SG(request_info).path_translated), SAFE_FILENAME(SG(request_info).request_uri), (char *)SAFE_FILENAME(SG(request_info).request_method));
#endif /* HAVE_DTRACE */
}
php_call_shutdown_functions, 调用 register_shutdown_function 注册的 shutdown 函数
void php_call_shutdown_functions(TSRMLS_D)
{
if (BG(user_shutdown_function_names)) {
zend_try {
zend_hash_apply(BG(user_shutdown_function_names), (apply_func_t) user_shutdown_function_call TSRMLS_CC);
}
zend_end_try();
php_free_shutdown_functions(TSRMLS_C);
}
}
调用 zend_hash_apply, 实际上是对 BG(user_shutdown_function_names) 循环调用 user_shutdown_function_call 函数
ZEND_API void zend_hash_apply(HashTable *ht, apply_func_t apply_func TSRMLS_DC)
{
Bucket *p;
IS_CONSISTENT(ht);
HASH_PROTECT_RECURSION(ht);
p = ht->pListHead;
while (p != NULL) {
int result = apply_func(p->pData TSRMLS_CC);
if (result & ZEND_HASH_APPLY_REMOVE) {
p = zend_hash_apply_deleter(ht, p);
} else {
p = p->pListNext;
}
if (result & ZEND_HASH_APPLY_STOP) {
break;
}
}
HASH_UNPROTECT_RECURSION(ht);
}
user_shutdown_function_call 函数
static int user_shutdown_function_call(php_shutdown_function_entry *shutdown_function_entry TSRMLS_DC)
{
zval retval;
char *function_name;
if (!zend_is_callable(shutdown_function_entry->arguments[0], 0, &function_name TSRMLS_CC)) {
php_error(E_WARNING, "(Registered shutdown functions) Unable to call %s() - function does not exist", function_name);
if (function_name) {
efree(function_name);
}
return 0;
}
if (function_name) {
efree(function_name);
}
if (call_user_function(EG(function_table), NULL,
shutdown_function_entry->arguments[0],
&retval,
shutdown_function_entry->arg_count - 1,
shutdown_function_entry->arguments + 1
TSRMLS_CC ) == SUCCESS)
{
zval_dtor(&retval);
}
return 0;
}
zend_call_destructors, 调用各变量的析构函数 __destruct
void zend_call_destructors(TSRMLS_D)
{
zend_try {
shutdown_destructors(TSRMLS_C);
} zend_end_try();
}
调用 shutdown_destructors 函数
void shutdown_destructors(TSRMLS_D) /* {{{ */
{
zend_try {
int symbols;
do {
symbols = zend_hash_num_elements(&EG(symbol_table));
zend_hash_reverse_apply(&EG(symbol_table), (apply_func_t) zval_call_destructor TSRMLS_CC);
} while (symbols != zend_hash_num_elements(&EG(symbol_table)));
zend_objects_store_call_destructors(&EG(objects_store) TSRMLS_CC);
} zend_catch {
/* if we couldn't destruct cleanly, mark all objects as destructed anyway */
zend_objects_store_mark_destructed(&EG(objects_store) TSRMLS_CC);
} zend_end_try();
}
zend_hash_reverse_apply 对 EG(symbol_table) 循环调用 zval_call_destructor 函数
static int zval_call_destructor(zval **zv TSRMLS_DC)
{
if (Z_TYPE_PP(zv) == IS_OBJECT && Z_REFCOUNT_PP(zv) == 1) {
return ZEND_HASH_APPLY_REMOVE;
} else {
return ZEND_HASH_APPLY_KEEP;
}
}
调用 zend_objects_store_call_destructors, 调用各变量的析构函数
ZEND_API void zend_objects_store_call_destructors(zend_objects_store *objects TSRMLS_DC)
{
zend_uint i = 1;
for (i = 1; i < objects->top ; i++) {
if (objects->object_buckets[i].valid) {
struct _store_object *obj = &objects->object_buckets[i].bucket.obj;
if (!objects->object_buckets[i].destructor_called) {
objects->object_buckets[i].destructor_called = 1;
if (obj->dtor && obj->object) {
obj->refcount++;
// 调用析构函数
obj->dtor(obj->object, i TSRMLS_CC);
obj = &objects->object_buckets[i].bucket.obj;
obj->refcount--;
if (obj->refcount == 0) {
GC_REMOVE_ZOBJ_FROM_BUFFER(obj);
}
}
}
}
}
}
php_output_discard_all 或者 php_output_end_all, flush 输出缓冲区 (在后续对输出功能做讲解时在详细展开)
zend_deactivate_modules, 调用各子模块的 RSHUTDOWN 函数
void zend_deactivate_modules(TSRMLS_D)
{
EG(opline_ptr) = NULL; /* we're no longer executing anything */
zend_try {
if (EG(full_tables_cleanup)) {
// 若全部清除, 则对 module_registry 循环调用 module_registry_cleanup 来循环调用 各子模块的 request_shutdown_func 函数
zend_hash_reverse_apply(&module_registry, (apply_func_t) module_registry_cleanup TSRMLS_CC);
} else {
zend_module_entry **p = module_request_shutdown_handlers;
while (*p) {
zend_module_entry *module = *p;
// 循环调用各子模块的 request_shutdown_func 函数
module->request_shutdown_func(module->type, module->module_number TSRMLS_CC);
p++;
}
}
} zend_end_try();
}
php_output_deactivate, 关闭 output, 释放所有 handler
PHPAPI void php_output_deactivate(TSRMLS_D)
{
php_output_handler **handler = NULL;
if ((OG(flags) & PHP_OUTPUT_ACTIVATED)) {
php_output_header(TSRMLS_C);
OG(flags) ^= PHP_OUTPUT_ACTIVATED;
OG(active) = NULL;
OG(running) = NULL;
// 释放 handlers
if (OG(handlers).elements) {
while (SUCCESS == zend_stack_top(&OG(handlers), (void *) &handler)) {
php_output_handler_free(handler TSRMLS_CC);
zend_stack_del_top(&OG(handlers));
}
zend_stack_destroy(&OG(handlers));
}
}
}
销毁超全局变量
zend_try {
int i;
// 循环释放 PG(http_globals)
for (i=0; i<NUM_TRACK_VARS; i++) {
if (PG(http_globals)[i]) {
zval_ptr_dtor(&PG(http_globals)[i]);
}
}
} zend_end_try();
zend_deactivate, 关闭扫描器, 执行器, 编译器, 重置 ini_entries
void zend_deactivate(TSRMLS_D)
{
/* we're no longer executing anything */
EG(opline_ptr) = NULL;
EG(active_symbol_table) = NULL;
zend_try {
shutdown_scanner(TSRMLS_C);
} zend_end_try();
// 调用各扩展的 deactivate 方法
shutdown_executor(TSRMLS_C);
zend_try {
// 销毁编译器
shutdown_compiler(TSRMLS_C);
} zend_end_try();
zend_destroy_rsrc_list(&EG(regular_list) TSRMLS_CC);
#ifdef ZEND_DEBUG
if (GC_G(gc_enabled) && !CG(unclean_shutdown)) {
gc_collect_cycles(TSRMLS_C);
}
#endif
#if GC_BENCH
fprintf(stderr, "GC Statistics\n");
fprintf(stderr, "-------------\n");
fprintf(stderr, "Runs: %d\n", GC_G(gc_runs));
fprintf(stderr, "Collected: %d\n", GC_G(collected));
fprintf(stderr, "Root buffer length: %d\n", GC_G(root_buf_length));
fprintf(stderr, "Root buffer peak: %d\n\n", GC_G(root_buf_peak));
fprintf(stderr, " Possible Remove from Marked\n");
fprintf(stderr, " Root Buffered buffer grey\n");
fprintf(stderr, " -------- -------- ----------- ------\n");
fprintf(stderr, "ZVAL %8d %8d %9d %8d\n", GC_G(zval_possible_root), GC_G(zval_buffered), GC_G(zval_remove_from_buffer), GC_G(zval_marked_grey));
fprintf(stderr, "ZOBJ %8d %8d %9d %8d\n", GC_G(zobj_possible_root), GC_G(zobj_buffered), GC_G(zobj_remove_from_buffer), GC_G(zobj_marked_grey));
#endif
zend_try {
// 调用 zend_ini_entry 的 on_modify 函数
// 释放 ini_entry 的值
zend_ini_deactivate(TSRMLS_C);
} zend_end_try();
}
zend_post_deactivate_modules, 调用各子模块的 post_deactivate_func 函数
void zend_post_deactivate_modules(TSRMLS_D)
{
if (EG(full_tables_cleanup)) {
zend_hash_apply(&module_registry, (apply_func_t) exec_done_cb TSRMLS_CC);
zend_hash_reverse_apply(&module_registry, (apply_func_t) module_registry_unload_temp TSRMLS_CC);
} else {
zend_module_entry **p = module_post_deactivate_handlers;
// 循环调用各模块的 post_deactivate_func 函数
while (*p) {
zend_module_entry *module = *p;
module->post_deactivate_func();
p++;
}
}
}
sapi_deactivate, 关闭 sapi, 释放 sapi 相关的结构体
shutdown_memory_manager, 关闭内存管理器
总结
request RSHUTDOWN 过程在每次请求结束后均会调用
主要做的事情是销毁请求执行过程中的一些内存使用
- 调用 register_shutdown_function 注册的函数
- 调用 __destruction 函数
- flush 输出缓存
- 调用各扩展的 RSHUTDOWN 函数
- 关闭 php 输出
- 销毁超全局数组
- 释放扫描器, 执行器, 编译器, ini_entries
- 调用各扩展的 post_deactivate_func 函数
- 关闭 sapi
- 关闭内存管理器
php 启动过程 - reqeust RSHUTDOWN 过程的更多相关文章
- php 启动过程 - reqeust RINIT 过程
php 启动过程 - reqeust RINIT 过程 概述 apache 接收到请求之后, 交给 php 处理 php 模块在接收到请求后, 会对请求进行初始化, 及 RINIT 过程 调用触发 a ...
- php 启动过程 - sapi MINIT 过程
php 启动过程 - sapi MINIT 过程 sapi 概念 sapi 是 php 的应用编程接口, server 端接收请求通过 sapi 接口层交给 php 处理 不同的 server 端底层 ...
- php 启动过程 - sapi MSHUTDOWN 过程
php 启动过程 - sapi MSHUTDOWN 过程 概述 当服务器关闭时, 会走到 sapi MSHUTDOWN 过程 注册过程 本次内容是在 php 启动过程 - sapi MINIT 过程 ...
- SpringBoot IoC启动流程、初始化过程及Bean生命周期各个阶段的作用
目录 SpringBoot IoC启动流程.初始化过程及Bean生命周期各个阶段的作用 简述 首先明确IoC容器是啥 准备-SpringApplication的实例化 启动-SpringApplica ...
- 第三次作业(1) Visual Studio程序安装过程和练习过程
Visual Studio程序安装过程和练习过程 第一步 首先要在网上找一个VS2013的安装包,之后我安装在D盘上,C盘上也需要有5.2G空间,勾选相应的选项,才能继续安装. 安装的过程很漫长,接近 ...
- Atitit. 软件开发中的管理哲学--一个伟大的事业必然是过程导向为主 过程导向 vs 结果导向
Atitit. 软件开发中的管理哲学--一个伟大的事业必然是过程导向为主 过程导向 vs 结果导向 1. 一个伟大的事业必然是过程导向为主 1 1.1. 过程的执行情况(有明确的执行手册及标准) ...
- VScript 函数调用的两种分类:Sub过程和Function过程
来源:http://soft.zdnet.com.cn/software_zone/2007/0925/523318.shtml 在 VBScript 中,过程被分为两类:Sub 过程和 Functi ...
- Oracle数据库体系结构、启动过程、关闭过程
一.Oracle数据库体系结构体系结构由下面组件组成:1.Oracle服务器(Server):由数据库实例和数据库文件组成,另外在用户建立与服务器的连接时启动服务器进程并分配PGA(程序全局区) (1 ...
- iOS开发实践:一个类微博客户端从启动到与用户交互的过程
本文基于数据字典和数据流图两种工具讲述一个完整微博客户端的实现.数据字典和数据流图都可以用来表达线程的执行流程,同时定义了需要的类,是进一步设计类的基础. 数据字典实际上是一张表,表的第一个字段是程序 ...
随机推荐
- Vue 2.0初学后个人总结及分享
摘要:最近在上海找工作,发现Vue前景还不错,于是就打算先学习一下(之前了解过,但是一直没提到日程上)这篇随笔当是为了自己学习之后,做一个小的阶段性总结.希望本文的内容对于刚开始接触vue的朋友们有点 ...
- Select的逻辑处理顺序(Transact-SQL)
1. Select 语句基本写法 2. Select的逻辑处理顺序 SQL与其他编程语言不同的明显特征就是代码的处理顺序.在其他编程语言中,常见是按编写代码的先后顺序(从上之下)来处理.但在SQL中, ...
- redux-form的学习笔记二--实现表单的同步验证
(注:这篇博客参考自redux-form的官方英文文档)左转http://redux-form.com/6.5.0/examples/syncValidation/ 在这篇博客里,我将用redux-f ...
- 1789: [Ahoi2008]Necklace Y型项链
1789: [Ahoi2008]Necklace Y型项链 Time Limit: 1 Sec Memory Limit: 64 MBSubmit: 421 Solved: 258[Submit] ...
- omi-cli新版发布-升级webpack2和支持sass生成组件局部CSS
写在前面 omi-cli是Omi的命令行工具.在v0.1.X以及之前版本中,生成出来的项目脚手架 是基于webpack1的.由于: webpack1不支持tree-shaking,webpack2 支 ...
- Redis Sentinel中的机制与原理详解
序言 Redis-Sentinel是Redis官方推荐的高可用性(HA)解决方案.实际上这意味着你可以使用Sentinel模式创建一个可以不用人为干预而应对各种故障的Redis部署. 它的主要功能有以 ...
- Octave Tutorial(《Machine Learning》)之第二课《数据移动》
第二课 Moving Data 数据移动 常用内置函数 (1)加载文件 load 文件名.dat(或load('文件名.dat')) 接着输入文件名便可查看文件里的数据 (2)显示当前工作空间的所有变 ...
- mysql编程---函数
(存储)函数: 函数,也说成"存储函数",其实就是js或php中所说的函数! 唯一的区别: 这里的函数必须返回一个数据(值): 定义形式: 注意事项: 1, 在函数内容,可以有各种 ...
- SQL注入相关的知识【Mysql为例子】
以DVWA的sql注入初级为例,结合网上搜索的相关利用方式,总结成这一篇文章,内容会有点跳跃. 大纲: 1.初级手工注入 2.order by的使用 3.union查询注意点 4.Mysql相关的注释 ...
- python计算文件夹大小(linux du命令 简化版)
C盘又满了,怎么办?用了一些垃圾清理软件(或者bat脚本),但是还是不理想,那么具体哪些文件夹下面有巨大的文件呢?windows并不能通过详细信息看到每个文件夹的大小(PS:这里所谓的文件夹的大小是指 ...