>  기사  >  백엔드 개발  >  PHP 커널 학습 - PHP 라이프사이클

PHP 커널 학습 - PHP 라이프사이클

WBOY
WBOY원래의
2016-08-08 09:23:501028검색

모든 것의 시작: SAPI 인터페이스

SAPI(Server Application Programing Interface)는 어떤 운영체제가 설치되어 있든 PC와 마찬가지로 특정 PHP 애플리케이션을 위한 프로그래밍 인터페이스를 말합니다. PC의 인터페이스 사양을 충족합니다. 모두 PC에서 정상적으로 실행될 수 있습니다. PHP 스크립트를 실행하는 방법에는 웹 서버를 통하거나 명령줄에서 직접 실행하거나 다른 프로그램에 내장되어 있습니다.

보통 우리는 Apache나 Nginx와 같은 웹 서버를 사용하여 PHP 스크립트를 테스트하거나 명령줄에서 PHP 인터프리터 프로그램을 통해 실행합니다. 스크립트가 실행된 후 웹 서버가 응답하고 브라우저는 응답 정보를 표시하거나 명령줄의 표준 출력에 내용을 표시합니다.

우리는 PHP 인터프리터가 어디에 있는지 거의 신경 쓰지 않습니다. 웹 서버와 명령줄 프로그램을 통해 스크립트를 실행하는 것은 매우 다르게 보이지만 작업 흐름은 실제로 동일합니다. 명령줄 매개변수는 PHP 인터프리터에 의해 실행될 스크립트로 전달됩니다. 이는 URL을 통해 PHP 페이지를 요청하는 것과 같습니다. 스크립트가 실행된 후 응답 결과가 반환되지만 터미널에는 명령줄의 응답 결과가 표시됩니다.

스크립트 실행은 SAPI 인터페이스 구현으로 시작됩니다. 단지 다른 SAPI 인터페이스 구현이 특정 작업을 완료한다는 것뿐입니다. 예를 들어 Apache의 mod_php SAPI 구현은 Apache에서 얻은 일부 정보를 초기화하고 콘텐츠를 출력할 때 해당 콘텐츠를 Apache에 반환해야 합니다.

다음 섹션에서는 몇 가지 일반적인 SAPI 구현을 더욱 심층적으로 소개합니다.

시작 및 종료

PHP가 실행을 시작한 후 두 가지 주요 단계, 즉 요청을 처리하기 전의 시작 단계와 요청 후의 종료 단계를 거칩니다. 시작 단계에는 두 가지 프로세스가 있습니다. 첫 번째 프로세스는 모듈 초기화 단계(MINIT)입니다. 이 프로세스는 전체 SAPI 수명 주기(예: Apache 시작 후 전체 수명 주기 또는 전체 실행 프로세스) 동안 한 번만 수행됩니다. 명령줄 프로그램). 두 번째 프로세스는 요청 단계에서 발생하는 모듈 활성화 단계(RINIT)입니다. 예를 들어, URL을 통해 페이지가 요청되면 각 요청 전에 모듈 활성화(RINIT 요청 시작)가 수행됩니다. 예를 들어, PHP가 일부 확장 모듈을 등록하면 MINIT 단계에서 모든 모듈의 MINIT 함수가 호출됩니다. 모듈은 이 단계에서 상수 등록, 모듈에서 사용하는 클래스 정의 등과 같은 일부 초기화 작업을 수행할 수 있습니다. 모듈이 구현되면 이러한 콜백 함수는 다음 매크로를 통해 구현될 수 있습니다.

<span>PHP_MINIT_FUNCTION(myphpextension)
{
    </span><span>//</span><span> 注册常量或者类等初始化操作</span><span>return</span><span> SUCCESS; 
}</span>

요청이 도착하면 PHP는 스크립트 생성 등 스크립트 실행을 위한 기본 환경을 초기화합니다. PHP 실행 저장을 포함한 실행 환경 프로세스의 변수 이름과 값 내용이 포함된 기호 테이블과 현재 모든 함수, 클래스 및 기타 정보가 포함된 기호 테이블입니다. 그런 다음 PHP는 모든 모듈의 RINIT 함수를 호출합니다. 이 단계에서 각 모듈은 일부 관련 작업을 수행할 수도 있습니다. 모듈의 RINIT 함수는 MINIT 콜백 함수와 유사합니다.

<span>PHP_RINIT_FUNCTION(myphpextension)
{
    </span><span>//</span><span> 例如记录请求开始时间
    </span><span>//</span><span> 随后在请求结束的时候记录结束时间。这样我们就能够记录下处理请求所花费的时间了</span><span>return</span><span> SUCCESS; 
}</span>

요청 처리 이후 종료 단계에 들어갑니다. 일반적으로 PHP는 스크립트가 끝까지 실행되거나 exit() 또는 die() 함수를 호출하면 종료 단계에 들어갑니다. 시작 단계에 해당하여 종료 단계도 두 단계로 나누어집니다. 하나는 요청이 완료된 후 모듈을 비활성화하는 단계(RSHUTDOWN, RINIT에 해당)이고, 다른 하나는 SAPI 수명 주기가 종료되면 모듈을 닫는 단계입니다( 웹 서버가 종료되거나 명령줄 스크립트가 실행되고 종료됩니다(MINIT에 해당하는 MSHUTDOWN).

<span>PHP_RSHUTDOWN_FUNCTION(myphpextension)
{
    </span><span>//</span><span> 例如记录请求结束时间,并把相应的信息写入到日至文件中。</span><span>return</span><span> SUCCESS; 
}</span>

단일 프로세스 SAPI 수명주기

CLI/CGI 모드 PHP는 단일 프로세스 SAPI 모드에 속합니다. 이 유형의 요청은 요청을 한 번 처리한 후 종료됩니다. 즉, 다음 링크만 통과하게 됩니다. 시작 - 요청 시작 - 요청 종료 - 종료 SAPI 인터페이스 구현은 수명 주기를 완료합니다. 아래 그림과 같습니다.

단일 프로세스 SAPI 라이프사이클

위 그림은 매우 간단하고 이해하기 쉽습니다. . 단지 PHP가 다양한 단계 사이에서 많은 작업을 수행한다는 것뿐입니다. 다음은 몇 가지 추가 사항입니다.

시작

각 모듈의 모듈 초기화를 호출하기 전에 다음을 포함하는 초기화 프로세스가 있습니다.

  • 여러 전역 변수 초기화

대부분의 경우 여기에서 초기화된 전역 변수는 NULL로 설정됩니다. 단, zuf(zend_utility_functions) 설정, zuf.printf_function = php_printf를 예로 사용하는 등 일부 예외는 있습니다. 여기서 php_printf는 zend_startup입니다. 함수는 전역 함수 포인터로 zend_printf에 할당되며 zend_printf 함수는 일반적으로 일반 문자열 출력으로 사용됩니다. 예를 들어 프로그램 호출 스택을 표시하는 debug_print_backtrace는 이를 사용하여 관련 정보를 인쇄합니다.

  • 여러 상수 초기화

여기에 있는 상수는 PHP 자체 상수 중 일부입니다. 이러한 상수는 PHP_VERSION과 같이 프로그램에 하드 코딩되어 있거나 PEAR_EXTENSION_DIR과 같은 구성 헤더 파일은 config.w32.h 파일에 기록됩니다.

  • 初始化Zend引擎和核心组件

前面提到的zend_startup函数的作用就是初始化Zend引擎,这里的初始化操作包括内存管理初始化、 全局使用的函数指针初始化(如前面所说的zend_printf等),对PHP源文件进行词法分析、语法分析、 中间代码执行的函数指针的赋值,初始化若干HashTable(比如函数表,常量表等等),为ini文件解析做准备, 为PHP源文件解析做准备,注册内置函数(如strlen、define等),注册标准常量(如E_ALL、TRUE、NULL等)、注册GLOBALS全局变量等。

  • 解析php.ini

php_init_config函数的作用是读取php.ini文件,设置配置参数,加载zend扩展并注册PHP扩展函数。此函数分为如下几步: 初始化参数配置表,调用当前模式下的ini初始化配置,比如CLI模式下,会做如下初始化:

INI_DEFAULT(<span>"</span><span>report_zend_debug</span><span>"</span>, <span>"</span><span>0</span><span>"</span><span>);
INI_DEFAULT(</span><span>"</span><span>display_errors</span><span>"</span>, <span>"</span><span>1</span><span>"</span>);

不过在其它模式下却没有这样的初始化操作。接下来会的各种操作都是查找ini文件:

  1. 判断是否有php_ini_path_override,在CLI模式下可以通过-c参数指定此路径(在php的命令参数中-c表示在指定的路径中查找ini文件)。
  2. 如果没有php_ini_path_override,判断php_ini_ignore是否为非空(忽略php.ini配置,这里也就CLI模式下有用,使用-n参数)。
  3. 如果不忽略ini配置,则开始处理php_ini_search_path(查找ini文件的路径),这些路径包括CWD(当前路径,不过这种不适用CLI模式)、 执行脚本所在目录、环境变量PATH和PHPRC和配置文件中的PHP_CONFIG_FILE_PATH的值。
  4. 在准备完查找路径后,PHP会判断现在的ini路径(php_ini_file_name)是否为文件和是否可打开。 如果这里ini路径是文件并且可打开,则会使用此文件, 也就是CLI模式下通过-c参数指定的ini文件的优先级是最高的, 其次是PHPRC指定的文件,第三是在搜索路径中查找php-%sapi-module-name%.ini文件(如CLI模式下应该是查找php-cli.ini文件), 最后才是搜索路径中查找php.ini文件。
  • 全局操作函数的初始化

php_startup_auto_globals函数会初始化在用户空间所使用频率很高的一些全局变量,如:$_GET、$_POST、$_FILES等。 这里只是初始化,所调用的zend_register_auto_global函数也只是将这些变量名添加到CG(auto_globals)这个变量表。

php_startup_sapi_content_types函数用来初始化SAPI对于不同类型内容的处理函数, 这里的处理函数包括POST数据默认处理函数、默认数据处理函数等。

  • 初始化静态构建的模块和共享模块(MINIT)

php_register_internal_extensions_func函数用来注册静态构建的模块,也就是默认加载的模块, 我们可以将其认为内置模块。在PHP5.3.0版本中内置的模块包括PHP标准扩展模块(/ext/standard/目录, 这里是我们用的最频繁的函数,比如字符串函数,数学函数,数组操作函数等等),日历扩展模块、FTP扩展模块、 session扩展模块等。这些内置模块并不是一成不变的,在不同的PHP模板中,由于不同时间的需求或其它影响因素会导致这些默认加载的模块会变化, 比如从代码中我们就可以看到mysql、xml等扩展模块曾经或将来会作为内置模块出现。

模块初始化会执行两个操作: 1. 将这些模块注册到已注册模块列表(module_registry),如果注册的模块已经注册过了,PHP会报Module XXX already loaded的错误。 1. 将每个模块中包含的函数注册到函数表( CG(function_table) ),如果函数无法添加,则会报 Unable to register functions, unable to load。

在注册了静态构建的模块后,PHP会注册附加的模块,不同的模式下可以加载不同的模块集,比如在CLI模式下是没有这些附加的模块的。

在内置模块和附加模块后,接下来是注册通过共享对象(比如DLL)和php.ini文件灵活配置的扩展。

在所有的模块都注册后,PHP会马上执行模块初始化操作(zend_startup_modules)。 它的整个过程就是依次遍历每个模块,调用每个模块的模块初始化函数, 也就是在本小节前面所说的用宏PHP_MINIT_FUNCTION包含的内容。

  • 禁用函数和类

php_disable_functions函数用来禁用PHP的一些函数。这些被禁用的函数来自PHP的配置文件的disable_functions变量。 其禁用的过程是调用zend_disable_function函数将指定的函数名从CG(function_table)函数表中删除。

php_disable_classes函数用来禁用PHP的一些类。这些被禁用的类来自PHP的配置文件的disable_classes变量。 其禁用的过程是调用zend_disable_class函数将指定的类名从CG(class_table)类表中删除。

ACTIVATION

파일 관련 내용을 처리한 후 PHP는 php_request_startup을 호출하여 요청 초기화 작업을 수행합니다. 요청 초기화 작업은 그림에 표시된 각 모듈의 요청 초기화 함수를 호출하는 것 외에도 많은 작업을 수행합니다.

  • Zend 엔진 활성화

gc_reset 함수는 가비지 수집 메커니즘을 재설정하는 데 사용됩니다. 물론 이는 PHP5.3 이후에만 사용할 수 있습니다.

init_compiler 함수는 컴파일 과정에서 opcode가 배치된 배열을 지우고, 컴파일에 사용되는 데이터 구조를 준비하는 등 컴파일러를 초기화하는 데 사용됩니다.

init_executor 함수는 중간 코드 실행 프로세스를 초기화하는 데 사용됩니다. 컴파일 프로세스 중에 함수 목록, 클래스 목록 등은 컴파일 타임에 전역 변수에 저장됩니다. 실행 프로세스를 준비할 때 이러한 목록은 다음과 같이 실행되는 전역 변수에 할당됩니다. function_table) ; 중간 코드는 PHP의 실행 가상 스택에서 실행되며, 이러한 스택은 초기화 중에 함께 초기화됩니다. 스택 외에도 변수를 저장하는 심볼 테이블(EG(symbol_table))은 50개 요소의 해시 테이블로 초기화되고, 객체를 저장하는 EG(objects_store)는 1024개 요소로 초기화됩니다. 위의 일부 변수 외에도 PHP의 실행 환경에는 오류 처리, 예외 처리 등이 있으며 모두 여기에서 초기화됩니다. php.ini를 통해 구성된 zend_extensions도 탐색되며 여기에서 활성화 함수가 호출됩니다.

  • SAPI 활성화

sapi_activate 함수는 SG(sapi_headers) 및 SG(request_info)를 초기화하고 HTTP 요청 방법에 대한 일부 내용을 설정하는 데 사용됩니다. 요청 방법은 HEAD인 경우 SG(request_info).headers_only=1로 설정합니다. 이 함수의 가장 중요한 작업은 요청된 데이터를 처리하는 것이며 결국 sapi_module.default_post_reader를 호출합니다. sapi_module.default_post_reader는 이전 모듈 초기화에서 php_startup_sapi_content_types 함수를 통해 등록되었습니다. 기본 처리 함수는 main/php_content_types.c 파일의 php_default_post_reader 함수입니다. 이 함수는 POST의 원시 데이터를 $HTTP_RAW_POST_DATA 변수에 기록합니다.

PHP는 게시 데이터를 처리한 후 sapi_module.read_cookies를 통해 쿠키 값을 읽습니다. CLI 모드에서 이 함수의 구현은 sapi_cli_read_cookies이지만 함수 본문에는 NULL 반환이 하나만 있습니다.

현재 모드에서 활성화 함수가 설정된 경우 이 함수를 실행하면 SAPI가 활성화됩니다. CLI 모드에서는 이 함수 포인터가 NULL로 설정됩니다.

  • 환경 초기화

여기서의 환경 초기화는 사용자 공간에서 사용해야 하는 일부 환경 변수의 초기화를 의미합니다. 여기서의 환경에는 서버 환경, 요청이 포함됩니다. 데이터 환경 등 우리가 사용하는 실제 변수는 $_POST, $_GET, $_COOKIE, $_SERVER, $_ENV 및 $_FILES입니다. sapi_module.default_post_reader와 마찬가지로 sapi_module.treat_data의 값도 모듈 초기화 중에 php_startup_sapi_content_types 함수를 통해 등록됩니다. 기본 데이터 처리 함수는 main/php_variables.c 파일의 php_default_treat_data 함수입니다.

$_COOKIE를 예로 들면 php_default_treat_data 함수는 구분 기호를 기준으로 모든 쿠키를 분할하여 해당 변수에 할당합니다.

  • 모듈 요청 초기화

PHP는 zend_activate_modules 함수를 통해 모듈 요청 초기화를 구현합니다. 즉, 그림에서 각 확장의 RINIT 호출을 볼 수 있습니다. 이 함수는 module_registry 변수에 등록된 모든 모듈을 순회하고 RINIT 메서드를 호출하여 모듈의 요청 초기화 작업을 구현합니다.

실행

php_execute_script 함수에는 PHP 스크립트를 실행하는 전체 프로세스가 포함됩니다.

PHP 파일을 구문 분석하고 실행해야 할 경우 실행 전 파일, 현재 실행해야 하는 메인 파일, 실행 후 파일 등 세 가지 파일을 실행해야 할 수 있습니다. 두 개의 현재가 아닌 파일은 auto_prepend_file 매개변수와 auto_append_file 매개변수를 통해 php.ini 파일에서 설정할 수 있습니다. 이 두 매개변수가 비어 있으면 해당 실행 파일이 비활성화됩니다.

파싱 및 실행이 필요한 파일에 대해서는 zend_compile_file(compile_file 함수)을 통해 어휘 분석, 구문 분석, 중간 코드 생성 작업을 수행하고 해당 파일의 중간 코드를 모두 반환합니다. 구문 분석된 파일이 유효한 중간 코드를 생성하는 경우 zend_execute(함수 실행)를 호출하여 중간 코드를 실행합니다. 실행 중에 예외가 발생하고 사용자가 이러한 예외 처리를 정의한 경우 이러한 예외 처리 함수가 호출됩니다. 모든 작업이 처리된 후 PHP는 EG(return_value_ptr_ptr)를 통해 결과를 반환합니다.

비활성화

PHP가 요청을 닫는 프로세스는 여러 가지 닫는 작업의 집합이며 이 집합은 php_request_shutdown 함수에 존재합니다. 이 컬렉션에는 다음이 포함됩니다:

  1. register_shutdown_function()을 통해 등록된 모든 함수를 호출합니다. 종료 시 호출되는 이러한 기능은 사용자 공간에 추가되었습니다. 간단한 예로, 스크립트 오류가 발생할 때 통합 함수를 호출하여 사용자에게 보다 친숙한 페이지를 제공할 수 있습니다. 이는 웹의 404 페이지와 다소 유사합니다.
  2. 사용 가능한 모든 __destruct 함수를 실행합니다. 여기서 소멸자에는 객체 풀(EG(objects_store))에 있는 모든 객체의 소멸자와 EG(symbol_table)에 있는 각 요소의 소멸자가 포함됩니다.
  3. 모든 출력을 플러시합니다.
  4. HTTP 응답을 보냅니다. 헤더를 출력하는 과정이기도 하지만, 이 문자열은 특정 사양을 따를 수도 있습니다.
  5. 각 모듈의 종료 요청 메서드를 순회하여 모듈의 요청 종료 작업을 수행합니다. picture.executor 및 중간 코드 executor
  6. 각 확장의 post-RSHUTDOWN 함수를 호출합니다. 기본적으로 각 확장의 post_deactivate_func 함수 포인터는 NULL이고 sapi_deactivate(sapi_headers), SG(request_info)를 통해 SAPI를 닫습니다.
  7. 스트림 래퍼를 닫고 스트림 필터를 닫습니다.
  8. 최대 실행 시간을 재설정합니다.
  9. 종료
  10. 마침내 시간입니다.
  11. flush

sapi_flush는 CLI 모드의 fflush 기능과 동일한 sapi_module.flush를 호출합니다. >

Zend 엔진 닫기

    zend_shutdown은 Zend 엔진을 종료합니다. 🎜>그림의 프로세스에 따라 각 모듈의 모듈 닫기 작업을 수행해야 합니다. module_registry를 파괴하는 zend_hash_graceful_reverse_destroy 함수는 단 하나뿐입니다. 근본 원인은 module_registry를 초기화할 때 해시 테이블을 파괴할 때 ZEND_MODULE_DTOR 매크로가 호출되도록 설정되어 있다는 것입니다. 이 함수에서는 PHP_RSHUTDOWN_FUNCTION 매크로에 의해 생성된 함수인 module_shutdown_func 메서드가 호출됩니다. 모든 모듈을 닫은 후 PHP는 계속해서 전역 함수 테이블을 삭제하고, 전역 클래스 테이블을 삭제하고, 전역 변수 테이블을 판매합니다. zend_shutdown_extensions를 통해 zend_extensions의 모든 요소를 ​​순회하고 각 확장의 종료 기능을 호출합니다.
  • 다중 프로세스 SAPI 수명 주기

일반적으로 PHP는 PHP 요청을 처리하기 위해 Apache 모듈로 컴파일됩니다. Apache는 일반적으로 다중 프로세스 모드를 채택하며 Apache가 시작된 후 여러 하위 프로세스를 분기합니다. 각 프로세스는 독립적인 메모리 공간을 갖습니다. 그러나 각 프로세스의 시작 단계만 발생합니다. 프로세스가 포크된 후에는 프로세스 수명 동안 여러 요청이 처리될 수 있습니다. 종료 단계는 Apache가 종료되거나 프로세스가 종료된 후에만 발생합니다. 이 두 단계 사이에서 요청 시작-요청 종료 단계가 각 요청마다 반복됩니다. 아래 그림과 같습니다.

멀티 프로세스 SAPI 라이프사이클

멀티 스레드 SAPI 라이프사이클

Multiple 스레드 모드는 다중 프로세스의 프로세스와 유사하지만, 프로세스의 수명 주기 동안 요청 시작-요청 닫기 프로세스가 병렬로 반복된다는 점이 다릅니다.

멀티 스레드 SAPI 수명 주기

출처: http://www.php-internals.com/book/?p=chapt02/02-01-php-life-cycle -and-zend-엔진

위 내용은 내용의 측면을 포함하여 PHP 커널-PHP 라이프사이클 학습을 소개합니다. PHP 튜토리얼에 관심이 있는 친구들에게 도움이 되기를 바랍니다.

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
이전 기사:ajax3—php(29)다음 기사:ajax3—php(29)