php 启动过程 - reqeust RINIT 过程

概述

apache 接收到请求之后, 交给 php 处理

php 模块在接收到请求后, 会对请求进行初始化, 及 RINIT 过程

调用触发

  • apache 启动时注册的钩子函数 ap_hook_handler 在接收请求时触发, 实际调用的是 php_handler 函数

    • ap_hook_handler(php_handler, NULL, NULL, APR_HOOK_MIDDLE);

调用过程

  • 调用 php_handler

    • static int php_handler(request_rec *r)
      {
      // 省略 ... #define PHPAP_INI_OFF php_apache_ini_dtor(r, parent_req TSRMLS_CC);
      // 获取 php 模块配置
      conf = ap_get_module_config(r->per_dir_config, &php5_module);
      // 设置 server_context
      ctx = SG(server_context);
      if (ctx == NULL || (ctx && ctx->request_processed && !strcmp(r->protocol, "INCLUDED"))) {
      normal:
      ctx = SG(server_context) = apr_pcalloc(r->pool, sizeof(*ctx));
      apr_pool_cleanup_register(r->pool, (void *)&SG(server_context), php_server_context_cleanup, apr_pool_cleanup_null);
      ctx->r = r;
      ctx = NULL; /* May look weird to null it here, but it is to catch the right case in the first_try later on */
      } else {
      parent_req = ctx->r;
      ctx->r = r;
      }
      // 应用配置
      apply_config(conf); // 省略 ... // php 层面对请求进行检查, 若发现问题, 返回相应错误
      if (r->used_path_info == AP_REQ_REJECT_PATH_INFO
      && r->path_info && r->path_info[0]) {
      PHPAP_INI_OFF;
      return HTTP_NOT_FOUND;
      } // 省略 ... // 构造请求
      if (php_apache_request_ctor(r, ctx TSRMLS_CC)!=SUCCESS) {
      zend_bailout();
      } // 省略 ... if (AP2(last_modified)) {
      ap_update_mtime(r, r->finfo.mtime);
      ap_set_last_modified(r);
      } // 判断是解析文件, 还是不经解析展示源码
      if (strncmp(r->handler, PHP_SOURCE_MAGIC_TYPE, sizeof(PHP_SOURCE_MAGIC_TYPE) - 1) == 0) {
      zend_syntax_highlighter_ini syntax_highlighter_ini;
      php_get_highlight_struct(&syntax_highlighter_ini);
      highlight_file((char *)r->filename, &syntax_highlighter_ini TSRMLS_CC);
      } else {
      zend_file_handle zfd;
      zfd.type = ZEND_HANDLE_FILENAME;
      zfd.filename = (char *) r->filename;
      zfd.free_filename = 0;
      zfd.opened_path = NULL; if (!parent_req) {
      // 执行 php 脚本
      php_execute_script(&zfd TSRMLS_CC);
      } else {
      zend_execute_scripts(ZEND_INCLUDE TSRMLS_CC, NULL, 1, &zfd);
      } apr_table_set(r->notes, "mod_php_memory_usage",
      apr_psprintf(ctx->r->pool, "%" APR_SIZE_T_FMT, zend_memory_peak_usage(1 TSRMLS_CC)));
      }
      } zend_end_try(); if (!parent_req) {
      // 清除请求结构
      php_apache_request_dtor(r TSRMLS_CC);
      ctx->request_processed = 1; // 省略 ... apr_pool_cleanup_run(r->pool, (void *)&SG(server_context), php_server_context_cleanup);
      } else {
      ctx->r = parent_req;
      } return OK;
      }
    • 设置 server 上下文 server_context

    • 加载配置

    • 对请求进行检查, 若出现问题, 返回对应错误

    • 构造请求 php_apache_request_ctor

      // 构造请求
      static int php_apache_request_ctor(request_rec *r, php_struct *ctx TSRMLS_DC)
      {
      char *content_length;
      const char *auth;
      // 设置全局变量的值
      SG(sapi_headers).http_response_code = !r->status ? HTTP_OK : r->status;
      SG(request_info).content_type = apr_table_get(r->headers_in, "Content-Type");
      SG(request_info).query_string = apr_pstrdup(r->pool, r->args);
      SG(request_info).request_method = r->method;
      SG(request_info).proto_num = r->proto_num;
      SG(request_info).request_uri = apr_pstrdup(r->pool, r->uri);
      SG(request_info).path_translated = apr_pstrdup(r->pool, r->filename);
      r->no_local_copy = 1; content_length = (char *) apr_table_get(r->headers_in, "Content-Length");
      SG(request_info).content_length = (content_length ? atol(content_length) : 0); apr_table_unset(r->headers_out, "Content-Length");
      apr_table_unset(r->headers_out, "Last-Modified");
      apr_table_unset(r->headers_out, "Expires");
      apr_table_unset(r->headers_out, "ETag"); auth = apr_table_get(r->headers_in, "Authorization");
      // 验证 auth
      php_handle_auth_data(auth TSRMLS_CC); if (SG(request_info).auth_user == NULL && r->user) {
      SG(request_info).auth_user = estrdup(r->user);
      } ctx->r->user = apr_pstrdup(ctx->r->pool, SG(request_info).auth_user);
      // 调用 php_request_startup 构造请求
      return php_request_startup(TSRMLS_C);
      }
      • 调用 php_request_startup, 初始化请求, 在这里, 程序对于是否定义了 APACHE_HOOKS 定义不同的 php_request_startup 函数, 但是主体内容基本是一致, 以下仅针对定义过 APACHE_HOOKS 常量的 php_request_startup

        • int php_request_startup(TSRMLS_D)
          {
          int retval = SUCCESS; #if PHP_SIGCHILD
          signal(SIGCHLD, sigchld_handler);
          #endif
          // 启动 sapi, php 处理请求 sapi 是第一步
          if (php_start_sapi() == FAILURE) {
          return FAILURE;
          } php_output_activate(TSRMLS_C);
          sapi_activate(TSRMLS_C);
          php_hash_environment(TSRMLS_C); zend_try {
          PG(during_request_startup) = 1;
          if (PG(expose_php)) {
          sapi_add_header(SAPI_PHP_VERSION_HEADER, sizeof(SAPI_PHP_VERSION_HEADER)-1, 1);
          }
          } zend_catch {
          retval = FAILURE;
          } zend_end_try(); return retval;
          }
        • php_start_sapi 启动 sapi, sapi 是处理 php 请求的入口, 第一步

          // 启动 sapi
          static int php_start_sapi(TSRMLS_D)
          {
          int retval = SUCCESS; if(!SG(sapi_started)) {
          zend_try {
          PG(during_request_startup) = 1;
          // 初始化变量
          PG(modules_activated) = 0;
          PG(header_is_being_sent) = 0;
          PG(connection_status) = PHP_CONNECTION_NORMAL;
          // zend 引擎激活
          zend_activate(TSRMLS_C);
          // 设置超时时间
          zend_set_timeout(EG(timeout_seconds), 1);
          // 激活各子模块
          zend_activate_modules(TSRMLS_C);
          // 设置模块激活标识
          PG(modules_activated)=1;
          } zend_catch {
          retval = FAILURE;
          } zend_end_try(); SG(sapi_started) = 1;
          }
          return retval;
          }
          • 调用 zend_activate, 激活 zend 引擎

            • // 激活 zend 引擎
              void zend_activate(TSRMLS_D) /* {{{ */
              {
              // 重置垃圾回收
              gc_reset(TSRMLS_C);
              // 初始化编译器
              init_compiler(TSRMLS_C);
              // 初始化执行器
              init_executor(TSRMLS_C);
              startup_scanner(TSRMLS_C);
              }
            • gc_reset, 重置 gc 垃圾回收全局结构

              ZEND_API void gc_reset(TSRMLS_D)
              {
              GC_G(gc_runs) = 0;
              GC_G(collected) = 0; #if GC_BENCH
              GC_G(root_buf_length) = 0;
              GC_G(root_buf_peak) = 0;
              GC_G(zval_possible_root) = 0;
              GC_G(zobj_possible_root) = 0;
              GC_G(zval_buffered) = 0;
              GC_G(zobj_buffered) = 0;
              GC_G(zval_remove_from_buffer) = 0;
              GC_G(zobj_remove_from_buffer) = 0;
              GC_G(zval_marked_grey) = 0;
              GC_G(zobj_marked_grey) = 0;
              #endif GC_G(roots).next = &GC_G(roots);
              GC_G(roots).prev = &GC_G(roots); if (GC_G(buf)) {
              GC_G(unused) = NULL;
              GC_G(first_unused) = GC_G(buf); GC_G(zval_to_free) = NULL;
              } else {
              GC_G(unused) = NULL;
              GC_G(first_unused) = NULL;
              GC_G(last_unused) = NULL;
              }
              }
            • init_compiler, 初始化编译器, 主要指的是初始化 compiler_globals 全局结构体的值以及初始化资源列表

              void init_compiler(TSRMLS_D)
              {
              // 设置 opcode 命令数组为空
              CG(active_op_array) = NULL;
              memset(&CG(context), 0, sizeof(CG(context)));
              // 初始化全局编译结构
              zend_init_compiler_data_structures(TSRMLS_C);
              // 初始化资源列表
              zend_init_rsrc_list(TSRMLS_C);
              zend_hash_init(&CG(filenames_table), 5, NULL, (dtor_func_t) free_estring, 0);
              zend_llist_init(&CG(open_files), sizeof(zend_file_handle), (void (*)(void *)) file_handle_dtor, 0);
              CG(unclean_shutdown) = 0;
              }
            • init_executor, 初始化执行器, 主要是设置 executor_globals 全局执行器结构的值

              // 初始化全局执行器
              void init_executor(TSRMLS_D) /* {{{ */
              {
              // 初始化浮点运算单元
              zend_init_fpu(TSRMLS_C);
              // 初始化 zval 结构
              INIT_ZVAL(EG(uninitialized_zval));
              // 将 zval 结构的 refcount__gc 自增 1
              Z_ADDREF(EG(uninitialized_zval));
              INIT_ZVAL(EG(error_zval));
              EG(uninitialized_zval_ptr)=&EG(uninitialized_zval);
              EG(error_zval_ptr)=&EG(error_zval);
              /* destroys stack frame, therefore makes core dumps worthless */
              #if 0&&ZEND_DEBUG
              original_sigsegv_handler = signal(SIGSEGV, zend_handle_sigsegv);
              #endif
              EG(return_value_ptr_ptr) = NULL; EG(symtable_cache_ptr) = EG(symtable_cache) - 1;
              EG(symtable_cache_limit) = EG(symtable_cache) + SYMTABLE_CACHE_SIZE - 1;
              EG(no_extensions) = 0; EG(function_table) = CG(function_table);
              EG(class_table) = CG(class_table); EG(in_execution) = 0;
              EG(in_autoload) = NULL;
              EG(autoload_func) = NULL;
              EG(error_handling) = EH_NORMAL; zend_vm_stack_init(TSRMLS_C);
              zend_vm_stack_push((void *) NULL TSRMLS_CC); zend_hash_init(&EG(symbol_table), 50, NULL, ZVAL_PTR_DTOR, 0);
              EG(active_symbol_table) = &EG(symbol_table); zend_llist_apply(&zend_extensions, (llist_apply_func_t) zend_extension_activator TSRMLS_CC);
              EG(opline_ptr) = NULL; zend_hash_init(&EG(included_files), 5, NULL, NULL, 0); EG(ticks_count) = 0; EG(user_error_handler) = NULL; EG(current_execute_data) = NULL; zend_stack_init(&EG(user_error_handlers_error_reporting));
              zend_ptr_stack_init(&EG(user_error_handlers));
              zend_ptr_stack_init(&EG(user_exception_handlers));
              // 初始化对象池
              zend_objects_store_init(&EG(objects_store), 1024); EG(full_tables_cleanup) = 0;
              #ifdef ZEND_WIN32
              EG(timed_out) = 0;
              #endif EG(exception) = NULL;
              EG(prev_exception) = NULL; EG(scope) = NULL;
              EG(called_scope) = NULL; EG(This) = NULL; EG(active_op_array) = NULL; EG(active) = 1;
              EG(start_op) = NULL;
              }
            • startup_scanner, 启动扫描器, 初始化扫描器

              void startup_scanner(TSRMLS_D)
              {
              CG(parse_error) = 0;
              CG(doc_comment) = NULL;
              CG(doc_comment_len) = 0;
              zend_stack_init(&SCNG(state_stack));
              zend_ptr_stack_init(&SCNG(heredoc_label_stack));
              }
              • 扫描器结构

                • // 扫描器
                  struct _zend_php_scanner_globals {
                  zend_file_handle *yy_in;
                  zend_file_handle *yy_out; unsigned int yy_leng;
                  unsigned char *yy_start;
                  unsigned char *yy_text;
                  unsigned char *yy_cursor;
                  unsigned char *yy_marker;
                  unsigned char *yy_limit;
                  int yy_state;
                  zend_stack state_stack;
                  zend_ptr_stack heredoc_label_stack; /* original (unfiltered) script */
                  unsigned char *script_org;
                  size_t script_org_size; /* filtered script */
                  unsigned char *script_filtered;
                  size_t script_filtered_size; /* input/output filters */
                  zend_encoding_filter input_filter;
                  zend_encoding_filter output_filter;
                  const zend_encoding *script_encoding;
                  };
          • 调用 zend_activate_modules, 调用各子模块的 request_startup_func 函数, 各子模块结构在 sapi MINIT 时被记录下来

            • void zend_activate_modules(TSRMLS_D)
              {
              // module_request_startup_handlers 在 sapi MINIT 的 zend_collect_module_handlers 时被记录下来
              zend_module_entry **p = module_request_startup_handlers;
              // 循环遍历, 调用各模块的 request_startup_func 函数
              while (*p) {
              zend_module_entry *module = *p; if (module->request_startup_func(module->type, module->module_number TSRMLS_CC)==FAILURE) {
              zend_error(E_WARNING, "request_startup() for %s module failed", module->name);
              exit(1);
              }
              p++;
              }
              }
    • 判断是否解析文件

    • 执行 php 脚本

    • 清除请求结构

总结

request RINIT 是每个请求传给 php 时均需要执行的

RINIT 过程主要是为每个请求设置服务器环境以及相关的各种配置, 以便后续执行

RINIT 过程的主要部分在于 php_request_startup 函数, 该函数的主要作用如下:

  • 启动 sapi
  • 激活 zend 引擎
  • 重置垃圾回收
  • 初始化编译器
  • 初始化执行器
  • 初始化扫描器

php 启动过程 - reqeust RINIT 过程的更多相关文章

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

    php 启动过程 - reqeust RSHUTDOWN 过程 概述 request RSHUTDOWN 过程在请求结束后调用 调用触发 同 request RINIT 过程一样, 先是用 apach ...

  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. category类别中添加属性

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Menlo; color: #c91b13 } p.p2 { margin: 0.0px 0. ...

  2. sys模块和Python常用的内建函数

    1.sys模块 当Python执行import sys语句的时候,它在sys.path变量中所列目录中寻找sys.py模块.如果找到了这个文件,这个模块的主块中的语句将被运行,然后这个模块将能够被使用 ...

  3. mvp架构解析

    MVP现在已经是目前最火的架构,很多的框架都是以MVP为基础,甚至于Google自己都出一个MVP的开源架构.https://github.com/googlesamples/android-arch ...

  4. 字符集&各种编码&编码解码

    要理解乱码问题,首先需要理解几个概念:字符集.编码.编码规则.乱码 1. 字符集: 字符(Character)是各种文字和符号的总称,包括各国家文字.标点符号.图形符号.数字等.字符集(Charact ...

  5. Docker 使用指南 (四)—— 数据卷的使用

    一.数据卷的使用 有时候需要使用数据库,但是又希望它的数据能保存在本地,Docker中提供了数据卷可以供你方便的操作数据.数据卷是一个可供一个或多个容器使用的特殊目录,它绕过 UFS,可以提供很多有用 ...

  6. 带金属光泽的模型shader的实现

    最近捣鼓了一下金属光泽的shader的实现,在一些高模展示的时候或者模型的金属部分的表现的时候,我们需要给模型添加一些金属光泽,表现出一个模型某些金属装备上有一定的反光.今天我主要写一种基于贴图实现的 ...

  7. 树链剖分-SPOJ375(QTREE)

    QTREE - Query on a tree You are given a tree (an acyclic undirected connected graph) with N nodes, a ...

  8. 纪念一下java学习之第一个get请求。

    环境,变量及工具: 1.java jdk1.8.X 2.tomcat 8.5.11 3. 环境变量: CATALINA_HOME: tomcat安装地址   JAVA_HOME:  JDK安装地址   ...

  9. quartz笔记

    首先网络上的很多教程经常有错(信息过载了),其主要原因是版本发生了变化,例如quartz1和2之间还是有不少差别的,导致查找资料的人浪费了不少时间.所以无论教程如何写,都建议读者首先学习官网的教程,如 ...

  10. iOS开发之UITabBarController

    1.概述 跟UINavigationController类似,UITabBarController也可以轻松地管理多个控制器,轻松完成控制器之间的切换,典型例子就是QQ.微信等应用. 2.UITabB ...