目次
PHPカーネルの徹底理解(3)概要-SAPIの概要、徹底理解-sapi
ホームページ バックエンド開発 PHPチュートリアル PHPカーネルを深く理解する(3) 概要~SAPIの概要、徹底理解~sapi_PHPチュートリアル

PHPカーネルを深く理解する(3) 概要~SAPIの概要、徹底理解~sapi_PHPチュートリアル

Jul 12, 2016 am 09:00 AM
Linuxカーネル

PHPカーネルの徹底理解(3)概要-SAPIの概要、徹底理解-sapi

この記事へのリンク: http://www.orlion.ml/234/

1. PHP ライフサイクルの各段階で、いくつかのサービス関連の操作が SAPI インターフェイスを通じて実装されます。これらの組み込み実装の物理的な場所は、PHP ソース コードの SAPI ディレクトリ内にあります。このディレクトリには、コマンド ライン プログラムの実装、Apache の mod_php モジュールの実装、fastcgi の実装など、各サーバー抽象化レイヤーの PHP コードが保存されます。

各サーバー抽象化レイヤー間では同じ規則に従います。ここではこれを SAPI インターフェイスと呼びます。各 SAPI 実装は、_sapi_module_struct 構造体変数です。 (SAPI インターフェース)。 PHP ソース コードでは、サーバー関連の情報を呼び出す必要がある場合、その情報はすべて SAPI インターフェイスの対応するメソッド呼び出しを通じて実装されます。これらのメソッドは、各サーバー抽象化レイヤーが実装されるときに独自の実装を持ちます。多くの操作には多用途性があるため、インターフェイス メソッドの大部分ではデフォルトのメソッドが使用されます。下の写真はSPAIの簡単な模式図です

CGI モードと Apache2 サーバーを例に挙げると、それらの起動方法は次のとおりです:

リーリー

ここでの cgi_sapi_module は、sapi_module_struct 構造体の静的変数です。その起動メソッドは php_cgi_startup 関数ポインタを指します。起動関数ポインターに加えて、この構造体には他の多くのメソッドやフィールドがあります。これらの構造体はサーバーのインターフェース実装で定義されます

SAPI 全体は、オブジェクト指向のテンプレート メソッド パターンの適用に似ています。 SAPI.c および SAPI.h ファイルに含まれる一部の関数は、テンプレート メソッド パターンの抽象テンプレートであり、各サーバーの sapi_module の定義および関連する実装は特定のテンプレートです

2.Apacheモジュール

(1) PHP を Apache サーバー上で実行する必要がある場合、一般的には mod_php5 モジュールの形式で統合できます。このとき、mod_php5 モジュールの機能は、Aapche によって渡された PHP ファイルのリクエストを受信して​​処理することです。これらのリクエストを処理し、最終結果が Apache に返されます。 Apache が起動する前に構成ファイルで PHP モジュールを構成すると、PHP モジュールは apache2 の ap_hook_post_config フックを登録し、Apache が PHP ファイルに対するリクエストの受信を開始したときにこのモジュールを起動します。

この起動時のロード方法に加えて、Apache モジュールは実行時に動的にロードできます。つまり、ソース コードを再コンパイルしたり、サーバーを再起動したりすることなく、サーバーを関数で拡張できます。必要なのは、信号 HUP または AP_SIG_GEACEFUL をサーバーに送信して、モジュールをリロードするようにサーバーに通知することだけです。ただし、動的にロードする前に、モジュールをダイナミック リンク ライブラリにコンパイルする必要があります。このときのダイナミックロードとは、ダイナミックリンクライブラリをロードすることです。 Apache のダイナミック リンク ライブラリの処理は、mod_so モジュールを通じて完了します。そのため、mod_so モジュールは Apache のコアに静的にコンパイルすることしかできません。これは、Apache と一緒に起動されることを意味します。

Apache はどのようにモジュールをロードしますか? mod_php5 を例として、まず httpd.conf に次の行を追加します:

リーリー

設定ファイルに示されている命令を追加すると、Apache はモジュールのロード時にモジュール名に基づいてモジュールを見つけてロードします。 Apache の各モジュールはモジュール構造の形で存在し、モジュール構造の name 属性は末尾のマクロ STANDARD20_MODULE_STUFF を通じて __FILE__ に反映されます。前の命令で指定されたパスを通じて関連するダイナミック リンク ライブラリ ファイルを見つけた後、Apache は内部関数を通じてダイナミック リンク ライブラリのコンテンツを取得し、モジュールのコンテンツをメモリ内の指定された変数にロードします。

実際にモジュールをアクティブ化する前に、Apache はロードされたすべてのモジュールが本物の Apache モジュールであるかどうかを確認します。最後に、Apache は関連する関数 (ap_add_loaded_module) を呼び出してモジュールをアクティブ化します。ここでのアクティブ化は、モジュールを対応するリンク リスト (ap_top_modules リンク リスト) に入れることです。

Apache は PHP モジュールをロードしますが、このモジュールはどのように実装されているのでしょうか? Apache2 の mod_php5 モジュールには、sapi/apache2handler と sapi/apache2filter の 2 つのディレクトリが含まれています。apache2_handle/mod_php5.c ファイル内のモジュール定義に関連するコードは次のとおりです。 リーリー

Apacheのモジュール構造に対応します。 モジュール構造は以下のように定義されます。 リーリー

上記のモジュール構造は、mod_php5.c で見た構造とは少し異なります。これは、最初の 8 フィールドの定義が含まれる STANDARD20_MODULE_STUFF によるものです。 STANDARD20_MODULE_STUFF マクロは次のように定義されています:

リーリー

php5_module で定義された構造体において、php_dir_cmds はモジュールで定義されたすべての命令のセットです。 定義の内容は次のとおりです。 リーリー

これはmod_php5モジュールによって定義されたコマンドリストです。これは実際には commond_rec 構造体の配列です。 Apache は命令を検出すると、各モジュールの命令テーブルを 1 つずつ調べて、その命令を処理できるモジュールがあるかどうかを確認します。見つかった場合、命令テーブル内のすべてのモジュールが処理できない場合は、応答処理関数が呼び出されます。上記のように、mod_php5 モジュールは php_value と他の 5 つの命令のみを提供します。

php_ap2_register_hook関数は次のように定義されています:

void php_ap2_register_hook(apr_pool_t *<span>p)
{
    ap_hook_pre_config(php_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
    ap_hook_post_config(php_apache_server_startup, NULL, NULL, 
APR_HOOK_MIDDLE);
    ap_hook_handler(php_handler, NULL, NULL, APR_HOOK_MIDDLE);
    ap_hook_child_init(php_apache_child_init, NULL, NULL, APR_HOOK_MIDDLE);
}</span>
ログイン後にコピー

以上代码声明了pre_config,post_config,handler和child_init4个挂钩以及对应的处理函数。其中pre_config,post_config,child_init是启动挂钩,它们在服务器启动时调用。handler挂钩是请求挂钩,它在服务器处理请求时调用。其中在post_config挂钩中启动php。它通过php_apache_server_startup函数实现,php_apache_server_startup函数通过调用sapi_startup启动sapi,并通过调用php_apache2_startup来注册sapi module struct,最后调用php_module_startup初始化php,其中又会初始化Zend引擎,以及填充zend_module_struct中的treat_data成员(通过php_startup_sapi_content_types)等。

  到这里,我们知道了Apache加载mod_php5模块的整个过程,可是这个过程与我们的饿SAPI有什么关系呢?mod_php5也定义了属于Apache的sapi_module_struct结构:

static sapi_module_struct apache2_sapi_module =<span> {
"apache2handler"<span>,
"Apache 2.0 Handler"<span>,
 
php_apache2_startup,                /* startup */<span>
php_module_shutdown_wrapper,            /* shutdown */<span>
 
NULL,                       /* activate */<span>
NULL,                       /* deactivate */<span>
 
php_apache_sapi_ub_write,           /* unbuffered write */<span>
php_apache_sapi_flush,              /* flush */<span>
php_apache_sapi_get_stat,           /* get uid */<span>
php_apache_sapi_getenv,             /* getenv */<span>
php_error,                  /* error handler */<span>
 
php_apache_sapi_header_handler,         /* header handler */<span>
php_apache_sapi_send_headers,           /* send headers handler */<span>
NULL,                       /* send header handler */<span>
 
php_apache_sapi_read_post,          /* read POST data */<span>
php_apache_sapi_read_cookies,           /* read Cookies */<span>
 
php_apache_sapi_register_variables,
php_apache_sapi_log_message,            /* Log message */<span>
php_apache_sapi_get_request_time,       /* Request Time */<span>
NULL,                       /* Child Terminate */<span>
 
STANDARD_SAPI_MODULE_PROPERTIES
};</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
ログイン後にコピー

这些方法都属于Apache服务器,以读取cookie为例,当我们在Apache服务器环境下,在PHP中调用读取Cookie时,最终获取的数据的位置是在激活SAPI时,它所调用的方法是read_cookie。

SG(request_info).cookie_data = sapi_module.read_cookies(TSRMLS_C);
ログイン後にコピー

对于每一个服务器在加载时,我们都指定了sapi_module,而Apache的sapi_module是apache2_sapi_module。其中对应read_cookie的方法是php_apache_sapi_read_cookie函数。这也是定义SAPI结构的理由:统一接口,面向接口编程,具有更好的扩展性和适应性。

(2)Apache的运行过程

Apache的运行包括启动阶段和运行阶段,启动阶段Apache以root完成启动,整个过程处于单进程单线程的环境中,这个阶段包括配置文件解析、模块加载、系统资源初始化(例如日志文件、共享内存段、数据库连接等)等工作。

在运行阶段,Apache主要工作是处理用户的服务请求,在这个阶段Apache以普通用户运行。主要是安全性考虑,Apache对HTTP的请求可以分为连接、处理和断开连接三个大的阶段。

2、FastCGI

(1)cgi是通用网关接口(Common Gateway Intedface),它可以让一个客户端从网页浏览器向执行在Web服务器上的程序请求数据。CGI描述了客户端和这个程序之间传输数据的标准。CGI的一个目的是独立于任何语言,所以CGI可以用任何语言编写,只要这种语言具有标准输入、输出和环境变量。如PHP、perl、tcl等。

FastCGI是Web服务器和处理程序之间通信的一种协议,是CGI的一种改进方案,FastCGI像是一个常驻型的CGI,它可以一直执行,在请求到达时不会花费时间去fork一个进程来处理(这是CGI对位人诟病的fork-and-execute模式)。正是因为它只是一个通信协议,它还支持分布式的运算,即FastCGI程序可以在网站服务器以外的主机上执行并且接受来自其他网站服务器的请求

FastCGI的整个流程是这样的:

  Step1:Web Server启动时载入FastCGI进程管理器(IIS ISAPI或Apache Module)

  Step2:FastCGI进程管理器自身初始化,启动多个CGI解释器进程(可见多个php-cgi)并等待来自web server的连接

  Step3:当客户端请求到达Web Server时,FastCGI进程管理器选择并连接到一个CGI解释器。Web Server将CGI环境变量和标准输入发送到FastCGI子进程php-cgi

  Step4:FastCGI子进程完成处理后将标准输出和错误新词从同一连接返回Web Server 当FastCGI子进程关闭连接时,请求便结束。FastCGI子进程接着等待并处理来自FastCGI进程管理器(运行在Web Server中)的下一个连接。在CGI模式中,php-cgi在此便退出了。

(2)php中CGI实现

PHP的CGI实现了Fastcgi协议。是一个TCP或UDP协议的服务器接受来自Web服务器的请求,当启动时创建TCP/UDP协议的服务器的socket监听,并接受相关请求并进行处理。随后就进入了PHP的生命周期:模块初始化,sapi初始化,处理PHP请求,模块关闭,sapi关闭等 就构成了整个CGI的生命周期。

以TCP为例在,在TCP的服务端,一般会执行这样几个步骤:

1、调用socket函数创建一个TCP用的流式套接字;

2、调用bind函数将服务器的本地地址与前面创建的套接字绑定;

3、调用listen函数将新创建的套接字作为监听,等待客户端发起的连接,当客户端有多个连接连接到这个套接字时,可能需要排队处理;

4、服务器进程调用accept函数进入阻塞状态,直到有客户进程调用connect函数而建立起一个连接;

5、当与客户端创建连接后,服务器调用read_stream函数读取客户端的请求;

6、处理完数据后,服务器调用write函数向客户端发送应答

TCP上客户-服务器事务的时序如图所示:

php的CGI实现从cgi_main.c文件的main函数开始,在main函数中调用了定义在fastcgi.c文件中的初始化,监听等函数。对比TCP的流程,我们查看php对TCP协议的实现,虽然php本身也实现了这些流程,但是在main函数中一些过程被封装成一个函数实现。对应TCP的操作流程,PHP首先会执行创建socket,绑定套接字,创建监听:

if<span> (bindpath) {
    fcgi_fd = fcgi_listen(bindpath, 128);   //  socket˥˦2sfcgi_initɩ    
<span>Ȑ
    ...
}</span></span>
ログイン後にコピー

在fastcgi.c文件中,fcig_listen函数主要用于创建、绑定socket并开始监听,它走完了前面所列TCP流程的前三个阶段,

 if ((listen_socket = socket(sa.sa.sa_family, SOCK_STREAM, 0)) < 0 ||<span>
        ...
        bind(listen_socket, (struct sockaddr *) &sa, sock_len) < 0 ||<span>
        listen(listen_socket, backlog) < 0<span>) {
        ...
    }</span></span></span>
ログイン後にコピー

当服务端初始化完成后,进程调用accept函数进入阻塞状态,在main函数中我们看到如下代码:

  while<span> (parent) {
        do<span> {
            pid = fork();   //  o&Ograve;Ÿ
<span>ȨėJ
            switch<span> (pid) {
            case 0: //  ȨėJ
                parent = 0<span>;
 
                /* don't catch our signals */<span>
                sigaction(SIGTERM, &old_term, 0);   //  ľ&acirc;&macr;ķ
                sigaction(SIGQUIT, &old_quit, 0);   //  ľĿɰ&pound;ƺ
                sigaction(SIGINT,  &old_int,  0);   //  ľĿKȠƺ
                break<span>;
                ...
                default<span>:
                /* Fine */<span>
                running++<span>;
                break<span>;
        } while (parent && (running <<span> children));
 
    ...
        while (!fastcgi || fcgi_accept_request(&request) >= 0<span>) {
        SG(server_context) = (void *) &<span>request;
        init_request_info(TSRMLS_C);
        CG(interactive) = 0<span>;
                    ...
            }</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
ログイン後にコピー

如上的代码是一个生成子进程,并等待用户请求。在fcgi_accept_request函数中,程序会调用accept函数阻塞新创建的线程。当用户的请求到达时,fcgi_accept_request函数会判断是否处理用户的请求,其中会过滤某些连接请求,忽略受限制客户的请求,如果程序受理用户的请求,他将分析请求的信息,将相关的变量写到对应的变量中。其中在读取请求内容时调用了safe_read方法。如下所示:main()->fcgi_accept_request()->fcgi_read_request()->safe_read()

static inline ssize_t safe_read(fcgi_request *req, const void *<span>buf, size_t 
count)
{
    size_t n = 0<span>;
    do<span> {
    ... //  省略 对win32的处理<span>
        ret = read(req->fd, ((char*)buf)+n, count-n);   //  非win版本的读操作
<span>D‰
    ... // 省略
    } while (n !=<span> count);
 
}</span></span></span></span></span></span>
ログイン後にコピー

如上对应服务器端读取用户的请求数据。

在请求初始化完成,读取请求完毕后,就该处理请求的PHP文件了。假设此次请求为PHP_MODE_STANDARD则会调用php_execute_script执行PHP文件。在此函数中它先初始化此文件相关的一些内容,然后再调用zend_execute_scripts函数,对PHP文件进行词法分析和语法分析,生成中间代码,并执行zend_execute函数,从而执行这些中间代码。

  在处理完用户的请求后,服务端将返回信息给客户端,此时在main函数中调用的是fcgi_finish_request(&request , 1);fcgi_finish_request函数定义在fasftcgi.c文件中。

  在发送了请求的应答后,服务器端将会执行关闭操作,仅限于CGI本身的关闭,程序执行的是fcgi_close函数。

www.bkjia.comtruehttp://www.bkjia.com/PHPjc/1095476.htmlTechArticle深入理解PHP内核(三)概览-SAPI概述,深入理解-sapi 本文链接:http://www.orlion.ml/234/ 1、在PHP生命周期的各个阶段,一些与服务相关的操作都是通...
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

Linux カーネルの機能を探る: 5 つの主要部分の詳細な紹介 Linux カーネルの機能を探る: 5 つの主要部分の詳細な紹介 Mar 21, 2024 am 09:57 AM

Linux カーネルは、オペレーティング システムの中核部分として、ハードウェア リソースの管理やシステム コールの提供などの重要な機能を担っています。この記事では、プロセス管理、ファイル システム、ネットワーク通信、デバイス ドライバー、メモリ管理を含む Linux カーネルの 5 つの主要な部分を詳しく掘り下げ、詳細な紹介とコード例を提供します。 1. プロセス管理 プロセスの作成 Linux カーネルでは、プロセスの作成は fork() システム コールを通じて実装されます。簡単なコード例を次に示します: #include

非 MMU サポートは、Linux カーネルの uClinux ポートによって提供されますか? 非 MMU サポートは、Linux カーネルの uClinux ポートによって提供されますか? Jan 26, 2024 pm 05:18 PM

長くて技術的な内容が多いので、クリックしてフォローしていけば迷うことはありません。序文: Linux カーネルについて理解する コンピュータ システムはハードウェアとソフトウェアが共生しており、相互に依存しており、分離することはできません。コンピューター ハードウェア Linux カーネルの移植手順には、コンピューター シリンダーを構成する周辺デバイス、プロセッサ、メモリ、ハード ドライブ、およびその他の電子デバイスが含まれます。また、それを操作および制御するためのソフトウェアがなければ、単独で動作することはできません。この制御作業を実行するソフトウェアはオペレーティング システムと呼ばれ、Linux 用語では「カーネル」または「コア」と呼ばれます。 Linux カーネルの主要モジュール (コンポーネント) は、ストレージ管理、CPU とプロセス管理、ファイル システム、デバイス管理とドライバー、ネットワーク通信 Linux フォーラム、およびシステムの部分に分かれています。

Linux カーネルの TCP プロトコル スタック最適化に関する秘密のヒントが明らかに Linux カーネルの TCP プロトコル スタック最適化に関する秘密のヒントが明らかに Jan 28, 2024 am 09:39 AM

親愛なる読者の皆さん、こんにちは!ここで、Linux カーネル TCP プロトコル スタックの開発と最適化における専門スキルとともに、シニア ネットワーク エンジニアとして蓄積してきた貴重な経験とスキルを皆さんと共有できることを光栄に思います。この記事を通じて、この分野に強い興味を持っている方、あるいは現在取り組んでいる方に、お互いに学び合い、議論し、実践的で役立つ参考資料をお届けできればと思います。 1. TCP 接続の確立 TCP 接続の確立は、TCP プロトコル スタックの重要なトランザクションですが、多くの接続問題に直面することは珍しくありません。慎重な検討と詳細なデバッグの結果、SYN フラッディング攻撃の防止 (システム パラメーターの調整による) やネットワーク輻輳への対処 (つまり、TCPFastOp の使用) など、いくつかの一般的で実用的な問題とその解決策を発見しました。

実践的な戦闘 | RISC-V Linux エントリ アドレス 2M 予約メモリの最適化 実践的な戦闘 | RISC-V Linux エントリ アドレス 2M 予約メモリの最適化 Aug 01, 2023 pm 03:37 PM

前回の記事では、RISC-V Linux のエントリ アドレスが 2M アラインメントである必要があると述べましたが、今日は 2M アラインメントの問題を解決する方法、またはその一部を最適化する方法について説明します。想い出。

Linux カーネルのソースコード配布の詳細な調査 Linux カーネルのソースコード配布の詳細な調査 Mar 15, 2024 am 10:21 AM

これは、Linux カーネルのソース コード配布を詳しく調査した 1500 ワードの記事です。スペースが限られているため、ここでは Linux カーネル ソース コードの組織構造に焦点を当て、読者の理解を深めるためにいくつかの具体的なコード例を示します。 Linux カーネルは、ソース コードが GitHub でホストされているオープン ソース オペレーティング システム カーネルです。 Linux カーネルのソース コード配布全体は非常に大きく、複数の異なるサブシステムとモジュールが関与する数十万行のコードが含まれています。 Linux カーネルのソース コードをより深く理解するには

Linux カーネル: コンピュータ オペレーティング システムの隠れたボスを明らかにする Linux カーネル: コンピュータ オペレーティング システムの隠れたボスを明らかにする Mar 24, 2024 am 09:10 AM

Linux カーネルがコンピュータ オペレーティング システムで重要な役割を果たしているという見解について説明します Linux カーネルの設計と実装 Linux カーネルの設計と実際のアプリケーションの詳細な分析を通じて、この分野における Linux カーネルの卓越した位置と影響力を明らかにします。 1. 最適化されたメモリ管理 仮想メモリ管理テクノロジを使用することで、Linux カーネルはメモリの割り当てとリサイクルを効率的に完了できます。 Linux カーネルは、置換ページ アルゴリズムの助けを借りて、物理メモリと仮想メモリ間のマッピング関係を正確に処理するように設計および実装されています。アプリケーションの特定のニーズに基づいて柔軟に調整できるため、システム全体のパフォーマンスが向上します。 2. 強力なプロセス管理カーネルは、優れたマルチタスク技術を使用して、複数のプロセスが単一システム内で調和して共存できるようにします。慎重に配合

Android システムと Linux カーネルの関係を調べる Android システムと Linux カーネルの関係を調べる Mar 14, 2024 pm 12:48 PM

Android システムと Linux カーネルは 2 つの密接な関係にあるエンティティであり、両者の関係は密接かつ複雑です。 Android システムでは、Linux カーネルが重要な役割を果たし、Android システムに基盤となるハードウェア ドライバーとシステム コールのサポートを提供します。この記事では、Android システムと Linux カーネルの関係、それらがどのように相互作用して連携するかを検討し、いくつかの具体的なコード例を示します。 Android は、Linux カーネルに基づいて開発されたモバイル オペレーティング システムで、主にスマートフォンやタブレットなどのモバイル デバイスに使用されます。 L

Linux カーネルの障害をデバッグする方法を本当に知っていますか? この記事を読めば、きっと理解できるでしょう。 Linux カーネルの障害をデバッグする方法を本当に知っていますか? この記事を読めば、きっと理解できるでしょう。 Aug 03, 2023 pm 04:50 PM

Linux カーネルはオペレーティング システムの中核であり、CPU、I/O デバイス、物理メモリ、ファイル システムなどのシステム リソースへのアクセスを制御します。ブート プロセス中およびシステムの実行中、カーネルはさまざまなメッセージをカーネル リング バッファに書き込みます。これらのメッセージには、システム動作に関するさまざまな情報が含まれています。

See all articles