当先锋百科网

首页 1 2 3 4 5 6 7

要过年了,在年前完成这篇文章,如果有缘可以看到,祝福看到的朋友新年快乐,在新的一年里,万事顺意!

按今年的计划每个月至少有两篇文章,而一月份因为各种理由而只有一篇2012的总结,无论什么原因,总归是不对的。这篇算是补上的,也作为今年的开始。

回正题,今天要研究的是PHP的ticks机制。

PHP提供declare关键字和ticks关键字来声明ticks机制。如:declare(ticks = N); 这表示:在当前scope内,每执行N句internal statements(opcodes),就会中断当前的业务语句,去执行通过register_tick_function注册的函数(如果存在的话),然后再继续之前的代码。需要注意的是这里的N是指的PHP的一些OPCODE,而OPCODE与我们见到的PHP语句却不是一一对应的。

最开始我以为PHP内核是在编译时记录是否有ticks机制,在真正执行中间代码时插入判断代码,实现此机制。但是事实上却不是这样滴。

看PHP代码示例1:

$name = "phppan"; echo $name; class Tipi { public function test() { echo "test"; } } function f_tipi() { }

如上代码包括了我们常见的几种语句,赋值,输出,定义类,定义函数。通常我们用VLD查看PHP生成的中间代码,上面的代码通过 php -dvld.active=1 t.php 我们会看到 ECHO、ASSIGN、NOP等中间代码。

现在我们在示例1的代码上添加上ticks机制。如PHP代码示例2:

declare(ticks=1); $name = "phppan"; echo $name; class Tipi { public function test() { echo "test"; } } function f_tipi() { }

示例2与示例1相比也就是多了第一条语句: declare(ticks=1); 如果我们此时再次通过VLD查看中间代码,会发现在每个中间代码的后面都多了一句中间代码:TICKS 。

是否因为ticks=1的原因而在每个中间代码的后面添加了TICKS?将declare(ticks=1);换成declare(ticks=100);,再次VLD,结果没有变化。从以上的结果可以看出,PHP内核在语法分析过程中实现了ticks机制。

从实现过程来说定义ticks机制分为两个过程:一个是定义是否需要执行ticks或者说声明ticks机制,另一个实现在声明了ticks机制的情况下控制语句的执行。

声明ticks机制过程

声明的过程就是调用declare(ticks = N); 在语法分析时根据declare关键字和参数中的ticks关键字来声明ticks机制。通过zend_compile.c文件中的zend_do_declare_begin、declare_statement、zend_do_declare_end三个函数来编译声明ticks机制。在declare_statement函数中我们可以看到:declare除了可以声明ticks之外,还可以声明encoding,这在代码里面就是一个if else的判断。

ticks机制的声明仅在编译过程有用,它为后面的声明控制语句服务。其编译过程中的全局变量为:CG(declarables)。这是一个结构体,它仅有一个成员:ticks。当然后面应该还会有更多的成员出现。

声明控制语句

示例1和示例2已经充分说明在每条语句的语法分析时,会根据是否声明了ticks机制来添加TICKS中间代码,其实现在于每条语句在语法解析时都会添加一条函数调用:zend_do_ticks。从zend_language_parser.y文件中可以看出:zend_do_ticks函数添加在类定义语句,函数定义语句和常规语句的后面。 zend_compile.c文件中的zend_do_ticks函数会根据前面提到的 CG(declarables).ticks 来判断是否生成 ZEND_TICKS 中间代码(在VLD中看到的中间代码都是没有ZEND开头)。

除了声明ticks机制,还有执行。执行过程中关键的变量是在声明时的ticks=N。其实这里的N可以换个角度去理解:ticks指定的数字是指执行了多少次TICKS语句。在TICKS中间代码的执行函数ZEND_TICKS_SPEC_CONST_HANDLER中,会统计执行当前函数的次数,存储变量为EG(ticks_count)。当达到当初声明的界限,就会调用一次所有通过register_tick_function注册的函数,并计数清零。

与当初自己设想的实现相比,PHP内核对ticks机制的实现满足了功能单一原则和松耦合原则。将ticks机制作为一个中间代码添加到整个中间代码的执行体系中,包括状态的转移,函数的切换这些都是直接使用原有的机制。

ticks机制的应用场景

手册上说:Ticks 很适合用来做调试,以及实现简单的多任务,后台 I/O 和很多其它任务。

在调试过程中,对于定位一段特定代码中速度慢的语句比较有用,我们可以每执行两条低级语句就记录一次时间。虽然这个过程也可以用其它方法完成,但用 tick 更方便也更容易实现。

PCNTL也使用ticks机制来作为信号处理机制(signal handle callback mechanism),可以最小程度地降低处理异步事件时的负载。这里的关键在于PCNTL扩展的模块初始化函数(PHP_MINIT_FUNCTION(pcntl))。在此模块做模块初始化时,它会调用: php_add_tick_function(pcntl_signal_dispatch);将pcntl的分发执行函数添加到ticks机制的调用函数中去,从而当ticks触发时就会调用PCNTL扩展函数中指定的所有方法。