简介Python虚拟机中的Python运行环境

开发 后端
当进行Python语言启动后,首先会碰到Python运行环境的初始化问题,注意:这里的运行时环境是与之前的运行环境不一样的环境。

其实Python运行环境是一个全局性的概念,而执行环境实际就是一个栈帧,是Code Block对应的概念,两者之间存在着本质上的区别,在以后的运行操作过程中就可以了解到他们呢两者之间的不同。

运行时环境的初始化过程非常地复杂,后面将用单独的一章来剖析,这里假设初始化的动作已经完成,我们已经站在了Python虚拟机的门槛外,只需要轻轻推动一下***张骨牌,整个执行过程就像多米诺骨牌一样,一环扣一环地展开。

这个推动***张骨牌的地方在一个名叫PyEval_EvalFramEx的函数中,这个函数实际上就是Python的虚拟机的具体实现,它是一个非常巨大的函数,因此我们在列出其中的源代码时和以前有些不同。

PyEval_EvalFrameEx首先会初始化一些变量,其中PyFrameObject对象中的PyCodeObject对象包含的重要信息都被照顾到了。当然,另一个重要的动作就是初始化了堆栈的栈顶指针,使其指向f->f_stacktop:

[PyEval_EvalFrameEx in ceval.c]      
 
    co = f->f_code;  
 
    names = co->co_names;  
 
    coconsts = co->co_consts;  
 
    ffastlocals = f->f_localsplus;  
 
    ffreevars = f->f_localsplus + co->co_nlocals;  
 
    first_instr = (unsigned char*)PyString_AS_STRING(co->co_code);  
 
    next_instr = first_instr + f->f_lasti + 1;  
 
    stack_pointer = f->f_stacktop;  
 
    f->f_stacktop = NULL;   /* remains NULL unless yield suspends frame */  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

前面我们说过,在PyCodeObject对象的co_code域中保存着字节码指令和字节码指令的参数,Python虚拟机执行字节码指令序列的过程就是从头到尾遍历整个co_code、依次执行字节码指令的过程。

Python运行环境的虚拟机中,利用3个变量来完成整个遍历过程。co_code实际上是一个PyStringObject对象,而其中的字符数组才是真正有意义的东西。这也就是说,整个字节码指令序列实际上就是一个在C中普普通通的字符数组。因此,遍历过程中所使用的这3个变量都是char*类型的变量:first_instr永远指向字节码指令序列的开始位置;

next_instr永远指向下一条待执行的字节码指令的位置;f_lasti指向上一条已经执行过的字节码指令的位置。展示了这3个变量在遍历中某时刻的情形:

[ceval.c]  
 
/* Interpreter main loop */  
 
PyObject* PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)  
 
{  
 
    ……  
 
    why = WHY_NOT;  
 
    ……  
 
    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:  
 
            ……  
 
        }  
 
}  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.

那么这个一步一步的动作是如何完成的呢,我们来看一看Python运行环境执行字节码指令的整体架构,其实就是一个for循环加上一个巨大的switch/case结构,熟悉Windows SDK编程的朋友可以想象一下Windows下那个巨大的消息循环,就是那样的结构。在对PyCodeObject对象的分析中我们说过,Python的字节码有的是带参数的,有的是没有参数的,而判断是否带参字节码是通过HAS_ARG这个宏实现的。

注意,对不同的字节码指令,由于存在是否需要指令参数的区别,所以next_instr的位移可能是不同的。但是无论如何,next_instr总是指向Python下一条要执行的字节码,这很像x86平台上的那个PC寄存器。

Python在获得了一条字节码指令和其需要的指令参数后,会对字节码指令利用switch进行判断,根据判断的结果选择不同的case语句,每一条字节码指令都会对应一个case语句。在case语句中,就是Python对字节码指令的实现。

在成功执行完一条字节码指令后,Python运行环境的执行流程会跳转到fast_next_opcode处,或者是for循环处,不管如何,Python接下来的动作都是获得下一条字节码指令和指令参数,完成对下一条指令的执行。如此一条一条地遍历co_code中包含的所有字节码指令,最终完成了对Python程序的执行。

【编辑推荐】

  1. 有关Python系统文件进行介绍指导
  2. 如何正确的使用Python函数
  3. 对Python 构建工具进行详细介绍分析
  4. PythonAndroid浅析Python优势所在
  5. 如何使用Python模块解析配置文件?
责任编辑:chenqingxiang 来源: CSDN
相关推荐

2015-03-03 09:39:28

Java运行环境Python解释器

2024-09-30 16:08:43

Python虚拟机栈帧

2010-02-24 10:39:28

Python虚拟机

2010-02-26 15:28:15

Python虚拟机

2022-07-04 12:26:00

云原生开源

2021-07-30 20:25:04

pipxPython编程语言

2013-06-17 10:16:53

虚拟机虚拟化安全

2014-11-27 15:08:05

虚拟化动态迁移

2023-03-06 10:03:05

Java运行机制

2010-03-01 10:52:25

VMware ESX ESX快照

2011-12-12 09:08:48

OpenStack虚拟机监控

2022-05-04 11:07:32

虚拟机Linux

2020-10-21 08:16:37

Java基础入门篇

2009-08-18 10:48:33

2021-07-31 12:58:53

PodmanLinux虚拟机

2012-05-18 10:22:23

2010-06-11 14:50:48

虚拟机安装openSU

2019-07-05 15:14:34

虚拟机WindowsWindows 10

2019-09-20 17:50:36

虚拟机管理器virt-manageLinux

2010-02-24 10:07:28

Python虚拟机
点赞
收藏

51CTO技术栈公众号