zoukankan      html  css  js  c++  java
  • 《python源代码剖析》笔记 Python虚拟机框架

    本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie


    1.
    Python虚拟机会从编译得到的PyCodeObject对象中依次读入每一条字节码指令。
    并在当前的上下文环境中运行这条字节码指令。


    Python虚拟机实际上是在模拟操作中运行文件的过程
    PyCodeObject对象中包括了字节码指令以及程序的全部静态信息,但没有包括
    程序执行时的动态信息——执行环境(PyFrameObject)


    2.Python源代码中的PyFrameObject

    typedef struct _frame{
    	PyObject_VAR_HEAD //"执行时栈"的大小是不确定的
    	struct _frame *f_back; //执行环境链上的前一个frame。非常多个PyFrameObject连接起来形成执行环境链表
    	PyCodeObject *f_code; //PyCodeObject 对象,这个frame就是这个PyCodeObject对象的上下文环境
    	PyObject *f_builtins; //builtin名字空间
    	PyObject *f_globals;  //global名字空间
    	PyObject *f_locals;   //local名字空间
    	PyObject **f_valuestack; //"执行时栈"的栈底位置
    	PyObject **f_stacktop;   //"执行时栈"的栈顶位置
    	//...
    	int f_lasti;  //上一条字节码指令在f_code中的偏移位置
    	int f_lineno; //当前字节码相应的源码行
    	//...
    	
    	//动态内存,维护(局部变量+cell对象集合+free对象集合+执行时栈)所须要的空间
    	PyObject *f_localsplus[1];	
    } PyFrameObject;


    名字空间实际上是维护着变量名和变量值之间关系的PyDictObject对象。
    f_builtins, f_globals, f_locals名字空间分别维护了builtin, global, local的name与相应值
    之间的映射关系。



    每个 PyFrameObject对象都维护了一个 PyCodeObject对象。这表明每个 PyFrameObject中的动态内存空间
    对象都和源码中的一段Code相相应。



    Code Block。PyFrameObject,作用域,名字空间好像都与一段代码段一一相应?



    PyFrameObject中的动态内存空间

    PyFrameObject *PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals, PyCodeObject *locals)
    {
    	PyFrameObject *f;
    	Py_ssize_t extras, ncells, nfrees, i;
    	ncells = PyTuple_GET_SIZE(code->co_cellvars);
    	nfrees = PyTuple_GET_SIZE(code->co_freevars);
    	//这四部分构成了 PyFrameObject维护的动态内存区。其大小由extras确定
    	extras = code->co_stacksize + code->co_nlocals + ncells + nfrees;
    	f = PyObject_GC_NewVar(PyFrameObject, &PyFrame_Type, extras);
    	//计算初始化时执行时栈的栈顶
    	extras = code->co_nlocals + ncells + nfrees;
    	//f_valuestack维护执行时栈的栈底,f_stacktop维护执行时栈的栈顶
    	f->f_valuestack = f->f_localsplus + extras;
    	f->f_stacktop = f->f_valuestack;
    	return f;
    }
    在创建 PyFrameObject对象时,额外申请的内存有一部分是给 PyCodeObject对象用的,
    还有一部分才是给执行时栈用的。


    在Python中訪问 PyFrameObject对象
    8.1.3的caller.py我执行失败了。在f_globals.keys()处出错
    看不懂frame_getter.py

    3.名字、作用域和名字空间
    对于python这类动态语言来说,名字的意义远比C这类的静态语言大。由于名字是python
    在执行时可以找到其所相应的东西的唯一途径。



    名字: 一个标识符就是一个名字,比方变量名、函数名、类名等
    作用域:作用域是指约束起作用的程序正文区域。

    Python是具有静态作用域(词法作用域)的。
    在Python中,一个约束在程序正文的某个位置是否起作用。是由该约束在文本中的位置唯一决定的,
    而不是执行时动态决定的。


    名字空间:由一个 PyDictObject对象实现。每个名字空间都与一个作用域相应

    module
    1.将逻辑相关的代码放在一起,实现代码复用
    2.为系统划分名字空间(一个module定义了一个独立的名字空间)

    赋值语句会影响名字空间(“拥有赋值行为、拥有设置对象属性的行为”)
    1.创建一个对象obj
    2.将obj"赋给"一个名字name

    a = 1
    def f()
    class A(object)
    import abc

    
    嵌套作用域最内嵌套作用域规则:1.闭包实现  2.编译器实现LEGB规则:名字引用动作沿着local作用域、嵌套作用域、global作用域、builtin作用域的顺序查找函数相应local作用域。module源文件相应global作用域,Python自身定义了最顶层的作用域——builtin作用域几个有助理解LEGB规则的代码段
    <span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px;"></span><pre name="code" class="cpp">#----------------------
    a = 1 #[1]
    def f():
    	a = 2 #[2]
    	print a #[3] 輸出2
    print a #[4] 輸出1
    #----------------------
    #闭包。

    在运行func = f()的时候。会运行函数f中的def g():语句,这时Python会将约束a = 2与函数g相应的函数对象捆绑在一起

    #将捆绑后的结果返回
    a = 1
    def f():
    	a = 2
    	def g():
    		print a #[1]:输出结果为2
    	return g
    func = f()
    func() #[2]
    #----------------------
    <pre name="code" class="cpp">#由一个赋值语句引进的名字在这个赋值语句所在的作用域是可见的。在 第9章 Python虚拟机中的一般表达式 这章有说明实现原理
    #[1]pint a语句处,a已经存在了这个Code Block相应的PyFrameObject相应的co_names符号表里了。但因为还没运行[2]的赋值语句,
    #在PyFrameObject的f_locals里并没有这个键值对,所以在[1]处会出现“local variable 'a' referenced before assignment”的错误
    a = 1def g():print adef f():print a #[1] error: local variable 'a' referenced before assignment a = 2 #[2]print ag()f()#----------------------a = 1def f():global aprint a #输出结果:1a = 2f()print a #输出结果:2#----------------------a = 1def f():a = 2def g():global aprint a #输出结果:1a += 1return gg = f()g()print a #输出结果:2
    
    
    
    
    4.Python虚拟机的执行框架——字节码执行引擎
    PyEval_EvalFrameEx 
    虚拟机的详细实现-->switch/case结构
    Python虚拟机运行字节码指令序列的过程就是从头到尾遍历整个co_code、依次运行字节码指令(字符数组)的过程。



    /* Interpreter main loop */ 
    PyObject* PyEval_EvalFrame(PyFrameObject *f) 
    {
    	//…… 
    	why = WHY_NOT;//指示了在退出for循环时Python运行引擎的状态
    	for (;;) {
    	//…… 
    	fast_next_opcode: 
    		f->f_lasti = INSTR_OFFSET();
    		/* 获得字节码指令和參数*/
    		opcode = NEXTOP();
    		oparg = 0;  
    		/* 假设指令须要參数。获得指令參数*/
    		if (HAS_ARG(opcode))
    			oparg = NEXTARG();
    	dispatch_opcode: 
    		switch (opcode) {
    		case NOP:
    		goto fast_next_opcode;
    		case LOAD_FAST:
    		//…… 
    		} 
    }

    5.Python执行时环境初探
    PyInterpreterState --> 进程的抽象
    PyThreadState --> 线程状态信息的抽象
    在Win32下,线程是还能独立存活的,它须要存活在进程的环境中。多个线程能够共享进程的一些资源
    进程中会有线程集合,线程中会有函数调用堆栈。


    当Python虚拟机開始运行时,会将当前线程状态对象中的frame设置为当前的运行环境(frame),而
    在建立新的PyFrameObject对象时,则从当前线程的状态对象中取出旧的frame,建立PyFrameObject链表
    线程之间的同步靠的是全局解释锁(GIL)

    //进程
    typedef struct _is{
    	struct _is *next;
    	struct _ts *tstate_head; //模拟进程环境中的线程集合
    	
    	PyObject *modules;
    	PyObject *sysdict;
    	PyObject *builtins;
    	
    	//...
    } PyInterpreterState
    //线程
    typedef struct _ts{
    	struct _ts *next
    	PyInterpreterState *interp;
    	struct _frame *frame; //模拟线程中的函数调用堆栈
    	int recursion_depth;
    	//...
    	PyObject *dict;
    	//...
    	long thread_id;
    } PyThreadState;


  • 相关阅读:
    又玩起了“数独”
    WebService应用:音乐站图片上传
    大家都来DIY自己的Blog啦
    CSS导圆角,不过这个代码没有怎么看懂,与一般的HTML是不同
    网站PR值
    CommunityServer2.0何去何从?
    网络最经典命令行
    炎热八月,小心"落雪"
    Topology activation failed. Each partition must have at least one index component from the previous topology in the new topology, in the same host.
    SharePoint 2013服务器场设计的一些链接
  • 原文地址:https://www.cnblogs.com/yutingliuyl/p/6872864.html
Copyright © 2011-2022 走看看