首页 > 代码库 > Python的运行机制--操作码(opcode)解析
Python的运行机制--操作码(opcode)解析
上一篇文章《Python的运行机制--pyc文件浅析》中已经对Python的运行单元PyCodeObject结构体作了初步的了解,但是要真正理解Python的运行机制,
还要通过分析Python的opcode才行。opcode的解释执行通过ceval.c中的以下函数:
PyObject *PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
来实现,所以这个函数是研究opcode的重点,对于opcode有什么不明白的地方都可以通过这个函数中相应的opcode处理过程来得到解答。
在开始分析opcode之前,还是有必要先了解一下一个PyFrameObject这个数据结构:
可见PyFrameObject中包含一个PyCodeObject的结构体指针f_code,
PyFrameObject中的f_globals和f_locals这两个dict对象分别用于保存全局对象和局部对象,
可以说,PyFrameObject结构就是PyCodeObject的运行环境,PyCodeObject结构是静态的,创建以后就一般不会再变,而PyFrameObject则是动态的,
在创建以后,f_globals和f_locals这两个dict都经常会发生变化,并且一个PyCodeObject可能对应好几个的PyFrameObject中。
好,下面来开始分析opcode,还是延用上篇文件的例子,在这个例子中,通过showfile.py脚本,我们可以看到整个pyc文件中一共保存了5个PyCodeObject结构:
最外层的为模块test的PyCodeObject,其中的代码在import或者直接运行时会得到执行。
在module的PyCodeObject中嵌套了add函数的PyCodeObject结构以及world类的PyCodeObject结构。
在world类的PyCodeObject结构中,又嵌套了__init__方法和sayHello方法的PyCodeObject结构。
我们先来分析一下test模块的PyCodeObject结构体:
再来解析一下产生的opcode:
下面的注释中,locals即为PyFrameObject.f_locals,names代码的对象即为PyCodeObject.co_names
上面对反汇编中来的opcode作了个简单的注释,这里只解释了比较常见的opcode,在Python 2.7中,一共有147条opcode,定义在opcode.h中,
可以研究PyEval_EvalFrameEx的代码来理解它们。
通过分析Python的opcode,相信对于Python的运行应该有了更深刻的理解,当然还有更多的疑问还有待于去解答,如PyFrameObject是怎么创建的,
什么时候会创建,以及如何维护的等等。
还要通过分析Python的opcode才行。opcode的解释执行通过ceval.c中的以下函数:
PyObject *PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
来实现,所以这个函数是研究opcode的重点,对于opcode有什么不明白的地方都可以通过这个函数中相应的opcode处理过程来得到解答。
在开始分析opcode之前,还是有必要先了解一下一个PyFrameObject这个数据结构:
typedef struct _frame { PyObject_VAR_HEAD struct _frame *f_back; /* previous frame, or NULL */ PyCodeObject *f_code; /* code segment */ PyObject *f_builtins; /* builtin symbol table (PyDictObject) */ PyObject *f_globals; /* global symbol table (PyDictObject) */ PyObject *f_locals; /* local symbol table (any mapping) */ PyObject **f_valuestack; /* points after the last local */ /* Next free slot in f_valuestack. Frame creation sets to f_valuestack. Frame evaluation usually NULLs it, but a frame that yields sets it to the current stack top. */ PyObject **f_stacktop; PyObject *f_trace; /* Trace function */ /* If an exception is raised in this frame, the next three are used to * record the exception info (if any) originally in the thread state. See * comments before set_exc_info() -- it's not obvious. * Invariant: if _type is NULL, then so are _value and _traceback. * Desired invariant: all three are NULL, or all three are non-NULL. That * one isn't currently true, but "should be". */ PyObject *f_exc_type, *f_exc_value, *f_exc_traceback; PyThreadState *f_tstate; int f_lasti; /* Last instruction if called */ /* Call PyFrame_GetLineNumber() instead of reading this field directly. As of 2.3 f_lineno is only valid when tracing is active (i.e. when f_trace is set). At other times we use PyCode_Addr2Line to calculate the line from the current bytecode index. */ int f_lineno; /* Current line number */ int f_iblock; /* index in f_blockstack */ PyTryBlock f_blockstack[CO_MAXBLOCKS]; /* for try and loop blocks */ PyObject *f_localsplus[1]; /* locals+stack, dynamically sized */ } PyFrameObject;
这里我们重点关注下以下的成员:
PyObject **f_stacktop; ... PyCodeObject *f_code; /* code segment */ PyObject *f_globals; /* global symbol table (PyDictObject) */ PyObject *f_locals; /* local symbol table (any mapping) */
可见PyFrameObject中包含一个PyCodeObject的结构体指针f_code,
PyFrameObject中的f_globals和f_locals这两个dict对象分别用于保存全局对象和局部对象,
可以说,PyFrameObject结构就是PyCodeObject的运行环境,PyCodeObject结构是静态的,创建以后就一般不会再变,而PyFrameObject则是动态的,
在创建以后,f_globals和f_locals这两个dict都经常会发生变化,并且一个PyCodeObject可能对应好几个的PyFrameObject中。
好,下面来开始分析opcode,还是延用上篇文件的例子,在这个例子中,通过showfile.py脚本,我们可以看到整个pyc文件中一共保存了5个PyCodeObject结构:
最外层的为模块test的PyCodeObject,其中的代码在import或者直接运行时会得到执行。
在module的PyCodeObject中嵌套了add函数的PyCodeObject结构以及world类的PyCodeObject结构。
在world类的PyCodeObject结构中,又嵌套了__init__方法和sayHello方法的PyCodeObject结构。
我们先来分析一下test模块的PyCodeObject结构体:
<code> <argcount> 0 </argcount> <nlocals> 0</nlocals> <stacksize> 3</stacksize> <flags> 0040</flags> <code> 6400006401006c00005a00006501005a02006402008400005a0300640300 640500640400840000830000595a04006504008300005a05006505006a06 008300000164010053 </code> <dis> ... </dis> <names> ('dis', 'True', 'myglobal', 'add', 'world', 'w', 'sayHello')</names> <varnames> ()</varnames> <freevars> ()</freevars> <cellvars> ()</cellvars> <filename> 'test.py'</filename> <name> '<module>'</name> <firstlineno> 1</firstlineno> <consts> -1 None ... </consts> </code>
再来解析一下产生的opcode:
下面的注释中,locals即为PyFrameObject.f_locals,names代码的对象即为PyCodeObject.co_names
1 0 LOAD_CONST 0 (-1) #加载consts数组中索引为0处的值,这里为数值-1 3 LOAD_CONST 1 (None) #加载consts数组中索引为1处的值,这里为None 6 IMPORT_NAME 0 (dis) #加载dis模块:names[0]即为"dis" 9 STORE_NAME 0 (dis) #将模块保存到一个dict中,这个dict专门用来保存局部变量的值,key为names[0],即"dis" 2 12 LOAD_NAME 1 (True) #将names[1],即True压栈。 15 STORE_NAME 2 (myglobal) #将栈顶的元素,即True保存到locals['myglobal']中,names[2]即为字符串'myglobal' 4 18 LOAD_CONST 2 (<code object add at 024E3B60, file "test.py", line 4>) #将consts[2],即add函数的PyCodeObject压栈。 21 MAKE_FUNCTION 0 #通过add函数的PyCodeObject创建一个函数,并压入栈顶。 24 STORE_NAME 3 (add) #将创建的函数出栈并保存到locals['add']中 9 27 LOAD_CONST 3 ('world') #consts[3],即"world"入栈 30 LOAD_CONST 5 (()) #consts[5],即空数组入栈 33 LOAD_CONST 4 (<code object world at 024E3650, file "test.py", line 9>) #将consts[4],即world的PyCodeObject入栈。 36 MAKE_FUNCTION 0 #创建函数 39 CALL_FUNCTION 0 #调用刚创建的函数,用于初始化类,会返回一个dict到栈顶,这个dict中保存了类的方法以及一些全局变量, #具体的实现要看world类的PyCodeObject中的opcode 42 BUILD_CLASS #创建类,注意BUILD_CLASS会用到栈中的三个对象,这里分别为:保存在dict中的类的信息,基类数组,这里为空数组(),类的名称:"world" 43 STORE_NAME 4 (world) #将类保存到locals['world']中 15 46 LOAD_NAME 4 (world) 49 CALL_FUNCTION 0 52 STORE_NAME 5 (w) #以上三行代码创建一个world对象 16 55 LOAD_NAME 5 (w) 58 LOAD_ATTR 6 (sayHello) 61 CALL_FUNCTION 0 #以上三行代码调用w.sayHello() 64 POP_TOP 65 LOAD_CONST 1 (None) 68 RETURN_VALUE
上面对反汇编中来的opcode作了个简单的注释,这里只解释了比较常见的opcode,在Python 2.7中,一共有147条opcode,定义在opcode.h中,
可以研究PyEval_EvalFrameEx的代码来理解它们。
通过分析Python的opcode,相信对于Python的运行应该有了更深刻的理解,当然还有更多的疑问还有待于去解答,如PyFrameObject是怎么创建的,
什么时候会创建,以及如何维护的等等。
Python的运行机制--操作码(opcode)解析
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。