您所在的位置: 首页>>读书频道>>设计开发>>其它开发>>

8.4 Python 运行时环境初探

http://book.51cto.com  2008-07-23 16:37  陈儒著  电子工业出版社  我要评论(0)
  • 摘要:《Python源码剖析--深度探索动态语言核心技术》第8章Python 虚拟机框架,从这一章开始,我们将切入Python 字节码虚拟机,深入剖析Python 字节码虚拟机的运行机理,本小节为大家介绍的是Python 运行时环境初探。
  • 标签:Python  源码剖析  动态语言  C程序员

8.4 Python 运行时环境初探

到这里,我们已经看到了Python 虚拟机的整体的执行框架,我们还看到了Python 虚拟机在执行时需要不断使用的执行环境。了解这两点对掌握第二部分的内容已经足够了。但是,虚拟机和执行环境还仅仅是Python 运行机理(或者说运行模型)的一部分,为了对Python 整个的运行机理做一个全面的了解,我们还需要大致了解一下Python 的运行时环境。

前面我们说了,PyFrameObject 对应于可执行文件在执行时的栈帧,但是一个可执行文件要在操作系统中运行,只有栈帧是不够的。之前我们遗漏了两个对于可执行文件运行至关重要的概念:进程和线程。

在本节中,我们首先要对Python 的运行模型(主要是线程模型)进行一个整体概念上的了解,虽然这部分内容我们会留到剖析Python 的多线程实现时再详细考察,但是由于Python 在初始化时会创建一个主线程,所以其运行时环境中存在一个主线程,而且本部分将剖析的Python 的异常机制会利用到Python 内部的线程模型,因此对Python 线程模型有一个整体概念上的了解也是必须的。

以Win32 平台为例,我们知道,对于原生的Win32 可执行文件,无论是由C/C++产生,还是由Delphi 产生,都会在一个进程(Process)中运行。进程并非是与机器指令序列相对应的活动对象,这个与可执行文件中机器指令序列对应的活动对象是由线程(Thread)这个概念来进行抽象的,而进程则是线程的活动环境。

对于通常的单线程可执行文件,在执行时操作系统会创建一个进程,在进程中,又会有一个主线程;而对于多线程的可执行文件,在执行时会操作系统会创建一个进程和多个线程。该多个线程能共享进程地址空间中的全局变量,这就自然而然地引出了线程同步的问题。CPU 对任务的切换实际上是在线程之间切换,在切换任务时,CPU 需要执行线程环境的保存工作,而在切换至新的线程之后,需要恢复该线程的线程环境。

这些关于程序运行的概念同样适用于Python,Python 实现了对多线程的支持,而且Python 中的一个线程就是操作系统上的一个原生线程。这里我们对多线程机制不过多深入,现在只需记住,Python 在执行时,可能会有多个线程存在。

在前面我们看到了虚拟机的大致运行框架,实际上这个虚拟机就是Python 中对CPU的抽象,可以看做是一个软CPU,Python 中的所有线程都使用这个软CPU 来完成计算工作。真实机器上的任务切换机制对应到Python 中,就是使不同的线程轮流使用虚拟机的机制。

CPU 切换任务时需要保存线程运行环境。对于Python 来说,在切换线程之前,同样需要保存关于当前线程的信息。在Python 中,这个关于线程状态信息的抽象是通过PyThreadState 对象来实现的,一个线程将拥有一个PyThreadState 对象。所以从另一种意义来说,这个PyThreadState 对象也可以看成是对线程本身的抽象。但实际上,这两者是有很大区别的,PyThreadState 并非是对线程本身的模拟,因为Python 中的线程仍然使用操作系统的原生线程。PyThreadState 仅仅是对线程状态的抽象,不过在本书的大部分章节中,为了叙述的方便,我们不过分严格地区分线程和线程状态本身,所以在以后我们有时会称PyThreadState 为线程对象,有时会称之为线程状态对象。只有在剖析多线程机制时,我们会严格区分两者。对于下面将提到的PyInterpreterState 对象,也有类似的考量。

刚才提到,在Win32 下,线程是不能独立存活的,它需要存活在进程的环境中,而多个线程可以共享进程的一些资源。在Python 中同样也是如此,考虑一下,如果Python 程序中有两个线程,都会进行同样的一个动作——import sys,那么这个sys module 究竟应该存在几份?是全局共享的还是每个线程都有一个sys module?如果每个线程有自己独立module 集合,那么Python 对内存的消耗就会显得非常惊人。所以在Python 中,这些module都是全局共享的,仿佛这些module 都是进程中的共享资源一样,对于进程这个抽象概念,Python 以PyInterpreterState 对象来实现。

在Win32 下,通常都会有多个进程,而Python 实际上也可以有多个逻辑上的interpreter存在。在通常的情况下,Python 中只有一个interpreter,这个interpreter 中维护了一个或多个PyThreadState 对象,与这些PyThreadState 对象对应的线程轮流使用一个字节码执行引擎。看,是不是与真实机器上的程序执行模型非常相似?

谈到多线程,就不能不谈到线程同步。在Python 中,是通过一个全局解释器锁GIL(Global Interpreter Lock)来实现线程同步的,关于这部分内容,我们留到剖析Python 多线程机制时再详细考察。

好了,现在讨论刚才提到的那两个关键对象:表示进程概念的PyInterpreterObject对象和表示线程概念的PyThreadState 对象。

[pystate.h]
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;

在PyThreadState 对象中,我们看到了熟悉的PyFrameObject(_frame)对象。也就是说,在每个PyThreadState 对象中,会维护一个栈帧的列表,以与PyThreadState 对象的线程中的函数调用机制对应。在Win32 上,情形也是一样的,每个线程都会有一个函数调用堆栈。

关于PyInterpreterState 和PyThreadState 的创建留待以后在合适的地方描述,不过这里可以看看PyThreadState 和PyFrameObject 之间的一些交互和联系。当Python虚拟机开始执行时,会将当前线程状态对象中的frame 设置为当前的执行环境(frame):

 [ceval.c]
PyObject *PyEval_EvalFrameEx
(PyFrameObject *f, int throwflag)
{
……
//通过PyThreadState_GET 获得当前活
动线程对应的线程状态对象
PyThreadState *tstate = PyThreadState_GET();
……
//设置线程状态对象中的frame
tstate->frame = f;
co = f->f_code;
names = co->co_names;
consts = co->co_consts;
……
//虚拟机主循环
for (;;) {
……
opcode = NEXTOP();
oparg = 0;
if (HAS_ARG(opcode))
oparg = NEXTARG();
dispatch_opcode:
//指令分派
switch (opcode) {
……
}
……
}
……
}
而在建立新的PyFrameObject 对象时,则从当前线程的状态对象中取出旧的frame,建立PyFrameObject 链表:
 [frameobject.c]
PyFrameObject *
PyFrame_New(PyThreadState *tstate,
PyCodeObject *code, PyObject *globals,
PyObject *locals)
{
//从PyThreadState 中获得当前线程的当前执行环境
PyFrameObject *back = tstate->frame;
PyFrameObject *f;
……
//创建新的执行环境
f = PyObject_GC_Resize(PyFrameObject, f, extras);
……
//链接当前执行环境
f->f_back = back
f->f_tstate = tstate;
……
return f;
}
现在我们发散思维,想象一下Python 在运行时的某刻,内存中所有参与执行的关键基础对象的布局,如图8-6 所示:
 
(点击查看大图)图8-6 Python 的运行时环境

好了,有了这些基础知识,我们就可以从容面对Python 虚拟机的一切内容了。
【责任编辑:夏书 TEL:(010)68476606】

回书目   上一节   下一节
Python实用开发指南
深入Vista应用程序开发
Ruby on Rails 社区网站开发
ASP.NET从入门到精通
Java完全自学宝典
 
 验证码: (点击刷新验证码)   匿名发表
  • Visual C++ 完全自学宝典

  • 作者:强锋科技,朱洪波
  • Visual C++ 6.0是微软公司为程序人员提供的Visual Studio 6.0工具套件中的重要组成部分。本书由浅入深地介绍使用Visual C++ 6.0..
Copyright©2005-2008 51CTO.COM 版权所有