php 啓動過程 - reqeust RINIT 過程

php 啓動過程 - reqeust RINIT 過程

概述

apache 接收到請求以後, 交給 php 處理php

php 模塊在接收到請求後, 會對請求進行初始化, 及 RINIT 過程apache

調用觸發

  • apache 啓動時註冊的鉤子函數 ap_hook_handler 在接收請求時觸發, 實際調用的是 php_handler 函數api

    • 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服務器

    • 加載配置app

    • 對請求進行檢查, 若出現問題, 返回對應錯誤less

    • 構造請求 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_startupcode

        • 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 請求的入口, 第一步orm

          // 啓動 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 引擎
  • 重置垃圾回收
  • 初始化編譯器
  • 初始化執行器
  • 初始化掃描器
相關文章
相關標籤/搜索