PHP扩展-生命周期和内存管理
1. PHP源码结构
PHP的内核子系统有两个,ZE(Zend Engine)和PHP Core.
ZE负责将PHP脚本解析成机器码(也成为token符)后,在进程空间执行这些机器码;ZE还负责内存管理,变量作用域管理和对PHP函数的调度管理。
PHP Core负责和SAPI层的通信;PHP Core也为safe_mode, open_basedir的检查提供了统一控制层;PHP Core还提供了streams层,用于用户域的文件和网络IO操作。其中SAPI(Server Application Programming Interface)通常包含Nginx,Apache,IIS,CLI,CGI等主机环境。
PHP扩展在ZE和PHP Core的基础上提供对各种常用操作的封装,比如对mysql,redis,memcache,sqlite等的读写,对json,xml文件的解析,对soap,sokcet,curl的网络协议的封装,对加密解密压缩解压缩等的封装,对图像处理的封装等等。有些扩展是从零开始实现某个功能,比如按照redis的通信协议使用C来实现和redis的通信;有些扩展则是通过调用系统已有的库,比如图片处理的gb扩展需要系统本身要安装了相应的gd库。
在PHP源码php-5.6.24/ext中提供了78个扩展。
总之,由ZE和PHP Core提供基础的架构,由EXT(扩展)提供用户域的各种操作。
以php-5.6.24源码为例,ZE对应文件夹php-5.6.24/Zend, PHP Core对应文件夹php-5.6.24/main, 扩展对应文件夹php-5.6.24/ext。
2. PHP扩展的生命周期
PHP在接收到SAPI命令时,首先初始化并启动它的内核子系统,在内核子系统的启动快结束时,PHP开始加载它的扩展代码并对扩展初始化,此时PHP将调用每个模块的初始化例程Module Initialization routine (MINIT)。
MINIT(Module Initialization)
PHP调用MINIT相关例程,使得每个扩展有机会初始化内部变量、分配资源、注册资源处理句柄,以及向ZE注册自己的函数,以便于脚本调用这其中的函数时候ZE知道执行哪些代码
RINIT(Request Initialization)
在模块初始化完成后,PHP等待来自SAPI的请求,当接收到SAPI请求后,由ZE为当前被请求的php脚本创建运行环境,并调用每个扩展的Request Initialization(RINIT)函数,使得每个扩展有机会设定特定的环境变量,根据请求分配资源,或者执行其他任务,如审核。
这里所说的SAPI请求分为两类,一类是Apache, IIS, 和其他成熟的web server SAPIs,他们在启动时PHP先执行了MINIT,之后等待来自用户的页面请求,当收到请求后执行RINIT;另一类SAPI请求则是CGI or CLI SAPIs,PHP在收到这类SAPI请求时,执行完MINIT马上就执行RINIT。
当RINIT请求初始化完毕后,ZE接回控制权并将当前被请求的脚本翻译成tokens, 最终构成opcodes(操作码),opcodes被执行过程中,如果某个opcode要求执行某个扩展函数,这是ZE就会将相关参数绑定到改函数,并将控制权临时交给该函数去执行,直到该函数执行完毕。
RSHUTDOWN(Request Shutdown)
PHP脚本运行结束后,PHP调用每个扩展的请求关闭(RSHUTDOWN)函数以执行最后的清理工作(如将session变量存入磁盘)。接下来,ZE执行清理过程(垃圾收集),有效地对之前的请求期间用到的每个变量执行unset()。
MSHUTDOWN(Module Shutdown)
当RSHUTDOWN完成后,PHP继续等待SAPI的其他文档请求或者是关闭信号。对于CGI和CLI等SAPI,没有“下一个请求”,所以SAPI立刻开始关闭。关闭期间,PHP再次遍历每个扩展,调用其模块关闭(MSHUTDOWN)函数,并最终关闭自己的内核子系统。
GINIT
初始化全局变量
GSHUTDOWN
释放全局变量
MINFO
设置phpinfo模块的信息,phpinfo要等级每个扩展的配置信息
// main/php.h #define PHP_MINIT ZEND_MODULE_STARTUP_N #define PHP_MSHUTDOWN ZEND_MODULE_SHUTDOWN_N #define PHP_RINIT ZEND_MODULE_ACTIVATE_N #define PHP_RSHUTDOWN ZEND_MODULE_DEACTIVATE_N #define PHP_MINFO ZEND_MODULE_INFO_N #define PHP_GINIT ZEND_GINIT #define PHP_GSHUTDOWN ZEND_GSHUTDOWN #define PHP_MINIT_FUNCTION ZEND_MODULE_STARTUP_D #define PHP_MSHUTDOWN_FUNCTION ZEND_MODULE_SHUTDOWN_D #define PHP_RINIT_FUNCTION ZEND_MODULE_ACTIVATE_D #define PHP_RSHUTDOWN_FUNCTION ZEND_MODULE_DEACTIVATE_D #define PHP_MINFO_FUNCTION ZEND_MODULE_INFO_D #define PHP_GINIT_FUNCTION ZEND_GINIT_FUNCTION #define PHP_GSHUTDOWN_FUNCTION ZEND_GSHUTDOWN_FUNCTION
3. PHP扩展的内存管理
ZE在执行自己内部的内存管理时,通过附加的标志来标识某某内存变量是否是持久性的,对于非持久内存,ZE会去清理。但在扩展内部最好还是自己去清理非持久内存,因为扩展自己请求分配的非持久内存,将在长时间内保持为未回收状态,这样与之相关的资源长时间得不到释放。