附录*PHP的ticks机制

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

Finding entry points
Branch analysis from position: 0
Jump found. (Code = 62) Position 1 = -2
filename:       /home/revin/work/code/test/1.php
function name:  (null)
number of ops:  7
compiled vars:  !0 = $name
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   2     0  E >   EXT_STMT                                                 
         1        ASSIGN                                                   !0, 'phppan'
   3     2        EXT_STMT                                                 
         3        ECHO                                                     !0
   4     4        EXT_STMT                                                 
   9     5        EXT_STMT                                                 
  10     6      > RETURN                                                   1

branch: #  0; line:     2-   10; sop:     0; eop:     6; out1:  -2
path #1: 0, 
Function f_tipi:
Finding entry points
Branch analysis from position: 0
Jump found. (Code = 62) Position 1 = -2
filename:       /home/revin/work/code/test/1.php
function name:  f_tipi
number of ops:  3
compiled vars:  none
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   9     0  E >   EXT_NOP                                                  
  10     1        EXT_STMT                                                 
         2      > RETURN                                                   null

branch: #  0; line:     9-   10; sop:     0; eop:     2; out1:  -2
path #1: 0, 
End of function f_tipi

Class Tipi:
Function test:
Finding entry points
Branch analysis from position: 0
Jump found. (Code = 62) Position 1 = -2
filename:       /home/revin/work/code/test/1.php
function name:  test
number of ops:  5
compiled vars:  none
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   5     0  E >   EXT_NOP                                                  
   6     1        EXT_STMT                                                 
         2        ECHO                                                     'test'
   7     3        EXT_STMT                                                 
         4      > RETURN                                                   null

branch: #  0; line:     5-    7; sop:     0; eop:     4; out1:  -2
path #1: 0, 
End of function test

End of class Tipi.

phppan

现在我们在示例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

Finding entry points
Branch analysis from position: 0
Jump found. (Code = 62) Position 1 = -2
filename:       /home/revin/work/code/test/1.php
function name:  (null)
number of ops:  13
compiled vars:  !0 = $name
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   2     0  E >   EXT_STMT                                                 
         1        TICKS                                                    
   3     2        EXT_STMT                                                 
         3        ASSIGN                                                   !0, 'phppan'
         4        TICKS                                                    
   4     5        EXT_STMT                                                 
         6        ECHO                                                     !0
         7        TICKS                                                    
   5     8        EXT_STMT                                                 
         9        TICKS                                                    
  10    10        EXT_STMT                                                 
  11    11        TICKS                                                    
        12      > RETURN                                                   1

branch: #  0; line:     2-   11; sop:     0; eop:    12; out1:  -2
path #1: 0, 
Function f_tipi:
Finding entry points
Branch analysis from position: 0
Jump found. (Code = 62) Position 1 = -2
filename:       /home/revin/work/code/test/1.php
function name:  f_tipi
number of ops:  3
compiled vars:  none
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
  10     0  E >   EXT_NOP                                                  
  11     1        EXT_STMT                                                 
         2      > RETURN                                                   null

branch: #  0; line:    10-   11; sop:     0; eop:     2; out1:  -2
path #1: 0, 
End of function f_tipi

Class Tipi:
Function test:
Finding entry points
Branch analysis from position: 0
Jump found. (Code = 62) Position 1 = -2
filename:       /home/revin/work/code/test/1.php
function name:  test
number of ops:  6
compiled vars:  none
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   6     0  E >   EXT_NOP                                                  
   7     1        EXT_STMT                                                 
         2        ECHO                                                     'test'
         3        TICKS                                                    
   8     4        EXT_STMT                                                 
         5      > RETURN                                                   null

branch: #  0; line:     6-    8; sop:     0; eop:     5; out1:  -2
path #1: 0, 
End of function test

End of class Tipi.

phppan

是否因为ticks=1的原因而在每个中间代码的后面添加了TICKS?将declare(ticks=1);换成declare(ticks=100);,再次VLD,结果没有变化。从以上的结果可以看出,PHP内核在语法分析过程中实现了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扩展函数中指定的所有方法。

资料

PHP的ticks机制

php手册里,pcntl_signal函数解释,很多例子前面declare(ticks=1)这句话的意义

你知道PHP信号处理的正确打开方式吗?

PHP官方的pcntl_signal性能极差

Last updated