目次
前言" >前言
跟踪运行轨迹" >跟踪运行轨迹
词法语法分析->opcode" >词法语法分析->opcode
結論" >結論
ホームページ バックエンド開発 PHPチュートリアル PHP-Zend エンジン分析 - Hello World (2)

PHP-Zend エンジン分析 - Hello World (2)

Jun 13, 2016 pm 12:32 PM
echo handler zend

PHP-Zend引擎剖析之Hello World(二)

前言

这一次,我围绕Hello World来展开Zend虚拟机的执行过程。Hello World的PHP版本:


? ? ?echo 'Hello World';

?>


前一篇文章聊到的词法分析阶段就会把上边的脚本分析出一个Token序列:

我们得到一个Token序列:T_OPEN_TAG, T_ECHO, T_CONSTANT_ENCAPSED_STRING, ';', T_CLOSE_TAG。但在Zend虚拟机执行的过程中,是怎么去分析这个Token序列的?

跟踪运行轨迹

我们还是从命令行入手,在$PHPSRC/sapi/cli/php_cli.c中的do_cli函数里边接收了命令行的参数输入(php -f HelloWorld.php表示执行HelloWorld.php文件)。

我们追踪到$PHPSRC/main/main.c里边有php_execute_script的定义,紧接着调用了zend_execute_scripts() ,在zend_execute_scripts的定义里边我们发现了:


?EG(active_op_array) =?zend_compile_file(file_handle, type TSRMLS_CC); zend_execute(EG(active_op_array) TSRMLS_CC);


首先通过zend_compile_file把文件解析成opcode中间代码(这一步会经过词法语法分析),然后用zend_execute执行这个生成的中间代码(这里就是所谓的运行时)。

这里很像C语言的编译方式,先编译成汇编,然后再转成机器码,这里的opcode就类似C语言编译过程中生成的汇编。

还可以延伸出一个思路,因为每次解析PHP文件时,都需要经过词法语法分析得到对应的opcode,其实在脚本文件不变化的时候,生成的opcode也不需要变化,因此为了减少PHP脚本的执行时间,可以把脚本的opcode缓存起来(例如缓存在共享内存里边)。

我给出一个流程图,然后随着这个流程图,看看Zend做了些什么事情:

我们先看看如何编译出opcode的。

词法语法分析->opcode

从上节知道我们通过zend_compile_file(实际上为compile_file()<定义在Zend/zend_language_scanner.c的555行>)把脚本文件编译出opcode,实际上通过zendparse这个API来编译出opcode的。
PHP的语法解析器是用bison来生成,安装完之后在$PHPSRC/Zend目录运行:
bison -o zend_language_parser.c?zend_language_parser.y
在Zend目录下就会生成语法解析器zend_language_parser.c。而这里的zendparse就是语法解析器里边的yyparse!
我们忽略掉生成的语法解析器,就Hello World的例子来跟踪一下bison的声明文件(我去掉不想关的声明):

start:
top_statement_list???? { zend_do_end_compilation(TSRMLS_C); }
;top_statement_list:
top_statement_list? { zend_do_extended_info(TSRMLS_C); } top_statement { HANDLE_INTERACTIVE(); }
|???? /* empty */
;top_statement:
statement????????????????????????????? { zend_verify_namespace(TSRMLS_C); }
;statement:
unticked_statement { DO_TICKS(); }
|???? T_STRING ':' { zend_do_label(&$1 TSRMLS_CC); }
;unticked_statement:
|???? T_ECHO echo_expr_list ';'echo_expr_list:
echo_expr_list ',' expr { zend_do_echo(&$3 TSRMLS_CC); }
|???? expr???????????????????????? { zend_do_echo(&$1 TSRMLS_CC); }
;

expr:
r_variable???????????????????????? { $$ = $1; }
|???? expr_without_variable????????? { $$ = $1; }
;

expr_without_variable:
|???? scalar??????????????????? { $$ = $1; }

scalar:
|???? common_scalar?????????????? { $$ = $1; }
;

common_scalar:
|???? T_CONSTANT_ENCAPSED_STRING???? { $$ = $1; }
;
ログイン後にコピー

语法分析从start开始,自上而下的分析,一个PHP脚本就是对应一个top_statement_list,接着分成每一行一条语句statement,发现echo 'Hello World'是一条unticked_statement(留意一下echo_expr_list的声明,?我们还可以发现语法上是支持echo 'Hello', ' World'的)。最后递归到T_CONSTANT_ENCAPSED_STRING状态就结束了这一行的语法解析。在这里我们忽略掉编译原理在语法分析阶段是怎么去做回溯等等东西,我们关注一下Zend引擎自身的的问题。

在规则后边的块"{}"里边的代码就是用来处理扫描到此规则时的动作,可以看到echo的执行是调用了zend_do_echo函数的。在动作声明的块里边我们看到了$$, $1,$2,$3等,这些对应的就是该条规则里边的返回值,参数1,参数2……,这里的返回值以及参数都是YYSTYPE类型,这个类型在43行里边有定义:#define YYSTYPE znode。znode的定义在zend_compile.h里边:

zend_op の構造に気づいたので追跡してみたところ、最終的に各ステートメントに対応するオペコード構造であることがわかりました。 ! ! !

オペコードの構造はアセンブリと非常によく似ており、1 つの演算子と 2 つのオペランドがあります。

Zend エンジンでは、各オペコードの主なものはハンドラーです。このハンドラーが Zend でどのように生成されるかについては後で説明します。ここで少し待って、Hello World の例によって生成されたオペコードを振り返ってみましょう。

vld をインストールして、php -dvld.active=1 HelloWorld.php を実行すると、この PHP ファイルによってコンパイルされたオペコード リストが表示されます。

echo ステートメントのオペコード タイプは ECHO であり、return には戻り値がなく、オペランド「Hello World」が 1 つだけあることがわかります。

構文分析後、各ステートメントのオペコードをコンパイルし、Zend はそれを op_array (実際にはオペコードのリスト) に入れます。

戻って zend_do_echo が何をしたか見てみましょう:

まず get_next_op を通じて現在の op_array の末尾にオペコードを生成し、次にそのオペコード タイプを ZEND_ECHO に設定し、次にその最初のパラメータ op1 を設定し、2 番目のパラメータ op2 を未使用としてマークします。

非常に多くの手順を経て、op_array のリストが得られました。このリスト内の各オペコードは独自のタイプにバインドされています。次に、各オペコード ノードがどのようにハンドラーにバインドされているかを見てみましょう。

zend_vm_def.h は、ZEND_ECHO のハンドラーを定義します。ここでの 40 に注意してください。これは、定数、変数など、複数のタイプのエコー パラメーターがあり、異なるハンドラーに対応するためです。

オペコードに対応するすべてのハンドラーは zend_vm_execute.h で定義されています。ここでは、エコー関連のハンドラーのみに注目して、次のコードに注目します。


void zend_init_opcodes_handlers(void) { static const opcode_handler_t labels[] = {//40913行 ZEND_ECHO_SPEC_CONST_HANDLER,//41914行 ZEND_ECHO_SPEC_CONST_HANDLER, ZEND_ECHO_SPEC_CONST_HANDLER, ZEND_ECHO_SPEC_CONST_HANDLER, ZEND_ECHO_SPEC_CONST_HANDLER };


ここでのラベルと行番号を思い出してください。

ハンドラーを取得するメソッドの最後にある return ステートメントの計算を発見しました。先ほどの echo のオペコードによれば、これは 40 です (2 つのパラメーター op1 と op2 の型が両方とも 0 であると仮定します)。したがって、対応するハンドラーは次のようになります:

zend_opcode_handlers[40*25 0*5 0*5] =?zend_opcode_handlers[1000] =?labels[1000] =?ZEND_ECHO_SPEC_CONST_HANDLER (どうしてそうなったのですか?理由: 41914 行-40913 行-1=1000)。

仮想マシンがオペコードを実行します

前に、zend_compile_file がスクリプトをオペコードのリストにコンパイルすることを説明しました。


?EG(active_op_array) =?

zend_compile_file(file_handle、タイプ TSRMLS_CC); zend_execute(EG(active_op_array) TSRMLS_CC); ;


この後、Zend エンジンは zend_execute を使用して、返されたオペコードを実行します。

zend_execute の最後の実行は、Zend/zend_vm_execute.h の 337 行目にあります。

ご覧のとおり、仮想マシンが実行されると、現在のオペコード リストをループし、オペコードの各行のハンドラーを呼び出し、次に何を行うかを決定します (関数呼び出しなど)。後で展開されます) ハンドラーの戻り値に基づきます。

この記事では、Hello World に関連するもののみに焦点を当てます。最終的な配置により、エコーのハンドラーが ZEND_ECHO_SPEC_CONST_HANDLER であることがわかります。

zend_write = (zend_write_func_t)utility_functions->write_function;

ここでのutility_functionsには、いくつかの基本的なハンドラーが含まれています。たとえば、コマンドラインモードでは、これは最終的に
と呼ばれます。

sapi_cli_single_write:

ソース コードから、最後の書き込み操作は write/fwrite を呼び出して標準出力ストリーム (つまり、端末画面上) に書き込むことであることがわかります。

結論

最後に、前のプロセスに基づいて、フローチャートを再度展開します。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、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)

テクノロジーの世界を探索できる、厳選された 5 つの Go 言語オープンソース プロジェクト テクノロジーの世界を探索できる、厳選された 5 つの Go 言語オープンソース プロジェクト Jan 30, 2024 am 09:08 AM

今日の急速な技術発展の時代では、雨後の筍のようにプログラミング言語が出現しています。多くの注目を集めている言語の 1 つは Go 言語です。Go 言語は、そのシンプルさ、効率性、同時実行の安全性などの機能により多くの開発者に愛されています。 Go 言語は、多くの優れたオープンソース プロジェクトがある強力なエコシステムで知られています。この記事では、厳選された 5 つの Go 言語オープンソース プロジェクトを紹介し、読者を Go 言語オープンソース プロジェクトの世界へ導きます。 KubernetesKubernetes は、自動化されたオープンソースのコンテナ オーケストレーション エンジンです。

Go 言語開発の要点: 5 つの人気のあるフレームワークの推奨事項 Go 言語開発の要点: 5 つの人気のあるフレームワークの推奨事項 Mar 24, 2024 pm 01:15 PM

「Go 言語開発の要点: 5 つの人気フレームワークの推奨事項」 高速で効率的なプログラミング言語として、Go 言語はますます多くの開発者に好まれています。開発効率を向上させ、コード構造を最適化するために、多くの開発者はフレームワークを使用してアプリケーションを迅速に構築することを選択します。 Go 言語の世界には、選択できる優れたフレームワークが数多くあります。この記事では、5 つの人気のある Go 言語フレームワークを紹介し、読者がこれらのフレームワークをよりよく理解して使用できるように、具体的なコード例を示します。 1.GinGin は高速な軽量 Web フレームワークです。

Zend Framework でのアクセス許可制御に ACL (アクセス コントロール リスト) を使用する方法 Zend Framework でのアクセス許可制御に ACL (アクセス コントロール リスト) を使用する方法 Jul 29, 2023 am 09:24 AM

Zend Framework でのアクセス許可制御に ACL (AccessControlList) を使用する方法 はじめに: Web アプリケーションでは、アクセス許可制御は重要な機能です。これにより、ユーザーはアクセスを許可されたページと機能にのみアクセスできるようになり、不正アクセスが防止されます。 Zend フレームワークは、ACL (AccessControlList) コンポーネントを使用してアクセス許可制御を実装する便利な方法を提供します。この記事では、Zend Framework で ACL を使用する方法を紹介します。

PHP 実装フレームワーク: Zend Framework 入門チュートリアル PHP 実装フレームワーク: Zend Framework 入門チュートリアル Jun 19, 2023 am 08:09 AM

PHP 実装フレームワーク: ZendFramework 入門チュートリアル ZendFramework は、PHP によって開発されたオープン ソースの Web サイト フレームワークであり、現在 ZendTechnologies によって保守されています。ZendFramework は、MVC デザイン パターンを採用し、Web2.0 アプリケーションと Web サーブの実装に役立つ一連の再利用可能なコード ライブラリを提供します。 。 ZendFramework は PHP 開発者に非常に人気があり、尊敬されており、幅広い機能を備えています。

Golang の Web フレームワーク Echo フレームワークを使用した分散タスク スケジューリングの実装 Golang の Web フレームワーク Echo フレームワークを使用した分散タスク スケジューリングの実装 Jun 24, 2023 am 11:49 AM

インターネットの発達や情報技術の進歩によりビッグデータの時代が到来し、データ分析や機械学習などの分野も広く活用されています。これらの分野では、タスクのスケジューリングは避けられない問題です。効率的なタスクのスケジューリングを実現する方法は、効率を向上させるために重要です。この記事では、Golang の Web フレームワーク Echo フレームワークを使用して分散タスク スケジューリングを実装する方法を紹介します。 1. Echo フレームワークの概要 Echo は、高性能、スケーラブル、軽量の GoWeb フレームワークです。 HTTPに基づいています

Laravel 開発: Laravel Echo と Pusher を使用して WebSocket 通信を実装するにはどうすればよいですか? Laravel 開発: Laravel Echo と Pusher を使用して WebSocket 通信を実装するにはどうすればよいですか? Jun 13, 2023 pm 05:01 PM

Laravel は、拡張性と効率性に優れた人気の PHP フレームワークであり、開発者が高品質の Web アプリケーションを迅速に構築できる強力なツールとライブラリを多数提供しています。その中でも、LaravelEcho と Pusher は、WebSocket 通信を簡単に実装できる 2 つの非常に重要なツールであり、この記事では、Laravel アプリケーションでこれら 2 つのツールを使用する方法について詳しく説明します。 WebSocket とは何ですか? Webソケット

PHPにおけるechoキーワードの役割と使い方の詳しい説明 PHPにおけるechoキーワードの役割と使い方の詳しい説明 Jun 28, 2023 pm 08:12 PM

PHP における echo キーワードの役割と使用法の詳細な説明 PHP は、Web 開発で広く使用されているサーバーサイド スクリプト言語です。 echo キーワードは、PHP でコンテンツを出力するために使用されるメソッドです。この記事では、echoキーワードの機能と使い方を詳しく紹介します。機能: echo キーワードの主な機能は、コンテンツをブラウザに出力することです。 Web 開発では、フロントエンド ページにデータを動的に表示する必要がありますが、このとき、echo キーワードを使用してデータをページに出力できます。 e

Go 言語フレームワークを探索する: 見逃せない 5 つの選択肢! Go 言語フレームワークを探索する: 見逃せない 5 つの選択肢! Feb 19, 2024 pm 02:29 PM

Go 言語は、高速で効率的なプログラミング言語として、常にプログラマーに好まれてきました。 Go 言語エコシステムでは、フレームワークは、開発者がアプリケーションをより迅速に構築できるようにする上で重要な役割を果たします。この記事では、Go 言語のフレームワークを 5 つ紹介し、それぞれの特徴と使い方を理解します。 1. Gin フレームワーク Gin フレームワークは、高速で高性能な特性を備えた軽量の Web フレームワークです。 Jin フレームワークを使用して、RESTful API と Web アプリケーションを迅速に構築します。簡単なコード例を次に示します。

See all articles