Python 虚拟机实现(一)

标签: python 虚拟机 | 发表时间:2011-08-27 17:06 | 作者:(author unknown) xcv58
出处:http://simple-is-better.com/

python并不将py文件编译为机器码来运行,而是由python虚拟机一条条地将py語句解释运行,这也是为什么被称为解释语言的原因之一。但python虚拟机并不直接执行py語句,它执行编译py語句后生成的字节码。本篇简单地讲下编译、运行的过程,涉及到的内容有如何编译、控制流、函数及类的实现等。

0. python的编译

python将py文件编译成为PyCodeObject,再将这个对象写入某文件就成为了pyc文件,文件中包含python的magic number(来说明编译时使用的python版本号)、源文件的mtime(使pyc和py文件保持同步)、编译出的code对象。将对象写入到一个文件似乎听起来不太可能,不过其实很简单,python只写入特定类型的对象,比如要写入一个code对象,python会按一定的顺序将这个对象中的属性一一写入,由于对象是固定的,因而只要记下写入时的顺序就可以从文件中恢复出对象。注意我们这里谈论的并不是python中的序列化。python在写入实际的对象时会写入标识符,即可以标明对象的边界,又可以保持内容信息以在内存中恢复出对象。

python将对象写入文件最后会将调用到两个函数,一个用来写个int,一个用来写入string。对于string来说,为了达到和intern机制一样的目的不重复写string,在写入的时候python还会维持一个dict来保存已经写入的被intern处理后的string,因此当一个string被intern处理过并且出现在之前所说的dict中时,python仅仅会写入该string的索引值,即它是第几个string。当读取文件时,python则会根据每个string的索引值重建一个list,碰到重复的intern的string时就可以根据索引值读出该string的值了。

1. python虚拟机基础

python虚拟机的执行方式就是模仿普通x86可执行文件运行方式,也有栈、帧等概念。除止之外,python还有一个执行环境的问题,考虑print a句话,a肯定指向了一个对象,但是是什么对象呢,这个就由执行环境来确定了,語句可以通过执行环境读写变量的值,在源代码中对这个执行环境的模拟是对它PyFrameObject来完成的,每一个code block都对应一个执行环境,就是一个PyFrameObject,可以认为是一帧。这个struct比较复杂,看下代码:

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;

    PyTryBlock f_blockstack[CO_MAXBLOCKS]; /* for try and loop blocks */
    PyObject *f_localsplus[1];  /* locals+stack, dynamically sized */
} PyFrameObject;

许多个PyFrameObject通过f_back连成一串链表,表示了帧与帧之间的先后、调用顺序。python中帧在运行时需要额外的内存,比如a = b + c这段代码,那么需要先请读入b、c,再算a,除此之外还有局部变量等需要保存在栈中,因此最后有一个f_localsplus指向这块多出来的内存,大小则在编译时计算出来保存在f_stacksize中,这块内存具体用来依次保存locals(局部变量)、cellvars、freevars(后两个和闭包的实现有关)、动态栈(f_valuestack指向栈底、起始位置,f_stacktop则维持栈顶)。

当Python虚拟机开始执行时,它会先进行一些初始化操作,最后进入PyEval_EvalFramEx函数,它的作用是不断读取编译好的字节码,并一条一条执行,类似CPU执行指令的过程。函数内部主要是一个switch结构,根据字节码的不同执行不同的代码。

2. Python运行环境及执行过程

先从整体上看看Python的运行环境。我们知道在操作系统中执行程序离不开两个概念:进程和线程,在Python中也是这样,Python模拟了这两个概念。模拟进程或线程的分别是PyInterpreterState和PyThreadState。可以想象,每个PyThredState都对应着一个帧栈,Python虚拟机在多个线程上切换。

当Python虚拟机开始执行时,它会先进行一些初始化操作,最后进入PyEval_EvalFramEx函数,它的作用是不断读取编译好的字节码,并一条一条执行,类似CPU执行指令的过程。函数内部主要是一个switch结构,根据字节码的不同执行不同的代码。用一张图表示:

3. Python的名字空间

名字空间在Python中是一个非常重要的概念,读写变量值其实就是到某个名字空间中读写与该变量名相对应的对象。如果我们把某个符号和与之关联的对象之间的关系(就是(name, value)这样的关联关系)称为約束的话,那可以认为名字空间的内容就是由一组组約束构成,而增加約束的語句可以被称为赋值語句,函数定义、类定义等都可以被称为是赋值語句。当一个module被加载后,Python会执行相应的代码在这个module的名字空间中建议相应的約束,然后就可以用module.attr这样的語句来访问module的属性。

对于每个module来说,它都有一个顶层的名字空间,可以认为是global名字空间,同时也没有哪个名字空间能够跨module存在。具体到module的某行代码,对它来说还存在local名字空间(比较在函数体或类定义中)、enclosing名字空间(用来实现闭包,其实它不真的存在,但假定有这样一个更好理解)。而Python内置了builtin名字空间。如果要查找某个符号的话,Python会依次查找LEGB。要注意的是Python具有静态作用域,它的意思就是说名字空间这个东西是在你写好py代码后就确定的,而不是由执行的时候确定的。这一部分对于理解Python非常重要,由于篇幅所限之前在是不能详述,建议查看《Python源码剖析》第8.2节。

# 来源:罗杰斯的博客


在微博上关注: 新浪, 腾讯   投稿

最新招聘

更多>>

相关 [python 虚拟机] 推荐:

Python 虚拟机实现(一)

- xcv58 - python.cn(jobs, news)
python并不将py文件编译为机器码来运行,而是由python虚拟机一条条地将py語句解释运行,这也是为什么被称为解释语言的原因之一. 但python虚拟机并不直接执行py語句,它执行编译py語句后生成的字节码. 本篇简单地讲下编译、运行的过程,涉及到的内容有如何编译、控制流、函数及类的实现等. python将py文件编译成为PyCodeObject,再将这个对象写入某文件就成为了pyc文件,文件中包含python的magic number(来说明编译时使用的python版本号)、源文件的mtime(使pyc和py文件保持同步)、编译出的code对象.

Java虚拟机家族考

- ReadReply - 博客园新闻频道
  说起Java虚拟机,许多Java程序员都会潜意识地把它与Sun[1] HotSpot虚拟机等同看待,也许还有一些程序员会注意到BEA JRockit和IBM J9,但大多数人对JVM的认识都仅限于此了.   从1996年初Sun发布的JDK 1.0中所包含的Sun Classic VM算起,Java虚拟机已经发展了15个年头,沧海桑田一瞬间,15年转眼而过,这期间曾经涌现、湮灭过许多或经典或优秀或有特色的虚拟机实现,在《Java虚拟机专栏》的第1篇中,我们先暂且把代码与技术放下,一起来回顾一下Java虚拟机家族的发展轨迹和历史变迁.

Xen 虚拟机架构

- - 博客园_知识库
  Xen 是一个基于开源软件组织的虚拟机监控器(即 Virtual Machine Monitor 简称 VMM),可以允许在单一的物理机器上同时运行多个操作系统实例.   虚拟计算机的概念最早由 IBM 公司在上世纪六七十年代提出,并将其运用于 VM/370 系统中以共享昂贵的大型机系统(Main Frame).

虚拟机三种协议

- - 操作系统 - ITeye博客
如何使虚拟机与主机互相能ping通 刚刚因为虚拟机与主机没法互相ping通的事情,奋战到将近凌晨一点. 现在把这个过程总结一下,以方便后加入该行业的广大IT精英. VMWare提供了三种工作模式:bridged(桥接模式)、NAT(网络地址转换模式)和host-only(主机模式). 1.       bridged(桥接模式).

dropbox讲python

- chuang - Initiative
dropbox定制优化CPython虚拟机,自己搞了个malloc调度算法. 那个 !!!111cos(0). 期待这次PyCon China 2011.

Python调试

- - 企业架构 - ITeye博客
原文地址: http://blog.csdn.net/xuyuefei1988/article/details/19399137. 1、下面网上收罗的资料初学者应该够用了,但对比IBM的Python 代码调试技巧:. IBM:包括 pdb 模块、利用 PyDev 和 Eclipse 集成进行调试、PyCharm 以及 Debug 日志进行调试:.

Python WSGI 初探

- - 坚实的幻想
在构建 Web 应用时,通常会有 Web Server 和 Application Server 两种角色. 其中 Web Server 主要负责接受来自用户的请求,解析 HTTP 协议,并将请求转发给 Application Server,Application Server 主要负责处理用户的请求,并将处理的结果返回给 Web Server,最终 Web Server 将结果返回给用户.

Colinux,不是虚拟机,胜似虚拟机

- wq - C++博客-首页原创精华区
排版走样了,点击下载Doc/pdf版本. 我似乎已经更喜欢使用word写东西,感觉更正式,缺点是发布为web版本时格式乱糟糟的. Colinux,不是虚拟机,胜似虚拟机 一些使用记录 潘孙友 2010.12.25 于遵义. 目录 一、Colinux长什么样. Colinux的格言是:如果Linux 可以运行在任何一种体系架构(i386,PowerPC,...).

Python实现逻辑回归(Logistic Regression in Python)

- - 神刀安全网
Logistic Regression in Python ,作了中文翻译,并相应补充了一些内容. 本文并不研究逻辑回归具体算法实现,而是使用了一些算法库,旨在帮助需要用Python来做逻辑回归的训练和预测的读者快速上手. 逻辑回归是一项可用于预测二分类结果(binary outcome)的统计技术,广泛应用于金融、医学、犯罪学和其他社会科学中.

python 下载文件

- Eric - python相关的python 教程和python 下载你可以在老王python里寻觅
之前给大家分享的python 多线程抓取网页,我觉的大家看了以后,应该会对python 抓取网页有个很好的认识,不过这个只能用python 来抓取到网页的源代码,如果你想用做python 下载文件的话,上面的可能就不适合你了,最近我在用python 做文件下载的时候就遇到这个问题了,不过最终得以解决,为了让大家以后碰过这个问题有更好的解决办法,我把代码发出来:.