深入剖析PHP7内核源码(一)- PHP架构与生命周期
PHP7 为什么这么快?
- 全新的zval 更节约的空间,栈上分配内存
- zend_string 存储字符串的Hash值,数组查询的时候不需要进行Hash计算
- 在HashTable桶内直接存数据,减少了内存的申请次数,提升了cache命中率和内存访问速度
- zend_parse_parameters改为了宏实现,性能提升5%
- 增加opcode指令 call_user_function,is_init/string/array,strlen,defined函数变成opcode指令,速度更快
- 排序算法的改进
PHP7 架构

- Zend 引擎:Zend引擎为PHP提供了基础服务,包括词法分析 语法分析 ,AST抽象语法树编译 opcodes执行,PHP的变量设计、内存管理、进程管理。
- PHP层:绑定了SAPI层并处理与它的通信,它同时对safe_mode和open_basedir的检测提供一致的控制层,将fopen()、fread()和fwrite()等用户空间的函数与文件和网络I/O联系起来。
- SAPI:包括了cli fpm等,把接口对外接口都抽象出来,只要遵守SAPI协议便可以实现一个server。
- 拓展:zend 引擎提供了核心能力和接口规范,在此基础上可以开发拓展
这里的拓展分为了两种,通常在php.ini中,通过extension=加载的扩展我们称为PHP扩展,通过zend_extension=加载的扩展我们称为Zend扩展,但从源码的角度来讲,PHP扩展应该称为“模块”(源码中以module命名),而Zend扩展称为“扩展”(源码中以extension命名)。两者最大的区别在于向引擎注册的钩子,向用户层面提供一些C实现的PHP函数,需要用到zend_module_entry(即作为PHP扩展),而需要hook到Zend引擎的话,就得用到zend_extension(即作为Zend扩展)。
PHP7执行流程

- 词法分析,把源代码切割成多个字符串单元(Token)
- 语法分析器把Token转换成AST抽象语法树
- 抽象语法树转换成opcodes(opcode指令集合)
- 虚拟机解释执行执行opcodes(opcode是一组指令标识,对应handler处理函数)
执行实例
词法分析
<?php
echo "Hello world";
切割成了4部分
<?php => #define T_OPEN_TAG 379
echo => #define T_ECHO 328
空格 => #define T_WHITESPACE 382
"hello world" => #define T_CONSTANT_ENCAPSED_STRING 323
语法分析
单独存在的词块不能完整表达语义,还需要语法分析器,它会检查语法,匹配Token,对Token进行关联,组织串联后的产物就是AST.AST 分为多种类型,对应PHP语法,比如赋值语句,生成的抽象语法树节点是ZEND_AST_ASSIGN,赋值语句的左右会被作为ZEND_AST_ASSIGN类型节点的孩子(AST是PHP7才加入的,解耦了编译器和解释器).
opcodes
opcode是PHP执行过程中的中间代码,生成后由虚拟机执行,生成的opcode是类似下面的样子
line op
1 ECHO
2 RETURN
源码中对应的opcode及handler
ZEND_ECHO // handler:ZEND_ECHO_SPEC_CONST_HANDLER 实现的功能是输出"hello world"
ZEND_RETURN // handler:ZEND_RETURN_SPEC_CONST_HANDLER
PHP 生命周期
CLI生命周期

- php_module_startup:注册全局变量GPC等,加载内部拓展和外部拓展。
- php_request_startup:重置垃圾回收器,初始化执行器,初始化扫描器,设置超时时间等。
- php_execute_script
=> compile_file
=> open_file_for_scanning(读取PHP代码内容,并使词法分析指针指向第一个位置)
=> zendparse(词法分析语法分析后生成AST) => init_op_array(初始化op_array)
=> zend_compile_top_stmt(把AST转为op_array)
=> pass_two(设置op_array对应的zend虚拟机handler)
=> 生成op_array
=> zend_execute(zend虚拟机中执行op_array)
- php_request_shutdown:调用所有关闭函数,调用所有析构函数,输出缓冲区内容,重置最大执行时间,关闭输出层(HTTP头等),释放所有request的全局变量
- php_module_shutdown:调用module对应的flush函数,清理持久化的符号表,销毁全局变量,关闭所有拓展,关闭内存管理,关闭输出output,析构垃圾回收
FPM模式的生命周期

- FPM跟CLI模式不同的是,FPM是常驻内存的,所以php_module_startup只在启动进程的时候做一次初始化,对应的php_module_shutdown也只做一次。
- 进入循环,调用fcgi_accept_request(accept) 阻塞等待,如果请求进来,则进入php_request_startup,初始化请求,同时加了锁来防止惊群效应
fcgi.c
...
FCGI_LOCK(req->listen_socket);
req->fd = accept(listen_socket, (struct sockaddr *)&sa, &len);
FCGI_UNLOCK(req->listen_socket);
引用
- PHP7的性能优化总结 https://blog.csdn.net/eebaicai/article/details/83629547
- PHP扩展与Zend扩展区别 http://yangxikun.github.io/php/2016/07/10/php-zend-extension.html
- 《PHP7 底层设计与源码实现》 陈雷等
深入剖析PHP7内核源码(一)- PHP架构与生命周期的更多相关文章
- 深入剖析PHP7内核源码(二)- PHP变量容器
简介 PHP的变量使用起来非常方便,其基本结构是底层实现的zval,PHP7采用了全新的zval,由此带来了非常大的性能提升,本文重点分析PHP7的zval的改变. PHP5时代的ZVAL typed ...
- 全方位深度剖析PHP7底层源码(已完结)
第1章 课程介绍本章主要介绍课程要讲的知识点,以及课程要求等. 第2章 PHP7的新特性本章主要介绍PHP7的新特性,做基准测试,与PHP5对比验证PHP7的性能提升程度,引出对PHP7源码学习的必要 ...
- 你还不知道Vue的生命周期吗?带你从Vue源码了解Vue2.x的生命周期(初始化阶段)
作者:小土豆biubiubiu 博客园:https://www.cnblogs.com/HouJiao/ 掘金:https://juejin.im/user/58c61b4361ff4b005d9e8 ...
- Tomcat源码分析 (三)----- 生命周期机制 Lifecycle
Tomcat里面有各种各样的组件,每个组件各司其职,组件之间又相互协作共同完成web服务器这样的工程.在这些组件之上,Lifecycle(生命周期机制)至关重要!在学习各个组件之前,我们需要看看Lif ...
- Spark Streaming源码解读之生成全生命周期彻底研究与思考
本期内容 : DStream与RDD关系彻底研究 Streaming中RDD的生成彻底研究 问题的提出 : 1. RDD是怎么生成的,依靠什么生成 2.执行时是否与Spark Core上的RDD执行有 ...
- MyBatis源码解析【3】生命周期
经过之前的项目构建,我们已经得到了一个可以使用的最基本的项目. 其中已经包括整个执行的过程.但是我们在完成之后也遇到了很多问题,我们就要慢慢的一步步解决这些问题. 讲道理,今天我们其实应该直接开始看源 ...
- 初探Spring源码之Spring Bean的生命周期
写在前面的话: 学无止境,写博客纯粹是一种乐趣而已,把自己理解的东西分享出去,不意味全是对的,欢迎指正! Spring 容器初始化过程做了什么? AnnotationConfigApplication ...
- Vue源码学习(二)——生命周期
官网对生命周期给出了一个比较完成的流程图,如下所示: 从图中我们可以看到我们的Vue创建的过程要经过以下的钩子函数: beforeCreate => created => beforeMo ...
- Vue.js 源码分析(九) 基础篇 生命周期详解
先来看看官网的介绍: 主要有八个生命周期,分别是: beforeCreate.created.beforeMount.mounted.beforeupdate.updated .beforeDes ...
随机推荐
- ~~Py2&Py3~~
进击のpython python2 整型 int -- long(长整型) /获取的是整数 python3 整型 int /获取的是浮点数(小数) python2 print(range(1,10)) ...
- HTML连载23-属性选择器(上)
一.属性选择器 1. (1)定义:根据指定的 属性名称找到对应的标签,然后设置属性 (2)格式:标签[属性=值]:{属性:值:] 注意:前一个值是不带引号的 (3)例子: <style> ...
- linux server 发送邮件
用linux服务器发送邮件centos1.安装mailx 和sendmail,系统一般会安装的yum -y isntall mailx sendmail 2.修改/etc/mail.rcset fro ...
- 网页判断浏览器是否安装flash
<script> //检验flash版本 以及falsh是否安装 function detectFlash() { try { if(navigator.mimeTypes.length& ...
- nginx配置目录访问&用户名密码控制
背景 项目上需要一些共享目录让外地同事可以网页访问对应的文件,且受权限控制: 现有环境: centos nginx 你可以了解到以下内容: 配置nginx开启目录访问 并配置nginx用户名和密码进行 ...
- JAVA开发异常处理十大秘诀
1.前提 第一层:遇到异常首先必须告诉自己,冷静,不要慌.(一看到Bug就心慌,那么武功就施展不了了) 2.入门级 第二层:遇到Bug,第一潜意识看输出异常的信息的(控制台输出,Junit输出,页面输 ...
- HashSet源码解读
一:先看其实现了哪些接口和继承了哪些类 1.实现了Serializable接口,表明它支持序列化. 2.实现了Cloneable接口,表明它支持克隆,可以调用超类的clone()方法进行浅拷贝. 3. ...
- 逆向MFC程序
目录 @ 1 MFC执行流程 1.1 环境支持 1.2 分析 1.3 实践探索 1.3.1 创建一个MFC程序 1.3.2 下关键断点并调试 1.4 转向MFC库源文件中观测 2 逆向 2.1 特征码 ...
- rabbitMQ_Publish/Subscribe(三)
发布/订阅 生产者发布信息,多个订阅者可以同时接收到信息. 转发器 现在是时候在RabbitMQ中引入完整的消息传递模式了. 让我们快速了解我们在以前的教程中介绍的内容: 生产者是一个发送消息的应用程 ...
- Python实现批量处理扫描特定目录
## 简述在渗透测试中遇到相同CMS站点时,搞定一个站点,相当于拿了一个站群的通用漏洞,所以我们首先需要标注站点的CMS类型,根据要求编写如下脚本 ## 要求1.访问特定目录,如:站点特定 /cmsa ...