位置: 编程技术 - 正文

php脚本运行时的超时机制详解(php脚本工作流程)

编辑:rootadmin

推荐整理分享php脚本运行时的超时机制详解(php脚本工作流程),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:php脚本主要用于的三个领域,php脚本主要用于的三个领域,php脚本主要用于的三个领域,php如何运行脚本,php 脚本,php脚本工作流程,php脚本运行时的注意事项,php脚本工作流程,内容如对您有帮助,希望把文章链接给更多的朋友!

在做php开发的时候,经常会设置max_input_time、max_execution_time,用来控制脚本的超时时间。但却从来没有思考过背后的原理。

趁着这两天有空,研究一下这个问题。

超时配置

php的ini配置如何起作用,这是一个老生常谈的话题了。

首先,我们在php.ini里进行配置。当php启动的时候(php_module_startup阶段),会尝试读取ini文件并解析。解析过程简单来说,是分析ini文件,提取出其中合法的键值对,并保存到configuration_hash表。

OK,然后php会进一步调用zend_startup_extensions来启动各个模块(包含php Core模块,以及所有需要加载的扩展)。各个模块的启动函数中,会完成REGISTER_INI_ENTRIES动作。REGISTER_INI_ENTRIES负责将模块对应的一些配置从configuration_hash表取出,然后调用处理函数,最终将处理完的值存入模块的globals变量。

max_input_time、max_execution_time这两个配置属于php Core模块。对于php Core来说,REGISTER_INI_ENTRIES依然发生在php_module_startup中。同样属于php Core模块的配置还有expose_php、display_errors、memory_limit等等...

示意图如下:

上面说到对于不同的配置,REGISTER_INI_ENTRIES会调用不同的函数来处理。我们直接来看max_execution_time对应的函数:

暂时只看上半截,因为我们目前只需关注php的启动阶段,该函数行为很简单,将max_execution_time存入了EG(timeout_seconds)。

至于max_input_time,并没有特殊的处理函数,默认是会将max_input_time存入存入PG(max_input_time)。

因此,当REGISTER_INI_ENTRIES完成,发生的是:

max_execution_time ----> 存入EG(timeout_seconds)

max_input_time ----> 存入PG(max_input_time)

请求超时控制

现在我们搞清楚php的启动阶段发生了什么,继续来看php在实际处理请求的时候,如何管理超时。

在php_request_startup函数中有如下代码:

php_request_startup的时机很讲究。

以cgi为例,只有当php已经从CGI拿到了原始请求以及一些CGI的环境变量之后,php_request_startup才会被调用。上面这段代码实际执行的时候,由于请求已经拿到,所以SG(request_info)处于准备就绪状态,但是php中的$_GET,$_POST,$_FILE等超全局变量尚未生成。

从代码上理解:

1、如果用户将max_input_time配做-1,或没有配置,那么脚本的生命周期就只受EG(timeout_seconds)约束。

2、否则,请求启动阶段的超时控制,受PG(max_input_time)约束。

3、zend_set_timeout函数负责设置定时器。一旦指定时间过去,定时器会通知php进程。zend_set_timeout下文会具体分析。

php_request_startup完成,则进入php的实际执行阶段,即php_execute_script。在php_execute_script中可以看到:

OK,假如代码执行到这里,尚未发生max_input_time超时,则会重新指定max_execution_time的超时。

同样也是采取调用zend_set_timeout,并传入max_execution_time。特别注意一下,windows下面的需要显式调用zend_unset_timeout关闭原来的定时器,而linux下不需要。这是由于两个平台的定时器实现原理不同导致的,下文也会详细展开叙述。

最后用一张图表示超时控制的流程,左侧的case表明用户既配置了max_input_time,又配置了max_execution_time。而右侧的区别在于用户仅仅配置了max_execution_time:

zend_set_timeout

前文提到,zend_set_timeout函数用来设置定时器。具体来看下实现:

上述实现基本上可以完全分成两种平台:

先看linux:

linux下的定时器要容易许多,调用setitimer函数就行,此外,zend_set_timeout还设定了SIGPROF信号的handler为zend_timeout。

注意,调用setitimer的时候,将it_interval设置成0,表明这个定时器只触发一次,而不会每隔一段时间触发一次。setitimer可以以三种方式计时,php中采用的是ITIMER_PROF,它同时计算了用户代码和内核代码的执行时间。一旦时间到了,会产生SIGPROF信号。

php脚本运行时的超时机制详解(php脚本工作流程)

当php进程接收到SIGPROF信号,不管当前正在执行什么,都会跳转进入到zend_timeout。zend_timeout才是实际处理超时的函数。

再看windows:

首先会启动一个子线程,该线程主要用于设置定时器,同时维护EG(timed_out)变量。

子线程一旦生成,主线程便会向子线程发送一条消息:WM_REGISTER_ZEND_TIMEOUT。子线程接收到WM_REGISTER_ZEND_TIMEOUT之后,产生一个定时器并开始计时。同时,子线程会设置EG(timed_out) = 0。这很重要!windows平台下正是通过判断EG(timed_out)是否为1,来决定是否超时。

如果定时器到时间了,子线程收到WM_TIMER消息,则取消定时器,并且设置EG(timed_out) = 1。

如果需要关闭定时器,则子线程会收到WM_UNREGISTER_ZEND_TIMEOUT消息。关闭定时器,并不会改变EG(timed_out)。

相关代码还是很清晰的:

根据上文描述,最终都是需要跳转到zend_timeout来处理超时的。那windows下如何进入zend_timeout呢?

window下仅在execute函数中(zend_vm_execute.h刚开始的地方),可以看到调用zend_timeout:

上述代码可以看到:

在windows下,每执行完成一条opcode指令,就会进行一次超时判断。

因为主线程执行opcode的同时,子线程可能已经发生超时,而windows并没有什么机制可以让主线程停止手头的工作,直接跳入zend_timeout。所以只好利用子线程先将EG(timed_out)设置为1,然后主线程在等到当前opcode执行完成、进入下一条opcode之前,判断一下EG(timed_out)再调用zend_timeout。

因此准确的讲,windows的超时,其实是有一点点延时的。至少在某一个opcode执行的过程中,无法被打断。当然,正常情况下,单条opcode的执行时间会很短。但是可以很容易人为构造出一些很耗时的函数,使得function call需要等待较长时间。此时,如果子线程判断出超时了,则还需要经过漫长的等待,直到主线程完成该条opcode之后,才能调用zend_timeout。

zend_unset_timeout

zend_unset_timeout同样分成两种平台的实现。

先看linux:

linux下的关闭定时器也很简单。只要将struct itimerval中的4个值都设置为0,就行了。

再看windows:

由于windows是利用一个独立的线程来计时。因此,zend_unset_timeout会向该线程发送WM_UNREGISTER_ZEND_TIMEOUT消息。WM_UNREGISTER_ZEND_TIMEOUT对应的动作是去调用KillTimer来关闭定时器。注意,线程本身并不退出。

前文留下了一个问题,在php_execute_script中,windows下面要显示调用zend_unset_timeout来关闭定时器,而linux下不需要。因为对于一个linux进程来说,只能存在一个setitimer定时器。也就是说,重复调用setitimer,后面的定时器会直接覆盖前面的。

zend_timeout

如前文所述,zend_timeout是实际处理超时的函数。它的实现也很简单。

如果有配置exit_on_timeout,则zend_on_timeout会尝试调用sapi_terminate_process关闭sapi进程。如果无需exit_on_timeout,则直接进入zend_error进行出错处理。大部分情况下,我们并不会设置exit_on_timeout,毕竟我们期望的是虽然一个请求超时了,但是进程仍然保留下来,服务下一个请求。

zend_error除了会打印错误日志,还会利用longjump跳转到boilout指定的栈帧,一般是zend_end_try或者zend_catch宏所在的地方。关于longjump,可以另起一个话题,本文就不具体叙述了。在php_execute_script里面,zend_error会使得程序跳转到zend_end_try的位置然后继续执行。继续执行是指,会调用php_request_shutdown等函数来完成收尾工作。

直到这里,php脚本的超时机制算是讲清楚了。

最后来看一个疑似php内核的bug。

windows下max_input_time的bug

回忆一下,之前有提到windows下只有一个地方调用了zend_timeout,就是execute函数里,准确讲是每条opcode执行之前。

那么,假如发生max_input_time类型的超时,即使子线程将EG(timed_out)被置为1,也得延迟到execute中才能进行超时处理。貌似一切正常。

而问题的关键之处便在于,我们并不能保证主线程执行到execute时,EG(timed_out)任然为1。一旦进入execute之前,EG(timed_out)被子线程修改成0,那么max_input_time类型的超时就永远不会被handle了。

为何EG(timed_out)会被子线程又修改为0呢?原因在于:php_execute_script中,调用了zend_set_timeout(INI_INT("max_execution_time"), 0)来设置定时器。

zend_set_timeout会向子线程发送WM_REGISTER_ZEND_TIMEOUT消息。子线程收到此消息,除了创建定时器之外,还会设置EG(timed_out) = 0(详见上文截取的zend_timeout_WndProc代码片段)。由于线程执行的不确定性,因此不能够判断主线程执行到execute的时候,子线程是否已接收到消息并设置EG(timed_out)为0。

如图所示,

如果execute中的判断发生在红线标注的时间点,则EG(timed_out)为1,execute会调用zend_timeout做超时处理。

如果execute中的判断发生在蓝线标注的时间点,则EG(timed_out)已被重置为0,max_input_time超时被彻底掩盖。

PHP和MySql中位和位的整形范围是多少 一个字节有8位,所以位int型占用位/8位=4个字节,位int型占用位/8位=8个字节.位,位无符号整型最大值:2^-1=^-1=位,位有

php实现中文转数字 分享一个辅助函数,使用php尽可能识别出字符串中的数字,先上代码functioncheckNatInt($str){$map=array('一'='1','二'='2','三'='3','四'='4','五'='5','六'='6','七'='7','

PHP利用imagick生成组合缩略图 先给大家炫下效果图,如果大家觉得还很满意,请继续往下阅读:这里说的imagick是ImageMagick在PHP下的扩展。使用pecl安装起来那叫一个轻松简单一条命令

标签: php脚本工作流程

本文链接地址:https://www.jiuchutong.com/biancheng/289846.html 转载请保留说明!

上一篇:PHP模拟post提交数据方法汇总(php写post接口)

下一篇:PHP和MySql中32位和64位的整形范围是多少(php5.6+mysql)

  • 小规模收到专票怎么处理
  • 固定资产折旧直接计入成本
  • 印花税按合同的多少收
  • 合并资产负债表和合并利润表的区别
  • 境外签署合同的效力
  • 接受捐赠收入会计利润含税吗
  • 公司车子的保养费怎么算
  • 当期免抵税额有什么用
  • 周转材料租赁费怎么结转成本
  • 公司厨房用品专用发票怎么处理?
  • 分包管理费取费标准
  • 其他应付款能转收入么
  • 小规模和一般纳税人的区别
  • 商业企业取得农产品税收
  • 怎样算纳税人
  • 西安职业中等专业学校校长
  • etc在充值后未索取可以再次索取吗
  • 商品期货交易会计核算
  • 化妆品的消费税率多少
  • 分支机构怎么认定和纳税?
  • gitlab lint
  • steam打开速度
  • Mac怎么禁用icloud
  • 美团收入怎么处理的?
  • 如何做无票收入的会计分录
  • 磁盘碎片指的是磁盘因为长期使用
  • 开启自动备份注册表
  • PHP:oci_result()的用法_Oracle函数
  • WINDOWS下php5.2.4+mysql6.0+apache2.2.4+ZendOptimizer-3.3.0配置
  • 公司出售已经提完折旧的机器
  • 受托代销商品会计科目
  • uniapp+uview
  • 喝咖啡的好处和坏处 女性
  • 银行存款转定期存款分录
  • 中小企业发展专项资金绩效评价报告
  • 进项税额转出的所有会计分录
  • 贷款的银行卡叫什么
  • 人工智能示例
  • php底层原理
  • vue fragment标签
  • 财务支出表怎么做
  • 转正差额怎么算
  • 家居办公装修
  • 帝国cms会侵权吗为什么
  • 浅谈php设计模式的理解
  • 正解之途
  • mysql集群配置
  • 医院执行政府会计制度操作指南 .pdf
  • 企业支付的佣金计算多少税率呢
  • 退教育费附加税怎么申报
  • 注册资本一般为多少
  • 融资租赁固定资产不属于筹资活动
  • 成品油发票如何下载库存
  • 公司接待考察团的费用入哪个科目
  • 什么是雇主责任险保单
  • 抵扣认证的发票怎么冲红
  • 损益平准金是什么会计科目
  • 研发支出资本化支出在资产负债表哪里体现
  • win8.1 升级
  • win8如何到桌面
  • linux检查是否存在vxlan模块
  • 您的服务器不支持此服务
  • win7系统安装谷歌浏览器
  • linux find命令的使用
  • android ui线程和主线程的区别
  • linux中awk用法详解
  • 关于echo的名字
  • node.js开发实战
  • centos6.7安装问题
  • js实现拖拽div的弹出框
  • 江西国税局电子税务局
  • 江苏省国税电子税务局官网发票验
  • 国家税务总局网站登录入口官网
  • 阿尔及利亚关税起征点查询
  • 河北航天信息技术有限公司官网
  • 个人租车收入如何缴纳个人所得税
  • 湖南什么时候实行电子驾驶证
  • 个人工资所得税缴纳标准2023年
  • 长沙市税收排名
  • 两会热点有哪些呀?
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设