PHP7 소스 코드: PHP 가상 머신의 상세 분석
이 기사의 내용은 PHP7 소스 코드에 관한 것입니다. PHP 가상 머신에 대한 자세한 분석입니다. 필요한 친구들이 참고할 수 있기를 바랍니다.
1. 물리적 기계에서 시작하기
가상 기계도 컴퓨터이며, 그 디자인 아이디어는 물리적 기계와 많은 유사점을 가지고 있습니다. ;
1.1 폰 노이만 아키텍처
폰 노이만은 현재 컴퓨터에 사용되는 디지털 컴퓨터의 아버지입니다. 폰 노이만 아키텍처입니다. 디자인 아이디어에는 주로 다음과 같은 측면이 포함됩니다.
명령어와 데이터는 구별 없이 혼합되어 동일한 메모리에 저장됩니다. 기억 속에. 최신 CPU의 보호 모드에서는 각 메모리 세그먼트에 세그먼트 설명자가 있습니다. 이 설명자는 이 메모리 세그먼트에 대한 액세스 권한(읽기, 쓰기 및 실행 가능)을 기록합니다. 이는 어떤 메모리가 명령어를 저장하는지, 어떤 것이 데이터인지를 지정합니다. 단위의 비트 수는 고정되어 있습니다.
데이터는 이진수로 표시됩니다. opcode는 이 명령어의 연산 유형을 지정하고 피연산자는 피연산자 자체 또는 피연산자의 주소를 지정합니다. 피연산자 자체에는 데이터 유형이 없으며 해당 데이터 유형은 opcode에 의해 결정됩니다. 모든 컴퓨터 아키텍처는 외부 명령어 세트를 제공하여 다양한 컴퓨터 작업을 제어합니다. 명령어 카운터는 실행될 명령어가 위치한 메모리 주소를 나타냅니다. 명령어 카운터는 단 하나이며 일반적으로 순차적으로 증가하지만, 당시의 연산 결과나 외부 조건에 따라 실행 순서가 변경될 수 있습니다. 🎜🎜#
- 1.2 어셈블리 언어 소개; 모든 아키텍처의 컴퓨터는 일련의 명령을 제공합니다.
명령은 연산 유형이며 피연산자는 즉각적일 수 있습니다. 숫자 또는 저장 주소; 각 명령어는 0, 1 또는 2개의 피연산자를 가질 수 있습니다.
명령어는 바이너리 어셈블리 언어의 문자열입니다. -
#🎜 🎜#push, mov, add, pop 등은 연산 코드입니다.
push %ebx mov %eax, [%esp+8] mov %ebx, [%esp+12] add %eax, %ebx pop %ebx
로그인 후 복사%ebx 레지스터 [%esp+12] 메모리 주소
피연산자는 데이터에 액세스할 수 있는 저장 영역일 뿐입니다. 피연산자 자체에는 데이터 유형이 없으며 해당 데이터 유형은 opcode에 의해 결정됩니다. 예를 들어 movb는 바이트를 전송하고, movw는 워드를 전송하고, movl은 더블 워드를 전송하는 등
# 🎜🎜#1.3 함수 호출 스택프로세스(함수)는 코드의 캡슐화이며, 외부에 노출되는 것은 지정된 매개변수 집합과 선택적 반환 값일 뿐입니다. ; 이 함수는 프로그램의 다른 위치에서 호출될 수 있습니다. 이 함수를 구현하려면 프로세스 P가 프로세스 Q를 호출하고 Q가 프로세스 P로 반환된다고 가정합니다.
명령 점프: 프로세스 Q에 들어갈 때 프로그램 카운터는 Q 코드의 시작 주소로 설정되어야 합니다. 프로그램 카운터는 P에서 Q를 호출한 후 지점으로 설정되어야 합니다.
데이터 전송: P는 Q에 하나 이상의 매개변수를 제공할 수 있고, Q는 P에 값을 반환할 수 있습니다. #메모리 할당 및 해제: Q가 실행을 시작할 때 로컬 변수에 대한 메모리 공간을 할당해야 할 수 있으며, 반환하기 전에 이러한 메모리 공간을 해제해야 합니다.#🎜🎜 ##🎜 🎜#
대부분의 언어 프로시저 호출은 아래 그림과 같이 스택 데이터 구조에서 제공하는 메모리 관리 메커니즘을 사용합니다.
#🎜 🎜## 🎜🎜#
함수가 실행되면 자체적인 개인 스택 프레임과 지역 변수가 생깁니다. 스택 프레임에 있는 함수의 전용 스택 프레임에 할당됩니다.우리가 일반적으로 발생하는 스택 오버플로는 호출하는 함수 수준이 너무 깊어서 지속적으로 스택에 푸시되기 때문에 발생합니다.
# 🎜🎜#2.PHP 가상 머신
- 가상 머신은 컴퓨터이기도 합니다. 가상 머신을 설계할 때 먼저 세 가지 요소를 고려해야 합니다. 명령어, 데이터 저장 및 함수 스택 프레임
다음은 PHP 가상 머신의 설계 아이디어를 자세히 분석한 세 가지 항목입니다. #
모든 컴퓨터; 아키텍처는 컴퓨터가 지원하는 일련의 작업 유형을 나타내는 일련의 명령을 외부 세계에 제공해야 합니다. -
PHP 가상 머신은 zend_vm_opcodes에 정의된 186가지 유형의 명령을 외부 세계에 제공합니다. .h 파일
//加、减、乘、除等 #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 지시문 🎜#2.1.2.1 명령 표현
명령 opcode와 피연산자로 구성됩니다. opcode는 이 명령어의 연산 유형을 지정하고 피연산자는 피연산자 자체 또는 연산을 지정합니다.
PHP 가상 머신은 명령어 형식을 정의합니다. 다음과 같이: opcode 피연산자 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类型的,这怎么表示一个操作数呢?(既不是指针不能代表地址,也无法表示所有数据类型);
其实,操作数大多情况采用的相对地址表示方式,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;
2.2 数据存储
PHP虚拟机支持多种数据类型:整型、浮点型、字符串、数组,对象等;PHP虚拟机如何存储和表示多种数据类型?
2.1.2.2节指出结构体_znode_op代表一个操作数;操作数可以是一个偏移量(计算得到一个地址,即zval结构体的首地址),或者一个zval指针;PHP虚拟机使用zval结构体表示和存储多种数据;
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; };
zval.u1.type表示数据类型, zend_types.h文件定义了以下类型:
#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 …………
zend_value存储具体的数据内容,结构体定义如下:
_zend_value占16字节内存;long、double类型会直接存储在结构体;引用、字符串、数组等类型使用指针存储;
代码中根据zval.u1.type字段,判断数据类型,以此决定操作_zend_value结构体哪个字段;
可以看出,字符串使用zend_string表示,数组使用zend_array表示…
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;
如下图为PHP7中字符串结构图:
2.3 再谈指令
2.1.2.1指出,指令使用结构体_zend_op表示;其中最主要2个属性:操作函数,操作数(两个操作数和一个返回值);
操作数的类型(常量、临时变量等)不同,同一个指令对应的handler函数也会不同;操作数类型定义在 Zend/zend_compile.h文件:
//常量 #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, …
对于$a=1,其操作函数为: ZEND_ASSIGN_SPEC_CV_CONST_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(); }
2.4 函数栈帧
2.4.1指令集
上面分析了指令的结构与表示,PHP虚拟机使用_zend_op_array表示指令的集合:
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; … };
注意: 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类型的指针;
struct _zend_vm_stack { zval *top; zval *end; zend_vm_stack prev; };
考虑如何设计函数执行时候的帧结构:当前函数执行时,需要存储函数编译后的指令,需要存储函数内部的局部变量等(2.1.2.2节指出,操作数使用结构体znode_op表示,其内部使用uint32_t表示操作数,此时表示的就是当前zval变量相对于当前函数栈帧首地址的偏移量);
PHP虚拟机使用结构体_zend_execute_data存储当前函数执行所需数据;
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; }
从上面分析可以得到函数栈帧结构图如下所示:
总结
PHP虚拟机也是计算机,有三点是我们需要重点关注的:指令集(包含指令处理函数)、数据存储(zval)、函数栈帧;
此时虚拟机已可以接受指令并执行指令代码;
但是,PHP虚拟机是专用执行PHP代码的,PHP代码如何能转换为PHP虚拟机可以识别的指令呢——编译;
PHP虚拟机同时提供了编译器,可以将PHP代码转换为其可以识别的指令集合;
理论上你可以自定义任何语言,只要实现编译器,能够将你自己的语言转换为PHP可以识别的指令代码,就能被PHP虚拟机执行;
추천 관련 기사:
세션을 데이터베이스에 저장하고 PHP에서 사용하는 방법(코드 포함)
시간 함수 strtotime( in PHP ) 함수의 원리 설명
위 내용은 PHP7 소스 코드: PHP 가상 머신의 상세 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

핫 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
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

PHP와 Python은 각각 고유 한 장점이 있으며 선택은 프로젝트 요구 사항을 기반으로해야합니다. 1.PHP는 간단한 구문과 높은 실행 효율로 웹 개발에 적합합니다. 2. Python은 간결한 구문 및 풍부한 라이브러리를 갖춘 데이터 과학 및 기계 학습에 적합합니다.

PHP는 서버 측에서 널리 사용되는 스크립팅 언어이며 특히 웹 개발에 적합합니다. 1.PHP는 HTML을 포함하고 HTTP 요청 및 응답을 처리 할 수 있으며 다양한 데이터베이스를 지원할 수 있습니다. 2.PHP는 강력한 커뮤니티 지원 및 오픈 소스 리소스를 통해 동적 웹 컨텐츠, 프로세스 양식 데이터, 액세스 데이터베이스 등을 생성하는 데 사용됩니다. 3. PHP는 해석 된 언어이며, 실행 프로세스에는 어휘 분석, 문법 분석, 편집 및 실행이 포함됩니다. 4. PHP는 사용자 등록 시스템과 같은 고급 응용 프로그램을 위해 MySQL과 결합 할 수 있습니다. 5. PHP를 디버깅 할 때 error_reporting () 및 var_dump ()와 같은 함수를 사용할 수 있습니다. 6. 캐싱 메커니즘을 사용하여 PHP 코드를 최적화하고 데이터베이스 쿼리를 최적화하며 내장 기능을 사용하십시오. 7

PHP와 Python은 각각 고유 한 장점이 있으며 프로젝트 요구 사항에 따라 선택합니다. 1.PHP는 웹 개발, 특히 웹 사이트의 빠른 개발 및 유지 보수에 적합합니다. 2. Python은 간결한 구문을 가진 데이터 과학, 기계 학습 및 인공 지능에 적합하며 초보자에게 적합합니다.

PHP는 전자 상거래, 컨텐츠 관리 시스템 및 API 개발에 널리 사용됩니다. 1) 전자 상거래 : 쇼핑 카트 기능 및 지불 처리에 사용됩니다. 2) 컨텐츠 관리 시스템 : 동적 컨텐츠 생성 및 사용자 관리에 사용됩니다. 3) API 개발 : 편안한 API 개발 및 API 보안에 사용됩니다. 성능 최적화 및 모범 사례를 통해 PHP 애플리케이션의 효율성과 유지 보수 성이 향상됩니다.

PHP는 여전히 역동적이며 현대 프로그래밍 분야에서 여전히 중요한 위치를 차지하고 있습니다. 1) PHP의 단순성과 강력한 커뮤니티 지원으로 인해 웹 개발에 널리 사용됩니다. 2) 유연성과 안정성은 웹 양식, 데이터베이스 작업 및 파일 처리를 처리하는 데 탁월합니다. 3) PHP는 지속적으로 발전하고 최적화하며 초보자 및 숙련 된 개발자에게 적합합니다.

PHP는 주로 절차 적 프로그래밍이지만 객체 지향 프로그래밍 (OOP)도 지원합니다. Python은 OOP, 기능 및 절차 프로그래밍을 포함한 다양한 패러다임을 지원합니다. PHP는 웹 개발에 적합하며 Python은 데이터 분석 및 기계 학습과 같은 다양한 응용 프로그램에 적합합니다.

PHP는 특히 빠른 개발 및 동적 컨텐츠를 처리하는 데 웹 개발에 적합하지만 데이터 과학 및 엔터프라이즈 수준의 애플리케이션에는 적합하지 않습니다. Python과 비교할 때 PHP는 웹 개발에 더 많은 장점이 있지만 데이터 과학 분야에서는 Python만큼 좋지 않습니다. Java와 비교할 때 PHP는 엔터프라이즈 레벨 애플리케이션에서 더 나빠지지만 웹 개발에서는 더 유연합니다. JavaScript와 비교할 때 PHP는 백엔드 개발에서 더 간결하지만 프론트 엔드 개발에서는 JavaScript만큼 좋지 않습니다.

PHP와 Python은 고유 한 장점과 단점이 있으며 선택은 프로젝트 요구와 개인 선호도에 달려 있습니다. 1.PHP는 대규모 웹 애플리케이션의 빠른 개발 및 유지 보수에 적합합니다. 2. Python은 데이터 과학 및 기계 학습 분야를 지배합니다.
