PHP7 언어의 실행 원리(PHP7 소스 코드 분석)

藏色散人
풀어 주다: 2023-04-05 16:56:02
앞으로
4371명이 탐색했습니다.

PHP7 언어의 실행 원리(PHP7 소스 코드 분석)

우리가 일반적으로 사용하는 고급 언어는 많지만 가장 유명한 언어로는 CC++, Python, PHP, Go, Pascal 등이 있습니다. 이러한 언어는 실행 방식에 따라 크게 컴파일 언어와 해석 언어의 두 가지 유형으로 나눌 수 있습니다.

그 중에는 컴파일된 언어로는 CC++, Pascal, Go 등이 있습니다. 여기서 말하는 컴파일이란 응용 프로그램 소스 프로그램이 실행되기 전에 프로그램 소스 코드를 어셈블리 언어로 "번역"한 다음 소프트웨어 및 하드웨어 환경에 따라 추가로 대상 파일로 컴파일하는 것을 의미합니다. 일반적으로 컴파일 작업을 완료하는 도구를 컴파일러라고 부릅니다. 통역된 언어는 프로그램이 실행될 때 기계어로 "번역"됩니다. 그러나 "번역"은 한 번만 수행되므로 실행 효율성이 낮습니다. 인터프리터의 역할은 소스 코드를 해석된 언어로 "번역"하는 프로그램입니다.

컴파일 언어와 해석 언어가 어떻게 작동하는지 더 자세히 논의해 보겠습니다.

1. 컴파일된 언어 및 해석된 언어

우리는 C 언어 코드 조각이 실행 가능한 바이너리 파일이 되기 전에 사전 컴파일, 컴파일, 어셈블링 및 링크되어야 한다는 것을 알고 있습니다. hello.c를 예로 들어보겠습니다.

#include<stdio.h>
int main(){   
    printf("hello world");   
    return 1;
}
로그인 후 복사

이 C 코드에서 main은 프로그램 입력 기능이고 해당 기능은 "hello world"라는 문자열을 화면에 인쇄하는 것입니다. 컴파일 및 실행 프로세스는 그림 1에 나와 있습니다.

PHP7 언어의 실행 원리(PHP7 소스 코드 분석)

그림 1 컴파일된 언어 실행의 도식 다이어그램

1단계: C 언어 코드 전처리(예: 종속성 처리, 매크로 교체 등). 위의 코드 예에서와 같이 #inlcude는 전처리 단계에서 대체됩니다.

2단계: 컴파일합니다. 컴파일러는 C 언어를 어셈블리 언어 프로그램으로 변환합니다. C 언어 조각은 일반적으로 여러 줄의 어셈블리 코드를 나타냅니다. 동시에 컴파일러는 프로그램을 최적화하고 대상 어셈블리 프로그램을 생성합니다.

3단계: 컴파일된 어셈블리 언어는 어셈블러를 통해 대상 프로그램 hello.o로 어셈블됩니다.

4단계: 링크. 프로그램에는 샘플 프로그램의 printf() 함수와 같은 일부 공유 객체 파일이 포함되는 경우가 많습니다. 이 함수는 정적 라이브러리에 있으며 링커(예: Uinx 커넥터 ld)를 통해 연결되어야 합니다.

C 언어로 대표되는 컴파일 언어, 코드 업데이트는 위의 단계를 거쳐야 합니다.

우리는 주로 소스 코드가 CPU 명령어로 컴파일되는 시점을 기준으로 컴파일 언어와 해석 언어를 구분합니다. 타겟 플랫폼. 컴파일된 언어의 경우 컴파일 결과는 이미 해석된 언어에 대한 현재 CPU 시스템에 대한 명령이므로 먼저 중간 코드로 컴파일한 다음 해석된 언어의 특정 가상 머신을 통해 특정 CPU 시스템에 대한 명령으로 변환해야 합니다. 실행. 해석된 언어는 런타임 중에 대상 플랫폼에 대한 지침으로 번역됩니다. 통역 언어는 종종 "느리다"고 말하는데, 이것이 주로 느린 이유입니다.

PHP7에서는 먼저 소스 코드를 어휘 분석한 후 소스 코드를 여러 문자열 단위로 분할합니다. 각각의 독립된 토큰은 완전한 의미를 표현할 수 없으며, 토큰을 추상 구문 트리(AST)로 변환하기 위해 구문 분석 단계를 거쳐야 합니다. 이후 추상 구문 트리는 실행을 위해 기계 명령어로 변환됩니다. PHP에서는 이러한 명령어를 opcode라고 합니다(opcode는 나중에 더 자세히 설명할 것이며 여기 독자들은 이를 CPU 명령어로 생각할 수 있습니다).

AST를 생성하는 단계까지 컴파일된 언어와 해석된 언어가 겪는 과정은 비슷합니다. 차이점은 추상 구문 트리 이후에 시작됩니다.

그림 2는 PHP(특별한 지침이 지정되지 않은 경우 이 장에서 언급된 PHP는 PHP7 버전) 코드가 실행되는 단순화된 단계를 보여줍니다. 마지막 단계의 왼쪽 분기는 컴파일된 언어의 프로세스입니다.

PHP7 언어의 실행 원리(PHP7 소스 코드 분석)

그림 2 PHP를 해석된 언어의 실행 다이어그램의 예로 사용

1단계: 소스 코드의 어휘 분석을 통해 토큰을 얻습니다.

2단계: 추상 구문 트리를 생성합니다. (AST) 구문 분석기 기반 );

3단계: 추상 구문 트리는 Opcode(opcode 명령어 세트)로 변환되고, PHP는 Opcode를 해석하고 실행합니다.

다음으로, 기본 단계를 바탕으로 PHP 언어의 실행 원리를 다듬고 보다 명확한 이해를 확립하도록 노력하겠습니다.

2. PHP7의 실행 원리 개요

우선 위에서 언급한 PHP7 프로그램의 실행 과정을 보완하겠습니다. 그림 3을 참조하세요.

PHP7 언어의 실행 원리(PHP7 소스 코드 분석)

그림 3 PHP7 언어로 작성된 프로그램의 실행 프로세스 다이어그램

1단계: 어휘 분석은 PHP 코드를 의미 있는 식별 토큰으로 변환합니다. 이 단계의 어휘 분석기는 Re2c를 사용하여 구현됩니다.

第2步:语法分析将Token和符合文法规则的代码生成抽象语法树。语法分析器基于Bison实现。语法分析使用了巴科斯范式(BNF)来表达文法规则,Bison借助状态机、状态转移表和压栈、出栈等一系列操作,生成抽象语法树。

第3步:上步的抽象语法树生成对应的opcode,被虚拟机执行。opcode是PHP7定义的一组指令标识,指令对应着相应的handler(处理函数)。当虚拟机调用opcode,会找到opcode背后的处理函数,执行真正的处理。以我们常见的echo语句为例,其对应的opcode便是ZEND_ECHO。

注意:这里为了便于理解词法分析和语法分析过程,将两者分开描述。但实际情况,出于效率考虑,两个过程并非完全独立。

下面,我们通过一段示例代码,来建立PHP7运转的初步理解。

示例代码如下:

<?phpecho "hello world";
로그인 후 복사

从图3可知,这段代码首先会被切割为Token。

1. Token

Token是PHP代码被切割成的有意义的标识。本书介绍的PHP7版本中有137 种Token,在zend_language_parser.h文件中做了定义:

/* Tokens.  */#define END 0#define T_INCLUDE 258#define T_INCLUDE_ONCE 259…#define T_ERROR 392
로그인 후 복사

更多Token的含义,感兴趣的读者可以参考《PHP 7底层设计与源码实现》附录。

PHP提供了token_get_all()函数来获取PHP代码被切割后的Token,可以在深入源码学习前,粗略查看PHP代码被切割后的Token。如下代码片段:

/home/vagrant/php7/bin/php –r &#39;print_r(Token_get_all("<?php echo \"hello world\";"));&#39;
로그인 후 복사

输出结果为:

Array
(
   [0] => Array
       (
           [0] => 379
           [1] => <?php
           [2] => 1
       )
   [1] => Array
       (
           [0] => 328
           [1] => echo
           [2] => 1
       )
   [2] => Array
       (
           [0] => 382
           [1] =>
           [2] => 1
       )
   [3] => Array
       (
           [0] => 323
           [1] => "hello world"
           [2] => 1
       )
   [4] => ;
)
로그인 후 복사

上文输出中,二维数组的每个成员数组第一个值为Token对应的枚举值;第二个值为Token对应的原始字符串内容;第三个值为代码对应的行号。可以看出,词法解析器将

1)文本“

#dfine T_OPEN_TAG 379
로그인 후 복사

不难理解,它是PHP代码的起始tag,也就是

2)echo对应的Token是T_ECHO:

#define T_ECHO 328
로그인 후 복사

3)源码中的空格,对应的Token叫T_WHITESPACE,值为382:

#define T_WHITESPACE 382
로그인 후 복사

4)字符串“hello world”对应的Token值为323:

#define T_CONSTANT_ENCAPSED_STRING 323
로그인 후 복사

可见,Token就是一个个的“词块”,但是单独存在的词块不能表达完整的语义,还需要借助规则进行组织串联。语法分析器就是这个组织者。它会检查语法、匹配Token,对Token进行关联。

PHP7中,组织串联的产物就是抽象语法树(Abstract Syntax Tree,AST)。

2. AST

AST是PHP7版本新特性。在这之前的版本,PHP代码的执行过程中没有生成AST这一步。PHP7对抽象语法树的支持,实现了PHP编译器和解释器解耦,有效提升了可维护性。

顾名思义,抽象语法树具有树状结构。AST的节点分为多种类型,对应着不同的PHP语法。在当前章节,我们可以认为节点类型是对语法规则的抽象,例如赋值语句,生成的抽象语法树节点为ZEND_AST_ASSIGN。而赋值语句的左右操作数,又将作为ZEND_AST_ASSIGN类型节点的孩子。通过这样的节点关系,构建出抽象语法树。

如果读者希望一睹为快,可以直接跳到本书第13章函数的实现,其中图片描绘了一段简单的PHP代码生成的抽象语法树。

在这里,我们推荐读者了解下PhpParser工具,可以用它来查看PHP代码生成的AST。

注意:PHP-Parser是PHP7内核作者之一nikic编写的将PHP源码生成AST的工具。源码见https://github.com/nikic/PHP-...

3. Opcodes

AST扮演了源码到中间代码的临时存储介质的角色,还需要将其转换为opcode,才能被引擎直接执行。Opcode只是单条指令,Opcodes是opcode的集合形式,是PHP执行过程中的中间代码,类似Java中的字节码。生成之后由虚拟机执行。

我们知道,PHP工程优化措施中有个比较常见的“开启Opcache”,指的就是这里的Opcodes的缓存(Opcodes Cache)。通过省去从源码到opcode的阶段,引擎可以直接执行缓存的opcode,以此提升性能。

借助vld插件,可以直观地看到一段PHP代码生成的opcode:

php -dvld.active=1 hello.php
로그인 후 복사

经过过滤整理,对应的opcode为:

line     op              
 1      ECHO            
 2      RETURN
로그인 후 복사

其实在源码实现中,上述代码生成的opcode及handler为:

ZEND_ECHO  // handler: ZEND_ECHO_SPEC_CONST_HANDLERZEND_RETURN  // handler: ZEND_RETURN_SPEC_CONST_HANDLER
로그인 후 복사

可见,ZEND_ECHO对应的handler是ZEND_ECHO_SPEC_CONST_HANDLER。此handler的实现的功能便是预期的“hello world”语句的输出。

相关推荐:《PHP7新特性手册

위 내용은 PHP7 언어의 실행 원리(PHP7 소스 코드 분석)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:imooc.com
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
최신 이슈
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿