Zend虛擬機部分的學習
學習自網址:
http://www.php-internals.com/book/?p=chapt07/07-01-zend-vm-overview
我之前也是一直非常奇怪php是如何把php編譯成機械碼的,2年前看zend虛擬機的文章雲山霧繞,這一次又開始看zend虛擬機是如何實現的,tipi比較老了,但是還是在一些方面很具有借鑑經驗,因爲畢竟php根還在那裏,不可能連根拔起
這一段引自tipi
爲了方便讀者對Zend引擎的實現有個全面的感覺,下面列出涉及到Zend引擎實現的核心代碼文件功能參考。
Zend引擎的核心文件都在$PHP_SRC/Zend/目錄下面。不過最爲核心的文件只有如下幾個:
PHP語法實現
Zend/zend_language_scanner.l
Zend/zend_language_parser.y
Opcode編譯
Zend/zend_compile.c
執行引擎
Zend/zend_vm_*
Zend/zend_execute.c
我們知道計算機其實gcc或者g++只認識c或者c++,c c++代碼又轉化爲彙編最後變爲了二進制機器碼作爲電腦程序執行,那麼他是怎麼把我們的php代碼轉化成c或者c++的呢?
其實自己寫的話真是一個十分複雜的東西,因爲c語言對字符串的處理比較弱。。至少我是這麼認爲的,php我認爲比較舒服的就是他有良好的api對字符串進行處理。
php詞法分析使用的是re2c,
re2c 是一個掃描器。可以很高效的產生代碼
在我們安裝號re2c之後
這個文件我是demo.l
#include <stdio.h>
char *scan(char *p){
#define YYCTYPE char
#define YYCURSOR p
#define YYLIMIT p
#define YYMARKER q
#define YYFILL(n)
/*!re2c
[0-9]+ {return "number";}
[a-z]+ {return "lower";}
[A-Z]+ {return "upper";}
[^] {return "unkown";}
*/
}
int main(int argc, char* argv[])
{
printf("%s\n", scan(argv[1]));
return 0;
}
使用re2c把demo.l轉化爲demo.c
運行結果
/* Generated by re2c 0.13.4 on Sun Jan 12 16:37:54 2020 */
#line 1 "demo.l"
#include <stdio.h>
char *scan(char *p){
#define YYCTYPE char
#define YYCURSOR p
#define YYLIMIT p
#define YYMARKER q
#define YYFILL(n)
#line 13 "demo.c"
{
YYCTYPE yych;
if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2);
yych = *YYCURSOR;
switch (yych) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': goto yy2;
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
case 'G':
case 'H':
case 'I':
case 'J':
case 'K':
case 'L':
case 'M':
case 'N':
case 'O':
case 'P':
case 'Q':
case 'R':
case 'S':
case 'T':
case 'U':
case 'V':
case 'W':
case 'X':
case 'Y':
case 'Z': goto yy6;
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
case 'g':
case 'h':
case 'i':
case 'j':
case 'k':
case 'l':
case 'm':
case 'n':
case 'o':
case 'p':
case 'q':
case 'r':
case 's':
case 't':
case 'u':
case 'v':
case 'w':
case 'x':
case 'y':
case 'z': goto yy4;
default: goto yy8;
}
yy2:
++YYCURSOR;
yych = *YYCURSOR;
goto yy15;
yy3:
#line 10 "demo.l"
{return "number";}
#line 91 "demo.c"
yy4:
++YYCURSOR;
yych = *YYCURSOR;
goto yy13;
yy5:
#line 11 "demo.l"
{return "lower";}
#line 99 "demo.c"
yy6:
++YYCURSOR;
yych = *YYCURSOR;
goto yy11;
yy7:
#line 12 "demo.l"
{return "upper";}
#line 107 "demo.c"
yy8:
++YYCURSOR;
#line 13 "demo.l"
{return "unkown";}
#line 112 "demo.c"
yy10:
++YYCURSOR;
if (YYLIMIT <= YYCURSOR) YYFILL(1);
yych = *YYCURSOR;
yy11:
switch (yych) {
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
case 'G':
case 'H':
case 'I':
case 'J':
case 'K':
case 'L':
case 'M':
case 'N':
case 'O':
case 'P':
case 'Q':
case 'R':
case 'S':
case 'T':
case 'U':
case 'V':
case 'W':
case 'X':
case 'Y':
case 'Z': goto yy10;
default: goto yy7;
}
yy12:
++YYCURSOR;
if (YYLIMIT <= YYCURSOR) YYFILL(1);
yych = *YYCURSOR;
yy13:
switch (yych) {
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
case 'g':
case 'h':
case 'i':
case 'j':
case 'k':
case 'l':
case 'm':
case 'n':
case 'o':
case 'p':
case 'q':
case 'r':
case 's':
case 't':
case 'u':
case 'v':
case 'w':
case 'x':
case 'y':
case 'z': goto yy12;
default: goto yy5;
}
yy14:
++YYCURSOR;
if (YYLIMIT <= YYCURSOR) YYFILL(1);
yych = *YYCURSOR;
yy15:
switch (yych) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': goto yy14;
default: goto yy3;
}
}
#line 14 "demo.l"
}
int main(int argc, char* argv[])
{
printf("%s\n", scan(argv[1]));
return 0;
}
php中zend_language_scanner.l這個就是re2c的規則文件
安裝步驟
autoreconf
./configure
make
sudo make install
php的語法解析文件 zend_language_scanner.l ,我們可以使用下面這個命令來生成
re2c -F -c -o zend_language_scanner2.c zend_language_scanner.l
-F 是flex語法
-c 是條件
-o 是目標文件應該是