仔细看了《python源码剖析》这书的7,8章,觉得记载一些笔记,理清一下思路比较好。
看完第八章,按照自己的理解画了一个图。
《剖析》一书的前六章纯属基础,甚至可以当一个c课程设计来读。第六章作者的smallpython这个应用程序就是例子。
《剖析》第七章开始读的速度要放慢,因为进入了新的领域。而且个人感觉从这一章开始,由于源码的复杂性,作者引用源码的省略部分增多,造成读者阅读困难,个人感觉要对照项目自己看。
这本书大概是为了方便读者理解,采取的并不是python启动顺序的时间线来讲解,而是采取的从简单到复杂的方式讲解的。我这里换个方式,按照到目前为止我的理解,python启动顺序的时间线来讲解。
python运行时环境(也就是整个全局环境)的建立,7,8章并没有详解,那么我们就从python运行时环境建立后说起。
按我的理解,python运行时环境建立好后,会调用PyEval_EvalFrameEx(ceval.c)。
首先获取当前线程,在这里是一个
/* pystate.h */
typedef struct _ts {
struct _ts *next; /* 指向兄弟线程 */
PyInterpreterState *interp; /* 指向自己所处的进程 */
struct _frame *frame;
...
long thread_id;
} PyThreadState;
可以看到一个进程中的线程是用单向链表串起来的。
进程类PyInterpreterState于本文无太大关系,略去不讲。
我们看struct _frame *frame.
/* frameobject.h */
typedef struct _frame {
...
struct _frame *f_back;
PyCodeObject *f_code;
PyObject *f_builtins;
PyObject *f_globals;
PyObject *f_locals;
...
/* 栈空间 */
} PyFrameObject;
简单来理解,一个PyFrameObject就是一个Code Block, 一个Code Block就是一个名字空间,一个名字空间就是一个module或一个函数或一个类。 反正进入了一个地方后,变量名的所处环境要发生改变,这个地方就叫做Code Block。
比如
/* module1.py */
import sys
a = 1
def p():
a = 2
print a
p()
这个代码里有两个Code Block。一个是module本身,一个是p函数。
每段Code Block都对应有PyCodeObject。 这里面记录了这个Code Block的字节码。
每个Code Block都对应有PyFrameObject,这里面记录了栈帧信息。
举例来说:
调用p,是发生在module1里的。
所以p对应的PyFrameObject的f_back字段就指向module1对应的PyFrameObject。
还是回到PyEval_EvalFrameEx这个函数,这个函数把当前线程的frame字段指向需要执行的frame后,就开始逐个按序执行frame->f_code里的字节码。一个字节码其实是一个占一个byte的数字。python总共有140个左右的字节码。
从上面的图来看,从.py到PyCodeObject的过程属于编译过程,本书中没有做讲解。
现在要仔细看的是两个地方
1.从PyCodeObject到PyFrameObject的过程(也就是frameobject.c中的
PyFrameObject PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals, PyObject *locals) 目前为止我猜想这个函数是被python运行时环境调用的)
2.生成PyFrameObject后,如何去执行。
PyObject *PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
这个函数位于ceval.c,是python虚拟机实现的核心部分。
阅读(1119) | 评论(0) | 转发(0) |