PHP7 ソースコード: PHP 仮想マシンの詳細な分析
この記事の内容は、PHP7 のソース コードに関するものです。PHP 仮想マシンの詳細な分析です。一定の参考価値があります。必要な友人は参照してください。お役に立てば幸いです。
#1. 物理マシンから始める
仮想マシンもコンピュータであり、その設計思想には物理マシンと多くの類似点があります;1.1 フォン ノイマン アーキテクチャ
フォン ノイマンは、当然のデジタル コンピュータの父です。現在のコンピュータはフォン ノイマン アーキテクチャを使用しており、設計上のアイデアには主に次のようないくつかの側面が含まれています:
- 同じメモリ上に命令とデータが区別なく混在して格納されており、すべてメモリ上のデータとなります。最新の CPU の保護モードでは、各メモリ セグメントにセグメント記述子があり、この記述子は、このメモリ セグメントのアクセス権 (読み取り可能、書き込み可能、および実行可能) を記録します。これは、どのメモリが命令を格納し、どのメモリがデータであるかを偽装して指定します);
- メモリは、アドレスによってアクセスされる線形にアドレス指定された 1 次元構造です。各ユニットのビット数は次のとおりです。修正済み;
- データはバイナリで表現されます;
- 命令はオペコードとオペランドで構成されます。オペコードはこの命令の演算タイプを指定し、オペランドはオペランド自体またはオペランドのアドレスを指定します。オペランド自体にはデータ型がなく、そのデータ型はオペコードによって決定されます。どのコンピューター アーキテクチャでも一連の命令が外部に提供されます。
- オペレーターは制御信号を直接送信します。命令やさまざまな操作を実行してコンピュータを制御すること。命令カウンタは、実行される命令が配置されているメモリ アドレスを示します。命令カウンタは 1 つだけで、通常は順次増加しますが、演算結果やその時の外部条件により実行順序が変わる場合があります。 1.2 アセンブリ言語の概要
どのようなアーキテクチャのコンピュータでも一連の命令が提供されます。命令はオペコードとオペランドで構成されます。オペコードは操作です。オペランドは即値またはストレージ アドレスです。各命令は 0、1、または 2 つのオペランドを持つことができます。
命令はバイナリの文字列です。アセンブリ言語はバイナリ命令のテキスト形式です。 push %ebx
mov %eax, [%esp+8]
mov %ebx, [%esp+12]
add %eax, %ebx
pop %ebx
オペランドはデータにアクセスできる単なる記憶領域です; オペランド自体はデータ型ではなく、そのデータ型はオペレーション コードによって決まります。
たとえば、movb はバイトを送信し、movw はワードを送信し、movl はダブル ワードを送信します。1.3 関数呼び出しスタック
プロセス (関数) はコードのカプセル化であり、外部に公開されるのは指定されたパラメーターのセットとオプションの戻り値のみです。この関数はプログラム内のさまざまな場所で呼び出すことができます。プロセス P がプロセス Q を呼び出すと仮定すると、Q が実行され、その後プロセス P に戻ります。この関数を実装するには、次の 3 つの点を考慮する必要があります:
命令ジャンプ: プロセス Q に入るとき、プログラム カウンタは Q のコード アドレスの先頭に設定する必要があります。戻るとき、プログラム カウンタは P で Q を呼び出した後の命令のアドレスに設定する必要があります。
データ転送: P は 1 つ以上のパラメーターを Q に提供でき、Q は P に値を返すことができます。
メモリの割り当てと解放: Q の開始時実行時に、ローカル変数にメモリ空間を割り当てる必要がある場合があり、戻る前にこれらのメモリ空間を解放する必要があります。
ほとんどの言語プロシージャ呼び出しは、スタック データ構造; 次の図に示すように:
通常発生するスタック オーバーフローは、深すぎる関数を呼び出すことによって発生します。 stack;
2.PHP 仮想マシン
以下は、これら 3 つの点から PHP 仮想マシンの設計アイデアを詳細に分析したものです。
# 2.1 は次のことを指します
##2.1.1 命令タイプ##あらゆるアーキテクチャのコンピュータは、一連の外部命令を提供する必要があります。これは、によってサポートされる一連の操作タイプを表します。コンピューター; PHP 仮想マシンは、zend_vm_opcodes.h ファイルで定義されている 186 の命令を外部に提供します;//加、减、乘、除等 #define ZEND_ADD 1 #define ZEND_SUB 2 #define ZEND_MUL 3 #define ZEND_p 4 #define ZEND_MOD 5 #define ZEND_SL 6 #define ZEND_SR 7 #define ZEND_CONCAT 8 #define ZEND_BW_OR 9 #define ZEND_BW_AND 10 ……………………
2.1.2.1 命令の表現
命令はオペレーション コードとオペランドで構成され、オペレーション コードは次の内容を指定します。この命令は操作タイプ、オペランドはオペランド自体またはオペランドのアドレスを指定します。PHP 仮想マシンは命令形式を次のように定義します: オペコード オペランド 1 オペランド 2 戻り値; 構造体 _zend_op を使用して、命令 :
struct _zend_op { const void *handler; //指针,指向当前指令的执行函数 znode_op op1; //操作数1 znode_op op2; //操作数2 znode_op result; //返回值 uint32_t extended_value;//扩展 uint32_t lineno; //行号 zend_uchar opcode; //指令类型 zend_uchar op1_type; //操作数1的类型(此类型并不代表字符串、数组等数据类型;其表示此操作数是常量,临时变量,编译变量等) zend_uchar op2_type; //操作数2的类型 zend_uchar result_type; //返回值的类型 };
2.1.2.2 オペランドの表現 从上面可以看到,操作数使用结构体znode_op表示,定义如下: constant、var、num等都是uint32_t类型的,这怎么表示一个操作数呢?(既不是指针不能代表地址,也无法表示所有数据类型); 2.2 数据存储 PHP虚拟机支持多种数据类型:整型、浮点型、字符串、数组,对象等;PHP虚拟机如何存储和表示多种数据类型? 2.1.2.2节指出结构体_znode_op代表一个操作数;操作数可以是一个偏移量(计算得到一个地址,即zval结构体的首地址),或者一个zval指针;PHP虚拟机使用zval结构体表示和存储多种数据; zval.u1.type表示数据类型, zend_types.h文件定义了以下类型: zend_value存储具体的数据内容,结构体定义如下: _zend_value占16字节内存;long、double类型会直接存储在结构体;引用、字符串、数组等类型使用指针存储; 代码中根据zval.u1.type字段,判断数据类型,以此决定操作_zend_value结构体哪个字段; 可以看出,字符串使用zend_string表示,数组使用zend_array表示… 如下图为PHP7中字符串结构图: 2.3 再谈指令 2.1.2.1指出,指令使用结构体_zend_op表示;其中最主要2个属性:操作函数,操作数(两个操作数和一个返回值); 操作数的类型(常量、临时变量等)不同,同一个指令对应的handler函数也会不同;操作数类型定义在 Zend/zend_compile.h文件: 对于$a=1,其操作函数为: ZEND_ASSIGN_SPEC_CV_CONST_RETVAL_UNUSED_HANDLER;函数实现为: 2.4 函数栈帧 2.4.1指令集 上面分析了指令的结构与表示,PHP虚拟机使用_zend_op_array表示指令的集合: 注意: last_var代表IS_CV类型变量的个数,这种类型变量存放在vars数组中;在整个编译过程中,每次遇到一个IS_CV类型的变量(类似于$something),就会去遍历vars数组,检查是否已经存在,如果不存在,则插入到vars中,并将last_var的值设置为该变量的操作数;如果存在,则使用之前分配的操作数 2.4.2 函数栈帧 PHP虚拟机实现了与1.3节物理机类似的函数栈帧结构; 使用 _zend_vm_stack表示栈结构;多个栈之间使用prev字段形成单向链表;top和end指向栈低和栈顶,分别为zval类型的指针; 考虑如何设计函数执行时候的帧结构:当前函数执行时,需要存储函数编译后的指令,需要存储函数内部的局部变量等(2.1.2.2节指出,操作数使用结构体znode_op表示,其内部使用uint32_t表示操作数,此时表示的就是当前zval变量相对于当前函数栈帧首地址的偏移量); PHP虚拟机使用结构体_zend_execute_data存储当前函数执行所需数据; 函数开始执行时,需要为函数分配相应的函数栈帧并入栈,代码如下: 从上面分析可以得到函数栈帧结构图如下所示: 总结 PHP虚拟机也是计算机,有三点是我们需要重点关注的:指令集(包含指令处理函数)、数据存储(zval)、函数栈帧; 此时虚拟机已可以接受指令并执行指令代码; 但是,PHP虚拟机是专用执行PHP代码的,PHP代码如何能转换为PHP虚拟机可以识别的指令呢——编译; PHP虚拟机同时提供了编译器,可以将PHP代码转换为其可以识别的指令集合; 理论上你可以自定义任何语言,只要实现编译器,能够将你自己的语言转换为PHP可以识别的指令代码,就能被PHP虚拟机执行; おすすめ関連記事:
其实,操作数大多情况采用的相对地址表示方式,constant等表示的是相对于执行栈帧首地址的偏移量;
另外,_znode_op结构体中有个zval *zv字段,其也可以表示一个操作数,这个字段是一个指针,指向的是zval结构体,PHP虚拟机支持的所有数据类型都使用zval结构体表示;typedef union _znode_op {
uint32_t constant;
uint32_t var;
uint32_t num;
uint32_t opline_num;
#if ZEND_USE_ABS_JMP_ADDR
zend_op *jmp_addr;
#else
uint32_t jmp_offset;
#endif
#if ZEND_USE_ABS_CONST_ADDR
zval *zv;
#endif
} znode_op;
struct _zval_struct {
zend_value value; //存储实际的value值
union {
struct { //一些标志位
ZEND_ENDIAN_LOHI_4(
zend_uchar type, //重要;表示变量类型
zend_uchar type_flags,
zend_uchar const_flags,
zend_uchar reserved) /* call info for EX(This) */
} v;
uint32_t type_info;
} u1;
union { //其他有用信息
uint32_t next; /* hash collision chain */
uint32_t cache_slot; /* literal cache slot */
uint32_t lineno; /* line number (for ast nodes) */
uint32_t num_args; /* arguments number for EX(This) */
uint32_t fe_pos; /* foreach position */
uint32_t fe_iter_idx; /* foreach iterator index */
uint32_t access_flags; /* class constant access flags */
uint32_t property_guard; /* single property guard */
} u2;
};
#define IS_UNDEF 0
#define IS_NULL 1
#define IS_FALSE 2
#define IS_TRUE 3
#define IS_LONG 4
#define IS_DOUBLE 5
#define IS_STRING 6
#define IS_ARRAY 7
#define IS_OBJECT 8
#define IS_RESOURCE 9
#define IS_REFERENCE 10
…………
typedef union _zend_value {
zend_long lval;
double dval;
zend_refcounted *counted;
zend_string *str;
zend_array *arr;
zend_object *obj;
zend_resource *res;
zend_reference *ref;
zend_ast_ref *ast;
zval *zv;
void *ptr;
zend_class_entry *ce;
zend_function *func;
struct {
uint32_t w1;
uint32_t w2;
} ww;
} zend_value;
//常量
#define IS_CONST (1<p>操作函数命名规则为:ZEND_[opcode]_SPEC_(操作数1类型)_(操作数2类型)_(返回值类型)_HANDLER</p><p>比如赋值语句就有以下多种操作函数:</p><pre class="brush:php;toolbar:false">ZEND_ASSIGN_SPEC_VAR_CONST_RETVAL_UNUSED_HANDLER,
ZEND_ASSIGN_SPEC_VAR_TMP_RETVAL_UNUSED_HANDLER,
ZEND_ASSIGN_SPEC_VAR_VAR_RETVAL_UNUSED_HANDLER,
ZEND_ASSIGN_SPEC_VAR_CV_RETVAL_UNUSED_HANDLER,
…
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_CV_CONST_RETVAL_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *value;
zval *variable_ptr;
SAVE_OPLINE();
//获取op2对应的值,也就是1
value = EX_CONSTANT(opline->op2);
//在execute_data中获取op1的位置,也就是$a(execute_data类似函数栈帧,后面详细分析)
variable_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
//赋值
value = zend_assign_to_variable(variable_ptr, value, IS_CONST);
if (UNEXPECTED(0)) {
ZVAL_COPY(EX_VAR(opline->result.var), value);
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
struct _zend_op_array {
…………
//last表示指令总数;opcodes为存储指令的数组;
uint32_t last;
zend_op *opcodes;
//变量类型为IS_CV的个数
int last_var;
//变量类型为IS_VAR和IS_TEMP_VAR的个数
uint32_t T;
//存放IS_CV类型变量的数组
zend_string **vars;
…………
//静态变量
HashTable *static_variables;
//常量个数;常量数组
int last_literal;
zval *literals;
…
};
struct _zend_vm_stack {
zval *top;
zval *end;
zend_vm_stack prev;
};
struct _zend_execute_data {
//当前指令指令
const zend_op *opline;
//当前函数执行栈帧
zend_execute_data *call;
//函数返回数据
zval *return_value;
zend_function *func;
zval This; /* this + call_info + num_args */
//调用当前函数的栈帧
zend_execute_data *prev_execute_data;
//符号表
zend_array *symbol_table;
#if ZEND_EX_USE_RUN_TIME_CACHE
void **run_time_cache;
#endif
#if ZEND_EX_USE_LITERALS
//常量数组
zval *literals;
#endif
};
static zend_always_inline zend_execute_data *zend_vm_stack_push_call_frame(uint32_t call_info, zend_function *func, uint32_t num_args, zend_class_entry *called_scope, zend_object *object)
{
//计算当前函数栈帧需要内存空间大小
uint32_t used_stack = zend_vm_calc_used_stack(num_args, func);
//根据栈帧大小分配空间,入栈
return zend_vm_stack_push_call_frame_ex(used_stack, call_info,
func, num_args, called_scope, object);
}
//计算函数栈帧大小
static zend_always_inline uint32_t zend_vm_calc_used_stack(uint32_t num_args, zend_function *func)
{
//_zend_execute_data大小(80字节/16字节=5)+参数数目
uint32_t used_stack = ZEND_CALL_FRAME_SLOT + num_args;
if (EXPECTED(ZEND_USER_CODE(func->type))) {
//当前函数临时变量等数目
used_stack += func->op_array.last_var + func->op_array.T - MIN(func->op_array.num_args, num_args);
}
//乘以16字节
return used_stack * sizeof(zval);
}
//入栈
static zend_always_inline zend_execute_data *zend_vm_stack_push_call_frame_ex(uint32_t used_stack, uint32_t call_info, zend_function *func, uint32_t num_args, zend_class_entry *called_scope, zend_object *object)
{
//上一个函数栈帧地址
zend_execute_data *call = (zend_execute_data*)EG(vm_stack_top);
//移动函数调用栈top指针
EG(vm_stack_top) = (zval*)((char*)call + used_stack);
//初始化当前函数栈帧
zend_vm_init_call_frame(call, call_info, func, num_args, called_scope, object);
//返回当前函数栈帧首地址
return call;
}
以上がPHP7 ソースコード: PHP 仮想マシンの詳細な分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

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

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

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

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

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

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

ホットトピック











PHP and Python each have their own advantages, and the choice should be based on project requirements. 1.PHPは、シンプルな構文と高い実行効率を備えたWeb開発に適しています。 2。Pythonは、簡潔な構文とリッチライブラリを備えたデータサイエンスと機械学習に適しています。

PHPは、サーバー側で広く使用されているスクリプト言語で、特にWeb開発に適しています。 1.PHPは、HTMLを埋め込み、HTTP要求と応答を処理し、さまざまなデータベースをサポートできます。 2.PHPは、ダイナミックWebコンテンツ、プロセスフォームデータ、アクセスデータベースなどを生成するために使用され、強力なコミュニティサポートとオープンソースリソースを備えています。 3。PHPは解釈された言語であり、実行プロセスには語彙分析、文法分析、編集、実行が含まれます。 4.PHPは、ユーザー登録システムなどの高度なアプリケーションについてMySQLと組み合わせることができます。 5。PHPをデバッグするときは、error_reporting()やvar_dump()などの関数を使用できます。 6. PHPコードを最適化して、キャッシュメカニズムを使用し、データベースクエリを最適化し、組み込み関数を使用します。 7

PHPとPythonにはそれぞれ独自の利点があり、プロジェクトの要件に従って選択します。 1.PHPは、特にWebサイトの迅速な開発とメンテナンスに適しています。 2。Pythonは、データサイエンス、機械学習、人工知能に適しており、簡潔な構文を備えており、初心者に適しています。

PHPは、電子商取引、コンテンツ管理システム、API開発で広く使用されています。 1)eコマース:ショッピングカート機能と支払い処理に使用。 2)コンテンツ管理システム:動的コンテンツの生成とユーザー管理に使用されます。 3)API開発:RESTFUL API開発とAPIセキュリティに使用されます。パフォーマンスの最適化とベストプラクティスを通じて、PHPアプリケーションの効率と保守性が向上します。

PHPは依然として動的であり、現代のプログラミングの分野で重要な位置を占めています。 1)PHPのシンプルさと強力なコミュニティサポートにより、Web開発で広く使用されています。 2)その柔軟性と安定性により、Webフォーム、データベース操作、ファイル処理の処理において顕著になります。 3)PHPは、初心者や経験豊富な開発者に適した、常に進化し、最適化しています。

PHPは主に手順プログラミングですが、オブジェクト指向プログラミング(OOP)もサポートしています。 Pythonは、OOP、機能、手続き上のプログラミングなど、さまざまなパラダイムをサポートしています。 PHPはWeb開発に適しており、Pythonはデータ分析や機械学習などのさまざまなアプリケーションに適しています。

PHPは、特に迅速な開発や動的なコンテンツの処理に適していますが、データサイエンスとエンタープライズレベルのアプリケーションには良くありません。 Pythonと比較して、PHPはWeb開発においてより多くの利点がありますが、データサイエンスの分野ではPythonほど良くありません。 Javaと比較して、PHPはエンタープライズレベルのアプリケーションでより悪化しますが、Web開発により柔軟性があります。 JavaScriptと比較して、PHPはバックエンド開発により簡潔ですが、フロントエンド開発のJavaScriptほど良くありません。

PHPとPythonには独自の利点と短所があり、選択はプロジェクトのニーズと個人的な好みに依存します。 1.PHPは、大規模なWebアプリケーションの迅速な開発とメンテナンスに適しています。 2。Pythonは、データサイエンスと機械学習の分野を支配しています。
