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 过程的更多相关文章

  1. php 启动过程 - reqeust RINIT 过程

    php 启动过程 - reqeust RINIT 过程 概述 apache 接收到请求之后, 交给 php 处理 php 模块在接收到请求后, 会对请求进行初始化, 及 RINIT 过程 调用触发 a ...

  2. php 启动过程 - sapi MINIT 过程

    php 启动过程 - sapi MINIT 过程 sapi 概念 sapi 是 php 的应用编程接口, server 端接收请求通过 sapi 接口层交给 php 处理 不同的 server 端底层 ...

  3. php 启动过程 - sapi MSHUTDOWN 过程

    php 启动过程 - sapi MSHUTDOWN 过程 概述 当服务器关闭时, 会走到 sapi MSHUTDOWN 过程 注册过程 本次内容是在 php 启动过程 - sapi MINIT 过程 ...

  4. SpringBoot IoC启动流程、初始化过程及Bean生命周期各个阶段的作用

    目录 SpringBoot IoC启动流程.初始化过程及Bean生命周期各个阶段的作用 简述 首先明确IoC容器是啥 准备-SpringApplication的实例化 启动-SpringApplica ...

  5. 第三次作业(1) Visual Studio程序安装过程和练习过程

    Visual Studio程序安装过程和练习过程 第一步 首先要在网上找一个VS2013的安装包,之后我安装在D盘上,C盘上也需要有5.2G空间,勾选相应的选项,才能继续安装. 安装的过程很漫长,接近 ...

  6. Atitit. 软件开发中的管理哲学--一个伟大的事业必然是过程导向为主 过程导向 vs 结果导向

    Atitit. 软件开发中的管理哲学--一个伟大的事业必然是过程导向为主    过程导向 vs 结果导向 1. 一个伟大的事业必然是过程导向为主 1 1.1. 过程的执行情况(有明确的执行手册及标准) ...

  7. VScript 函数调用的两种分类:Sub过程和Function过程

    来源:http://soft.zdnet.com.cn/software_zone/2007/0925/523318.shtml 在 VBScript 中,过程被分为两类:Sub 过程和 Functi ...

  8. Oracle数据库体系结构、启动过程、关闭过程

    一.Oracle数据库体系结构体系结构由下面组件组成:1.Oracle服务器(Server):由数据库实例和数据库文件组成,另外在用户建立与服务器的连接时启动服务器进程并分配PGA(程序全局区) (1 ...

  9. iOS开发实践:一个类微博客户端从启动到与用户交互的过程

    本文基于数据字典和数据流图两种工具讲述一个完整微博客户端的实现.数据字典和数据流图都可以用来表达线程的执行流程,同时定义了需要的类,是进一步设计类的基础. 数据字典实际上是一张表,表的第一个字段是程序 ...

随机推荐

  1. 【JS】JavaScript中的参数传递

    ECMAScript中所有函数的参数都是按值传递的,简单讲就是函数外部的值 复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样.切记访问变量有按值访问和按引用访问,而参数只能按值传递. 在向 ...

  2. Java面试04|Spring框架

    1.动态代理的几种方式 Java主要有两种代理,JDK和Cglib动态代理.先看JDK代理实例如下: JDK创建代理有一个限制,即它只能为接口创建代理实例.举个例子如下: public interfa ...

  3. WeMall微商城源码报名插件Apply的主要源码

    WeMall微信商城源码报名插件Apply,用于商城的签到系统,分享了部分比较重要的代码,供技术员学习参考 AdminController.class.php <?php namespace A ...

  4. 2016: [Usaco2010]Chocolate Eating

    2016: [Usaco2010]Chocolate Eating Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 224  Solved: 87[Su ...

  5. 2020: [Usaco2010 Jan]Buying Feed, II

    2020: [Usaco2010 Jan]Buying Feed, II Time Limit: 3 Sec  Memory Limit: 64 MBSubmit: 220  Solved: 162[ ...

  6. JAVA面试题和答案(二)

    本文我们将要讨论Java面试中的各种不同类型的面试题,它们可以让雇主测试应聘者的Java和通用的面向对象编程的能力.下面的章节分为上下两篇,第一篇将要讨论面向对象编程和它的特点,关于Java和它的功能 ...

  7. php函数的使用

    <?php header("Content-type:text/html; charset=utf-8"); //普通函数 echo "<br/>--- ...

  8. Java基础--定时任务Timer(转载)

    Java基础--定时任务Timer 一.Timer介绍 java.util.Timer java.util.TimerTask Timer是一个定时器类,通过该类可以为指定的定时任务进行配置.Time ...

  9. PHP生成随机水印图片

    基于PHP的GD图形库,自己生成一张图片.仅限初识GD库,实例学习. 一.需求 网站的布局用到了类似慕课网课程列表的风格,每一个课程是一个banner图,图下面是标题加简介.因为课程的数量较大没有为所 ...

  10. 给 Virtualbox 中 Ubuntu 系统设置静态 IP ,让 DNS 配置信息不会在重启后被清除

    虚拟机网络选择 桥接网卡 模式. 主要涉及两个步骤: 1. 修改 /etc/network/interfaces 文件: 2. 修改 dns : 第一步,修改 interfaces 文件: sudo ...