资讯专栏INFORMATION COLUMN

【PHP7源码学习】2019-04-25 PHP生命周期浅析

zxhaaa / 1378人阅读

摘要:在此函数的实现种主要有以下几个函数初始化内部字符串哈希表启动的输出激活引擎激活,进行编译器,重置,执行器以及词法扫描器。

Grape

视频传送门:【每日学习记录】使用录像设备记录每天的学习

今天我们来看下PHP的生命周期,我们都知道PHP生命周期有五个步骤,那么在源码层级是怎么去实现PHP生命周期呢?首先,我们抛出本文的几个问题:

php的生命周期是什么?每个阶段做了什么?

为什么会有FPM?

cli执行代码和请求经过fpm执行有什么区别?

       思考ing。。。。

好的,接下来我们解释上边三个问题。

1.什么是php的生命周期?每个阶段做了什么?

这个问题相信大家都能够回答,php的生命周期有五个步骤:

- php_module_startup:模块初始化
- php_request_startup:请求初始化
- php_execute_script:执行脚本
- php_request_shutdown:请求关闭
- php_module_shutdown:模块关闭

在执行完这个个步骤之后,就走过了PHP的一生,感觉设计者完全借鉴了人的一生去设计的生命周期,出生,成长奋斗,结婚生子,完成理想以及老去,妙啊。
那么,对于这五个步骤有什么意义呢?我们来逐个了解一下。我们拿cli来举例子(入口在sapi/cli/php.ini),我们假设sapi的初始化等步骤已经完成,因为本文重点是PHP生命周期,着着重讲解五个步骤。

php_module_startup

看名字就这道这个函数的作用,模块的初始化,即调用每个拓展源码中的的PHP_MINIT_FUNCTION中的方法初始化模块,进行一些模块所需变量的申请,内存分配等。
这一步骤主要完成的工作有以下几点:

- 初始化zend_utility_functions 结构.这个结构是设置zend的函数指针,比如错误处理函数,输出函数,流操作函数等.
- 设置环境变量.
- 加载php.ini配置.
- 加载php内置扩展.
- 写日志.
- 注册php内部函数集.
- 调用 php_ini_register_extensions,加载所有外部扩展
- 开启所有扩展
- 一些清理操作.

我们看一下加载php.ini配置,代码如下:

     /* this will read in php.ini, set up the configuration parameters,
           load zend extensions and register php function extensions
           to be loaded later */
        if (php_init_config() == FAILURE) {
            return FAILURE;
        }//   php_init_config函数会在这里检查所有php.ini配置,并且找到所有加载的模块,添加到php_extension_lists结构中.
    
        /* Register PHP core ini entries */
        REGISTER_INI_ENTRIES();//展开后为zend_register_ini_entries(ini_entries, module_number),ini_entries是PHP_INI_BEGIN/END()两个宏生成的配置映射规则数组,通常会把这个操作放到PHP_MINIT_FUNCTION()中。
        //注意:此时php.ini已经解析到configuration_hash哈希表中,zend_register_ini_entries()将根据配置name查找这个哈希表,
        //如果找到了表明用户在php.ini中配置了该项,
        //然后将调用此规则指定的on_modify函数进行赋值,
        此处更详细的介绍请看[https://www.kancloud.cn/nickbai/php7/363320]
    对于其它的一些操作是怎么实现的,大家可以自行查看源码。
php_request_startup

请求初始化阶段, 即接受到客户端的请求后调用每个拓展的PHP_RINIT_FUNCTION中的方法,初始化PHP脚本的执行环境。
在此函数的实现种主要有以下几个函数:

zend_interned_strings_activate():初始化内部字符串哈希表

php_output_activate():启动php的输出

zend_activate():激活Zend引擎

sapi_activate():激活SAPI,进行编译器,重置gc,执行器以及词法扫描器。

zend_signal_activate(),处理一些信号

zend_activate_modules():回调各扩展定义的request_startup钩子函数。

php_execute_script

执行脚本阶段,入口是php_execute_script()。此过程和2一样,均在do_cli函数内完成。首先获取真正执行的文件信息等,把要执行的文件放在included_files列表里边。然后会调用zend_execute_scripts()去真正执行。真正执行的时候就涉及到了编译,执行,op_array之类的概念。编译过程又涉及到词法分析,语法分析和抽象语法树(AST)等概念。执行的话会涉及到opcode的概念。这些概念在之前的文章中已经讲解过具体实现,感兴趣的读者可以自行前往。传送门:笔记汇总。

php_request_shutdown

请求关闭阶段。在这个阶段总共有16个步骤,在源码里有着明确的注释,无谓就是做一些“清理”操作,我们看下源码怎么做的。

EG(current_execute_data) = NULL;/*EG(current_execute_data) 指向nirvana,因此无法在zend_executor回调函数中安全地访问.*/
php_deactivate_ticks()//清空tick函数
1.php_call_shutdown_functions()//调用注册了register_shutdown_function()的所有可能的shutdown函数
2.zend_call_destructors()//调用所有可能的__destruct() 函数
3.php_output_discard_all()/php_output_end_all()://刷新所有输出缓冲区
4.zend_unset_timeout()//重置max_execution_time(响应发送后不再执行php代码)
5.zend_deactivate_modules()//调用所有扩展RSHUTDOWN函数
6.php_output_deactivate()//关闭输出层(发送设置好的HTTP头文件,清除输出处理程序等)
7.php_free_shutdown_functions()//释放shutdown函数
8.zval_ptr_dtor()//销毁 super-globals
9.php_free_request_globals()//释放request-bound globals
10.zend_deactivate()//关闭扫描仪/执行器/编译器并还原ini条目
11.zend_post_deactivate_modules//调用rshutdown后的所有扩展
12.sapi_deactivate//SAPI相关的shutdown (free stuff)
13.virtual_cwd_deactivate//释放virtual CWD 内存
14.php_shutdown_stream_hashes//破坏流哈希表
15.zend_interned_strings_deactivate()/shutdown_memory_manager():Free Willy (here be crashes)
16.zend_unset_timeout():重置max_execution_time
php_module_shutdown

模块关闭阶段:与模块初始化阶段相反,这个阶段将清理资源、各php模块关闭等操作。具体的代码函数调用不再赘述。

2. 为什么会有FPM?

我们在看过cli下生命周期的五个阶段之后会发现一个问题,这种形式好像有个问题,就是它每来一次请求就会有这五个阶段,这样会造成多大的资源浪费啊。那么为了解决这个问题,FPM应运而生,FPM(FastCGI Process Manager)是 PHP FastCGI 运行模式的一个进程管理器。
概括来说,fpm的实现就是创建一个 master进程,在master进程中创建并监听socket,然后fork 出多个子进程,这些子进程各自accept请求,子进程的处理非常简单,它在启动后阻塞在accept上,有请求到达后开始读取请求数据,读取完成后开始处理然后再返回,在这期间是不会接收其它请求的,也就是说fpm的子进程同时只能响应一个请求,只有把这个请求处理完成后才会accept下一个请求,这一点与nginx的事件驱动有很大的区别nginx的子进程通过epoll管理套接字,如果一个请求数据还未发送完成则会处理下一个请求,即一个进程会同时连接多个请求,它是非阻塞的模型,只处理活跃的套接字。
知道它的工作机制我们就可以想象一下他会如何去改善cli模式下每个请求都完成一次初始化的问题,我们猜测一下,他会在master进程进行一次初始化之后在请求阶段循环,直至结束,这样就达到了不用多次初始化的目的。好的我们看下它是怎么实现的?
首先进行fpm_init,此步主要是对fpm进行初始化,加载fpm配置文件,分配用于和worker进行通信的共享内存,创建worker_pool的套接字,启动 master 的事件管理器(fpm 实现了一个事件管理器用于管理 IO、定时事件,其中 IO 事件通过 kqueue、epoll、poll、select 等管理,定时事件就是定时器,一定时间后触发某个事件)等等操作。
接下来就是fpm_run的过程,master将fork出worker进程,worker进程返回main()中继续向下执行,后面的流程就是worker进程不断accept请求,然后执行PHP脚本并返回。fpm_run整体流程如下:

1. 等待请求:worker进程阻塞在fcgi_accept_request() 等待请求;
2. 解析请求:fastcgi请求到达后被worker接收,然后开始接收并解析请求数据,直到request数据完全到达;
3. 请求初始化:执行php_request_startup(),此阶段会调用每个扩展的:PHP_RINIT_FUNCTION();
4. 编译、执行:由php_execute_script() 完成 PHP 脚本的编译、执行;
5. 关闭请求:请求完成后执行php_request_shutdown(),此阶段会调用每个扩展的:PHP_RSHUTDOWN_FUNCTION(),然后进入步骤 (1) 等待下一个请求;

在这个阶段,master进程将进入fpm_event_loop()来依赖注册的几个事件进行不同的操作。
到此,对于fpm的简单叙述就到此为止了。可以理解fpm的诞生就是一剂灵丹妙药,拉长了PHP的生命战线

3. cli执行代码和请求经过fpm执行有什么区别?

其实我觉得这个问题在看过上边两个问题之后答案就已经出来了~,那么这块就让聪明的你来解决啦。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/110569.html

相关文章

  • ThinkPHP5.1 源码浅析(二)自动加载机制

    摘要:如果遍历后没有找到,则加载失败。在之后碰到了之后直接拿来用,提高系统自动加载的性能。这里我们就讲完了注册自动加载。使用自动加载我们在中定义了我们自动加载函数式方法。 继 生命周期的第二篇,大家尽可放心,不会随便鸽文章的 第一篇中,我们提到了入口脚本,也说了,里面注册了自动加载的功能 本文默认你有自动加载和命名空间的基础。如果没有请 看此篇文章 php 类的自动加载与命名空间 自动加载...

    mudiyouyou 评论0 收藏0
  • 浅析 React 生命周期

    摘要:在使用组件的进行组件实例化时,得到的便是其返回值。也就是说,如果其子组件的或发生改变时,只会取决于那个组件的方法的返回值。文章为本人原创,原文见本人个博浅析生命周期一浅析生命周期二 Overview 最近常有学习React相关的技术,写了几个React的小Demo,使用 React/Express 技术栈。实在太小,羞于拿出来细说。React 的确是一个值得追随的技术。但React体系...

    lansheng228 评论0 收藏0
  • Just for fun——PHP7扩展编写中的宏

    摘要:扩展开发者可以通过实现自定义的功能,通过扩展嵌入到中。就是这个扩展注册的函数数组。 PHP内核架构 showImg(https://segmentfault.com/img/bV2Gzd?w=816&h=772); SAPI是PHP的最上层,它是PHP的应用接口层,对于源码目录为sapi main是PHP的主要代码,主要是输入/输出,Web通信,以及PHP框架的初始化操作,对于源...

    qieangel2013 评论0 收藏0
  • 改造 Android 官方架构组件 ViewModel

    摘要:前言官方架构组件在今年月份大会上被公布直到月份一直都是测试版由于工作比较繁忙期间我只是看过类似的文章但没有在实际项目中使用过更没有看过源码所以对这几个组件的使用很是生疏同时也觉得这几个组件非常高大上非常神秘直到月份官方架构组件正式版发布并且 前言 Android 官方架构组件在今年 5 月份 Google I/O 大会上被公布, 直到 11 月份一直都是测试版, 由于工作比较繁忙, 期...

    DevTTL 评论0 收藏0
  • 改造 Android 官方架构组件 ViewModel

    摘要:原文地址前言官方架构组件在今年月份大会上被公布直到月份一直都是测试版由于工作比较繁忙期间我只是看过类似的文章但没有在实际项目中使用过更没有看过源码所以对这几个组件的使用很是生疏同时也觉得这几个组件非常高大上非常神秘直到月份官方架构组件正式版 原文地址: http://www.jianshu.com/p/963a... 前言 Android 官方架构组件在今年 5 月份 Google I...

    yzzz 评论0 收藏0

发表评论

0条评论

zxhaaa

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<