Jadual Kandungan
前言" >前言
跟踪运行轨迹" >跟踪运行轨迹
词法语法分析->opcode" >词法语法分析->opcode
虚拟机执行opcode" >虚拟机执行opcode
结语" >结语
Rumah pembangunan bahagian belakang tutorial php PHP-Zend发动机剖析之Hello World(二)

PHP-Zend发动机剖析之Hello World(二)

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())把脚本文件编译出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; }
;
Salin selepas log masuk

语法分析从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这个结构,于是跟踪发现这个就是最后每条语句对应的opcode结构了!!!!

opcode的结构跟汇编有很大的相似之处,一个操作符,两个操作数。

在Zend引擎中,每个opcode主要的东西就是那个handler,一会我们会看到Zend里边是怎么生成这个handler的。到了这里先Hold住一下,回过头,我们看一下Hello World这个例子生成的opcode是什么。

装上vld,然后运行:php -dvld.active=1 HelloWorld.php,我们就可以看到这个PHP文件编译出来的opcode列表了:

可以看到echo这个语句的opcode类型是ECHO,同时return没有返回值,只有一个操作数"Hello World"。

现在经过了语法分析,我们对每条语句都编译出了opcode,Zend就会把它放入一个op_array里边(其实就是一个opcode的列表)。

回过头我们看一下zend_do_echo做了什么事情:

首先通过get_next_op在当前的op_array的最后边生成一条opcode,然后设置其opcode类型是ZEND_ECHO,然后设置它的第一个参数op1,同时标记第二个参数op2是不需要使用的(unused的)。

经过了这么多步骤之后我们得到了一个op_array的列表,这个列表里边的每一条opcode都绑定了自己的类型,接着我们看一下每个opcode节点是如何绑定handler的。

zend_vm_def.h定义了ZEND_ECHO的handler,留意到这里的40,一会需要用到,因为echo的参数可以有几种:常量,变量等等,所以对应着不同的handler

在zend_vm_execute.h定义了opcode对应的所有的handler,我们只关注echo相关的handler,注意到其中的代码:


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 };


请花费短暂的时间先记住这里的labels以及行数。

发现了获取handler的方法最后边return语句的计算,根据前面说的echo的opcode是40(假设两个参数op1,op2的type都是0),于是乎其对应的handler就是:

zend_opcode_handlers[40*25+0*5+0*5] =?zend_opcode_handlers[1000] =?labels[1000] =?ZEND_ECHO_SPEC_CONST_HANDLER(怎么来的?因为:41914行-40913行-1=1000)。

虚拟机执行opcode

前边我们已经解释了zend_compile_file把一个脚本编译成一个opcode的list:


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


在这之后,Zend引擎用zend_execute执行返回的opcode。

我们定位到了zend_execute最后执行到Zend/zend_vm_execute.h的337行:

可以看到,虚拟机执行的时候会循环当前的opcode列表,然后调用每一行opcode的handler,根据handler返回值确定下一步做啥(例如函数调用等,以后再展开)。

在这篇文章中我们只关注跟Hello World相关的东西,我们前边知道echo的handler是ZEND_ECHO_SPEC_CONST_HANDLER,通过最后的定位你会发现其调用了:


zend_write = (zend_write_func_t) utility_functions->write_function;


这里的utility_functions里边包含了一些基础的handler,每个sapi接入层自己修改了这里的基础函数指针,例如在命令行模式下,最后调用到了

sapi_cli_single_write:

从源码中,我们看到了最后的写操作就是调用了write/fwrite写入到标准输出流(也即是终端屏幕上)。

结语

最后根据前边的过程,再展开一下流程图就是:

Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn

Alat AI Hot

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Clothoff.io

Clothoff.io

Penyingkiran pakaian AI

AI Hentai Generator

AI Hentai Generator

Menjana ai hentai secara percuma.

Artikel Panas

R.E.P.O. Kristal tenaga dijelaskan dan apa yang mereka lakukan (kristal kuning)
2 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
Repo: Cara menghidupkan semula rakan sepasukan
4 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island Adventure: Cara mendapatkan biji gergasi
3 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌

Alat panas

Notepad++7.3.1

Notepad++7.3.1

Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina

SublimeText3 versi Cina

Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1

Hantar Studio 13.0.1

Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6

Dreamweaver CS6

Alat pembangunan web visual

SublimeText3 versi Mac

SublimeText3 versi Mac

Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Lima projek sumber terbuka bahasa Go yang dipilih untuk membawa anda meneroka dunia teknologi Lima projek sumber terbuka bahasa Go yang dipilih untuk membawa anda meneroka dunia teknologi Jan 30, 2024 am 09:08 AM

Dalam era perkembangan teknologi yang pesat hari ini, bahasa pengaturcaraan bermunculan seperti cendawan selepas hujan. Salah satu bahasa yang telah menarik perhatian ramai ialah bahasa Go, yang digemari oleh ramai pembangun kerana kesederhanaan, kecekapan, keselamatan serentak dan ciri-ciri lain. Bahasa Go terkenal dengan ekosistemnya yang kukuh dengan banyak projek sumber terbuka yang sangat baik. Artikel ini akan memperkenalkan lima projek sumber terbuka bahasa Go yang dipilih dan membawa pembaca untuk meneroka dunia projek sumber terbuka bahasa Go. KubernetesKubernetes ialah enjin orkestrasi kontena sumber terbuka untuk automatik

Keperluan pembangunan bahasa Go: 5 cadangan rangka kerja yang popular Keperluan pembangunan bahasa Go: 5 cadangan rangka kerja yang popular Mar 24, 2024 pm 01:15 PM

"Go Language Development Essentials: 5 Syor Rangka Kerja Popular" Sebagai bahasa pengaturcaraan yang pantas dan cekap, bahasa Go digemari oleh semakin ramai pembangun. Untuk meningkatkan kecekapan pembangunan dan mengoptimumkan struktur kod, ramai pembangun memilih untuk menggunakan rangka kerja untuk membina aplikasi dengan cepat. Dalam dunia bahasa Go, terdapat banyak rangka kerja yang sangat baik untuk dipilih. Artikel ini akan memperkenalkan 5 rangka kerja bahasa Go yang popular dan menyediakan contoh kod khusus untuk membantu pembaca memahami dan menggunakan rangka kerja ini dengan lebih baik. 1.GinGin ialah rangka kerja web yang ringan dengan pantas

Cara menggunakan ACL (Senarai Kawalan Akses) untuk kawalan kebenaran dalam Rangka Kerja Zend Cara menggunakan ACL (Senarai Kawalan Akses) untuk kawalan kebenaran dalam Rangka Kerja Zend Jul 29, 2023 am 09:24 AM

Cara menggunakan ACL (AccessControlList) untuk kawalan kebenaran dalam Rangka Kerja Zend Pengenalan: Dalam aplikasi web, kawalan kebenaran ialah fungsi penting. Ia memastikan bahawa pengguna hanya boleh mengakses halaman dan ciri yang dibenarkan untuk mereka akses dan menghalang akses yang tidak dibenarkan. Rangka kerja Zend menyediakan cara yang mudah untuk melaksanakan kawalan kebenaran, menggunakan komponen ACL (AccessControlList). Artikel ini akan memperkenalkan cara menggunakan ACL dalam Rangka Kerja Zend

Rangka Kerja Pelaksanaan PHP: Tutorial Permulaan Rangka Kerja Zend Rangka Kerja Pelaksanaan PHP: Tutorial Permulaan Rangka Kerja Zend Jun 19, 2023 am 08:09 AM

Rangka kerja pelaksanaan PHP: Tutorial pengenalan ZendFramework ZendFramework ialah rangka kerja tapak web sumber terbuka yang dibangunkan oleh PHP dan kini diselenggara oleh ZendTechnologies menggunakan corak reka bentuk MVC dan menyediakan satu siri perpustakaan kod boleh guna semula untuk melaksanakan aplikasi Web2.0 dan Web Serve. ZendFramework sangat popular dan dihormati oleh pembangun PHP dan mempunyai pelbagai jenis

Melaksanakan penjadualan tugas teragih menggunakan rangka kerja web Golang rangka kerja Echo Melaksanakan penjadualan tugas teragih menggunakan rangka kerja web Golang rangka kerja Echo Jun 24, 2023 am 11:49 AM

Dengan perkembangan Internet dan kemajuan teknologi maklumat, era data besar telah tiba, dan bidang seperti analisis data dan pembelajaran mesin juga telah digunakan secara meluas. Dalam bidang ini, penjadualan tugas adalah masalah yang tidak dapat dielakkan. Cara untuk mencapai penjadualan tugas yang cekap adalah penting untuk meningkatkan kecekapan. Dalam artikel ini, kami akan memperkenalkan cara menggunakan rangka kerja web Golang rangka kerja Echo untuk melaksanakan penjadualan tugas teragih. 1. Pengenalan kepada rangka kerja Echo Echo ialah rangka kerja GoWeb berprestasi tinggi, berskala dan ringan. Ia berdasarkan HTTP

Pembangunan Laravel: Bagaimana untuk melaksanakan komunikasi WebSockets menggunakan Laravel Echo dan Pusher? Pembangunan Laravel: Bagaimana untuk melaksanakan komunikasi WebSockets menggunakan Laravel Echo dan Pusher? Jun 13, 2023 pm 05:01 PM

Laravel ialah rangka kerja PHP popular yang sangat berskala dan cekap Ia menyediakan banyak alat dan perpustakaan yang berkuasa yang membolehkan pembangun membina aplikasi web berkualiti tinggi dengan cepat. Antaranya, LaravelEcho dan Pusher ialah dua alat yang sangat penting di mana komunikasi WebSockets boleh dilaksanakan dengan mudah Artikel ini akan memperincikan cara menggunakan kedua-dua alat ini dalam aplikasi Laravel. Apakah itu WebSockets? WebSockets

Penjelasan terperinci tentang peranan dan penggunaan kata kunci gema dalam PHP Penjelasan terperinci tentang peranan dan penggunaan kata kunci gema dalam PHP Jun 28, 2023 pm 08:12 PM

Penjelasan terperinci tentang peranan dan penggunaan kata kunci gema dalam PHP PHP ialah bahasa skrip bahagian pelayan yang digunakan secara meluas, yang digunakan secara meluas dalam pembangunan web. Kata kunci gema ialah kaedah yang digunakan untuk mengeluarkan kandungan dalam PHP. Artikel ini akan memperkenalkan secara terperinci fungsi dan penggunaan kata kunci gema. Fungsi: Fungsi utama kata kunci gema adalah untuk mengeluarkan kandungan ke penyemak imbas. Dalam pembangunan web, kita perlu mempersembahkan data secara dinamik ke halaman hadapan Pada masa ini, kita boleh menggunakan kata kunci gema untuk mengeluarkan data ke halaman. e

Terokai rangka kerja bahasa Go: 5 pilihan yang tidak boleh dilepaskan! Terokai rangka kerja bahasa Go: 5 pilihan yang tidak boleh dilepaskan! Feb 19, 2024 pm 02:29 PM

Sebagai bahasa pengaturcaraan yang pantas dan cekap, bahasa Go sentiasa digemari oleh pengaturcara. Dalam ekosistem bahasa Go, rangka kerja memainkan peranan penting dalam membantu pembangun membina aplikasi dengan lebih pantas. Artikel ini akan memperkenalkan lima rangka kerja bahasa Go untuk membolehkan anda memahami ciri dan penggunaannya. 1. Rangka kerja Gin Rangka kerja Gin ialah rangka kerja Web yang ringan dengan ciri prestasi yang pantas dan tinggi. Gunakan rangka kerja Gin untuk membina API RESTful dan aplikasi web dengan cepat. Berikut ialah contoh kod mudah:

See all articles