【协程原理】 - cPython的VM真变态

kilim在JVM上实现了协程,其实现看起来挺容易的:http://www.malhar.net/sriram/kilim/thread_of_ones_own.pdf
在cPython上是否能够复制其技法呢?粗看上去,是很容易的,甚至比JVM更好实现:

一个更加牛B的事情是,有人已经用@goto装饰器的方式在Python上实现了GOTO了
这个是Python 2版本的:http://code.activestate.com/recipes/576944-the-goto-decorator/
这个是Python 3版本的:https://github.com/cdjc/goto

这两个实现很好的展示了如何在Python中做类似JVM上ASM库做的事情,字节码增强。

===============
但是,cPython的VM对于frame状态的建模使得在cPython上实现kilim,比JVM要难得多。在cPython某个指令执行的那一刻,有五个部分的状态

  • builtins
  • globals
  • locals
  • value_stack
  • block_stack

前三个都是python里做动态执行的常客,没啥困难的。而且frame通过inpsect可以很轻易的获得locals的值。而后两者是非常困难的,我们可以来看一段我随便写的代码

for i in range(1):
    print(i)

生成的bytecode是

      0 SETUP_LOOP         30 (to 33) 
      3 LOAD_GLOBAL         0 (0) 
      6 LOAD_CONST          1 (1) 
      9 CALL_FUNCTION       1 (1 positional, 0 keyword pair) 
     12 GET_ITER        
>>   13 FOR_ITER           16 (to 32) 
     16 STORE_FAST          0 (0) 
     19 LOAD_GLOBAL         1 (1) 
     22 LOAD_FAST           0 (0) 
     25 CALL_FUNCTION       1 (1 positional, 0 keyword pair) 
     28 POP_TOP         
     29 JUMP_ABSOLUTE      13 
>>   32 POP_BLOCK       
>>   33 LOAD_CONST          0 (0) 
     36 RETURN_VALUE 

我们可以看到一个简单的循环,产生了一对SETUP_LOOP和POP_BLOCK,这个是操作block_stack的。以及GET_ITER和FOR_ITER这是操作value_stack的。如果我们想要直接跳入到print(i)这一行,那么就要恢复当时的block_stack,以及当时的value_stack。而且要特别注意GET_ITER的含义是把栈顶的值标识为ITER,使得FOR_ITER可以使用,所以还不是简单地把value_stack恢复就可以了

GET_ITER
Implements TOS = iter(TOS).

这篇博客把python的frame内部状态讲得非常清楚:http://tech.blog.aknin.name/tag/block-stack/
以上可以解释为什么没有人在python里搞字节码的trick了,因为这个VM太变态。

    原文作者:taowen
    原文地址: https://segmentfault.com/a/1190000000668456
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞